certificate overhaul
This commit is contained in:
@ -2,18 +2,19 @@ package certificate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func CreateCA() ([]byte, []byte, []byte, error) {
|
||||
func CreateCA(privateKey string) (string, error) {
|
||||
serial, _ := strconv.ParseInt(time.Now().Format("20060102150405"), 10, 64)
|
||||
ca := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(serial),
|
||||
@ -37,14 +38,18 @@ func CreateCA() ([]byte, []byte, []byte, error) {
|
||||
SignatureAlgorithm: x509.SHA384WithRSA,
|
||||
}
|
||||
|
||||
keyPair, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
pemKey, _ := pem.Decode([]byte(privateKey))
|
||||
if pemKey == nil || pemKey.Type != "RSA PRIVATE KEY" {
|
||||
return "", fmt.Errorf("failed to decode PEM block containing private key")
|
||||
}
|
||||
keyPair, err := x509.ParsePKCS1PrivateKey(pemKey.Bytes)
|
||||
if err != nil {
|
||||
return []byte(""), []byte(""), []byte(""), err
|
||||
return "", err
|
||||
}
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &keyPair.PublicKey, keyPair)
|
||||
if err != nil {
|
||||
return []byte(""), []byte(""), []byte(""), err
|
||||
return "", err
|
||||
}
|
||||
|
||||
c := new(bytes.Buffer)
|
||||
@ -53,17 +58,6 @@ func CreateCA() ([]byte, []byte, []byte, error) {
|
||||
Bytes: certBytes,
|
||||
})
|
||||
|
||||
k := new(bytes.Buffer)
|
||||
pem.Encode(k, &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(keyPair),
|
||||
})
|
||||
|
||||
p := new(bytes.Buffer)
|
||||
pem.Encode(p, &pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: x509.MarshalPKCS1PublicKey(&keyPair.PublicKey),
|
||||
})
|
||||
|
||||
return c.Bytes(), k.Bytes(), p.Bytes(), nil
|
||||
log.Printf("[DEBUG] Generated Certificate Authority Certificate:\n%s", c)
|
||||
return c.String(), nil
|
||||
}
|
||||
|
@ -1,72 +0,0 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func CreateCert() ([]byte, []byte, []byte, error) {
|
||||
serial, _ := strconv.ParseInt(time.Now().Format("20060102150405"), 10, 64)
|
||||
ca := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(serial + 1),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Kubernetes Mutating Webserver"},
|
||||
Country: []string{"K8S"},
|
||||
Province: []string{"Cluster Service"},
|
||||
Locality: []string{"Cluster Local"},
|
||||
//StreetAddress: []string{""},
|
||||
//PostalCode: []string{""},
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(1, 6, 0),
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||
x509.ExtKeyUsageClientAuth,
|
||||
x509.ExtKeyUsageServerAuth,
|
||||
},
|
||||
DNSNames: []string{
|
||||
"svc.cluster.local",
|
||||
"*.svc.cluster.local",
|
||||
},
|
||||
SubjectKeyId: []byte{1, 2, 3, 4, 6},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||
SignatureAlgorithm: x509.SHA384WithRSA,
|
||||
}
|
||||
|
||||
keyPair, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return []byte(""), []byte(""), []byte(""), err
|
||||
}
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &keyPair.PublicKey, keyPair)
|
||||
if err != nil {
|
||||
return []byte(""), []byte(""), []byte(""), err
|
||||
}
|
||||
|
||||
c := new(bytes.Buffer)
|
||||
pem.Encode(c, &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: certBytes,
|
||||
})
|
||||
|
||||
k := new(bytes.Buffer)
|
||||
pem.Encode(k, &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(keyPair),
|
||||
})
|
||||
|
||||
p := new(bytes.Buffer)
|
||||
pem.Encode(p, &pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: x509.MarshalPKCS1PublicKey(&keyPair.PublicKey),
|
||||
})
|
||||
|
||||
return c.Bytes(), k.Bytes(), p.Bytes(), nil
|
||||
}
|
53
internal/certificate/create-csr.go
Normal file
53
internal/certificate/create-csr.go
Normal file
@ -0,0 +1,53 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
)
|
||||
|
||||
func CreateCSR(privateKey string) (string, error) {
|
||||
csr := x509.CertificateRequest{
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Kubernetes Mutating Webserver"},
|
||||
Country: []string{"K8S"},
|
||||
Province: []string{"Cluster Service"},
|
||||
Locality: []string{"Cluster Local"},
|
||||
//StreetAddress: []string{""},
|
||||
//PostalCode: []string{""},
|
||||
},
|
||||
DNSNames: []string{
|
||||
"svc.cluster.local",
|
||||
"*.svc.cluster.local",
|
||||
},
|
||||
SignatureAlgorithm: x509.SHA384WithRSA,
|
||||
}
|
||||
|
||||
pemKey, _ := pem.Decode([]byte(privateKey))
|
||||
if pemKey == nil || pemKey.Type != "RSA PRIVATE KEY" {
|
||||
return "", fmt.Errorf("failed to decode PEM block containing private key")
|
||||
}
|
||||
keyPair, err := x509.ParsePKCS1PrivateKey(pemKey.Bytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
csrData, err := x509.CreateCertificateRequest(rand.Reader, &csr, keyPair)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
c := new(bytes.Buffer)
|
||||
pem.Encode(c, &pem.Block{
|
||||
Type: "CERTIFICATE REQUEST",
|
||||
Bytes: csrData,
|
||||
})
|
||||
|
||||
log.Printf("[TRACE] Generated Host CSR:\n%s", c.String())
|
||||
return c.String(), nil
|
||||
}
|
15
internal/certificate/create-key.go
Normal file
15
internal/certificate/create-key.go
Normal file
@ -0,0 +1,15 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
)
|
||||
|
||||
func CreateRSAKeyPair(bytes int) (*rsa.PrivateKey, error) {
|
||||
keyPair, err := rsa.GenerateKey(rand.Reader, bytes)
|
||||
if err != nil {
|
||||
return &rsa.PrivateKey{}, err
|
||||
}
|
||||
|
||||
return keyPair, nil
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
)
|
||||
|
||||
func CreateServerCert() tls.Certificate {
|
||||
caCertPem, caPrivKeyPem, _, _ := CreateCA()
|
||||
certCertPem, certPrivKeyPem, certPublicKeyPem, _ := CreateCert()
|
||||
|
||||
caCertBlob, _ := pem.Decode(caCertPem)
|
||||
caCert, _ := x509.ParseCertificate(caCertBlob.Bytes)
|
||||
caPrivKeyBlob, _ := pem.Decode(caPrivKeyPem)
|
||||
caPrivKey, _ := x509.ParsePKCS1PrivateKey(caPrivKeyBlob.Bytes)
|
||||
certCertBlob, _ := pem.Decode(certCertPem)
|
||||
certCert, _ := x509.ParseCertificate(certCertBlob.Bytes)
|
||||
certPublicKeyBlob, _ := pem.Decode(certPublicKeyPem)
|
||||
certPublicKey, _ := x509.ParsePKCS1PublicKey(certPublicKeyBlob.Bytes)
|
||||
|
||||
signedCert, err := x509.CreateCertificate(rand.Reader, certCert, caCert, certPublicKey, caPrivKey)
|
||||
if err != nil {
|
||||
log.Fatalf("[FATAL] CreateCertificate: %v", err)
|
||||
}
|
||||
|
||||
serverCertPem := new(bytes.Buffer)
|
||||
pem.Encode(serverCertPem, &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: signedCert,
|
||||
})
|
||||
|
||||
serverCert, err := tls.X509KeyPair(append(serverCertPem.Bytes(), caCertPem...), certPrivKeyPem)
|
||||
if err != nil {
|
||||
log.Fatalf("[FATAL] x509KeyPair: %v", err)
|
||||
}
|
||||
|
||||
return serverCert
|
||||
}
|
74
internal/certificate/sign-cert.go
Normal file
74
internal/certificate/sign-cert.go
Normal file
@ -0,0 +1,74 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func SignCert(caCertPem, caPrivKeyPem, csrPem string) (string, error) {
|
||||
caCertData, _ := pem.Decode([]byte(caCertPem))
|
||||
caCert, err := x509.ParseCertificate(caCertData.Bytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parse cert %v", err)
|
||||
}
|
||||
|
||||
pemKey, _ := pem.Decode([]byte(caPrivKeyPem))
|
||||
if pemKey == nil || pemKey.Type != "RSA PRIVATE KEY" {
|
||||
return "", fmt.Errorf("failed to decode PEM block containing private key")
|
||||
}
|
||||
keyPair, err := x509.ParsePKCS1PrivateKey(pemKey.Bytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("private key %v", err)
|
||||
}
|
||||
|
||||
csrData, _ := pem.Decode([]byte(csrPem))
|
||||
csr, err := x509.ParseCertificateRequest(csrData.Bytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parse csr %v", err)
|
||||
}
|
||||
|
||||
serial, _ := strconv.ParseInt(time.Now().Format("20060102150405"), 10, 64)
|
||||
certTemplate := x509.Certificate{
|
||||
SerialNumber: big.NewInt(serial + 1),
|
||||
Issuer: caCert.Issuer,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Kubernetes Mutating Webserver"},
|
||||
Country: csr.Subject.Country,
|
||||
Province: []string{"Cluster Service"},
|
||||
Locality: []string{"Cluster Local"},
|
||||
StreetAddress: csr.Subject.StreetAddress,
|
||||
PostalCode: csr.Subject.PostalCode,
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(1, 6, 0),
|
||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||
x509.ExtKeyUsageClientAuth,
|
||||
x509.ExtKeyUsageServerAuth,
|
||||
},
|
||||
DNSNames: csr.DNSNames,
|
||||
Signature: csr.Signature,
|
||||
SignatureAlgorithm: csr.SignatureAlgorithm,
|
||||
PublicKey: csr.PublicKey,
|
||||
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
|
||||
}
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, &certTemplate, caCert, csr.PublicKey, keyPair)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("sign %v", err)
|
||||
}
|
||||
c := new(bytes.Buffer)
|
||||
pem.Encode(c, &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: certBytes,
|
||||
})
|
||||
return c.String(), nil
|
||||
}
|
@ -35,9 +35,15 @@ type Config struct {
|
||||
|
||||
// mutation configuration
|
||||
AllowAdminNoMutate bool `env:"allow_admin_nomutate" default:"false"`
|
||||
AllowAdminNoMutateToggle string `env:"allow_admin_nomutate_toggle" default:"2d77b689-dc14-40a5-8971-34c62999335c"`
|
||||
AllowAdminNoMutateToggle string `env:"allow_admin_nomutate_toggle" default:"7b068a99-c02b-410a-bd59-3514bac85e7a"`
|
||||
DockerhubRegistry string `env:"dockerhub_registry" default:"registry.hub.docker.com"`
|
||||
MutateIgnoredImages []string `ignored:"true"`
|
||||
|
||||
// certificate configuration
|
||||
CACert string `env:"ca_cert"`
|
||||
CAPrivateKey string `env:"ca_private_key"`
|
||||
CertCert string `env:"cert_cert"`
|
||||
CertPrivateKey string `env:"cert_private_key"`
|
||||
}
|
||||
|
||||
// DefaultConfig initializes the config variable for use with a prepared set of defaults.
|
||||
|
@ -9,10 +9,18 @@ import (
|
||||
)
|
||||
|
||||
type configFileStruct struct {
|
||||
AllowAdminNoMutate bool `yaml:"allow-admin-nomutate"`
|
||||
AllowAdminNoMutateToggle string `yaml:"allow-admin-nomutate-toggle"`
|
||||
DockerhubRegistry string `yaml:"dockerhub-registry"`
|
||||
MutateIgnoredImages []string `yaml:"mutate-ignored-images"`
|
||||
AllowAdminNoMutate bool `yaml:"allow-admin-nomutate"`
|
||||
AllowAdminNoMutateToggle string `yaml:"allow-admin-nomutate-toggle"`
|
||||
DockerhubRegistry string `yaml:"dockerhub-registry"`
|
||||
MutateIgnoredImages []string `yaml:"mutate-ignored-images"`
|
||||
CertificateAuthority CertStruct `yaml:"certificate-authority"`
|
||||
Certificate CertStruct `yaml:"certificate"`
|
||||
}
|
||||
|
||||
type CertStruct struct {
|
||||
Certificate string `yaml:"certificate"`
|
||||
PrivateKey string `yaml:"private-key"`
|
||||
PublicKey string `yaml:"public-key"`
|
||||
}
|
||||
|
||||
func getConfigFileData(fileLocation string) (configFileStruct, error) {
|
||||
|
@ -1,8 +1,13 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"mutating-webhook/internal/certificate"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
@ -70,25 +75,108 @@ func Init() Config {
|
||||
}
|
||||
time.Now().Format(cfg.TimeFormat)
|
||||
|
||||
// print running config
|
||||
printRunningConfig(&cfg, cfgInfo)
|
||||
|
||||
// read config file
|
||||
configFileData, err := getConfigFileData(cfg.ConfigFile)
|
||||
if err != nil {
|
||||
log.Fatalf("[FATAL] Unable to read configuration file")
|
||||
}
|
||||
if cfg.AllowAdminNoMutate == false {
|
||||
cfg.AllowAdminNoMutate = configFileData.AllowAdminNoMutate
|
||||
updateValues(&cfg, configFileData)
|
||||
|
||||
// Generate certificates if needed
|
||||
if err := certificateInit(&cfg); err != nil {
|
||||
log.Fatalf("[FATAL] Unable to initialize certificate data: %v", err)
|
||||
}
|
||||
if cfg.AllowAdminNoMutateToggle == "2d77b689-dc14-40a5-8971-34c62999335c" {
|
||||
cfg.AllowAdminNoMutateToggle = configFileData.AllowAdminNoMutateToggle
|
||||
}
|
||||
if cfg.DockerhubRegistry == "registry.hub.docker.com" {
|
||||
cfg.DockerhubRegistry = configFileData.DockerhubRegistry
|
||||
}
|
||||
cfg.MutateIgnoredImages = configFileData.MutateIgnoredImages
|
||||
|
||||
// print running config
|
||||
printRunningConfig(&cfg, cfgInfo)
|
||||
|
||||
log.Println("[INFO] initialization sequence complete")
|
||||
return cfg
|
||||
}
|
||||
|
||||
func updateValues(cfg *Config, configFileData configFileStruct) {
|
||||
if cfg.AllowAdminNoMutate == false && configFileData.AllowAdminNoMutate != false {
|
||||
cfg.AllowAdminNoMutate = configFileData.AllowAdminNoMutate
|
||||
}
|
||||
if cfg.AllowAdminNoMutateToggle == "7b068a99-c02b-410a-bd59-3514bac85e7a" && configFileData.AllowAdminNoMutateToggle != "2d77b689-dc14-40a5-8971-34c62999335c" {
|
||||
cfg.AllowAdminNoMutateToggle = configFileData.AllowAdminNoMutateToggle
|
||||
}
|
||||
if cfg.DockerhubRegistry == "registry.hub.docker.com" && configFileData.DockerhubRegistry != "registry.hub.docker.com" {
|
||||
cfg.DockerhubRegistry = configFileData.DockerhubRegistry
|
||||
}
|
||||
if len(configFileData.MutateIgnoredImages) != 0 {
|
||||
cfg.MutateIgnoredImages = configFileData.MutateIgnoredImages
|
||||
}
|
||||
if len(configFileData.CertificateAuthority.Certificate) != 0 {
|
||||
cfg.CACert = configFileData.CertificateAuthority.Certificate
|
||||
}
|
||||
if len(configFileData.CertificateAuthority.PrivateKey) != 0 {
|
||||
cfg.CAPrivateKey = configFileData.CertificateAuthority.PrivateKey
|
||||
}
|
||||
if len(configFileData.Certificate.Certificate) != 0 {
|
||||
cfg.CertCert = configFileData.Certificate.Certificate
|
||||
}
|
||||
if len(configFileData.Certificate.PrivateKey) != 0 {
|
||||
cfg.CertPrivateKey = configFileData.Certificate.PrivateKey
|
||||
}
|
||||
}
|
||||
|
||||
func certificateInit(cfg *Config) error {
|
||||
// certificate authority private key does not exist, generate key pair
|
||||
if len(cfg.CAPrivateKey) == 0 {
|
||||
log.Printf("[TRACE] No certificate authority private key detected")
|
||||
keyPair, err := certificate.CreateRSAKeyPair(4096)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Create RSA Key (%v)", err)
|
||||
}
|
||||
// pem encode private key
|
||||
k := new(bytes.Buffer)
|
||||
pem.Encode(k, &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(keyPair),
|
||||
})
|
||||
cfg.CAPrivateKey = k.String()
|
||||
}
|
||||
|
||||
// certificate authority certificate is missing, create it
|
||||
if len(cfg.CACert) == 0 {
|
||||
log.Printf("[TRACE] No certificate authority certificate detected")
|
||||
caCert, err := certificate.CreateCA(cfg.CAPrivateKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Create CA (%v)", err)
|
||||
}
|
||||
cfg.CACert = caCert
|
||||
}
|
||||
|
||||
// certificate private key does not exist, generate key pair
|
||||
if len(cfg.CertPrivateKey) == 0 {
|
||||
log.Printf("[TRACE] No server private key detected")
|
||||
keyPair, err := certificate.CreateRSAKeyPair(4096)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Create RSA Key (%v)", err)
|
||||
}
|
||||
// pem encode private key
|
||||
k := new(bytes.Buffer)
|
||||
pem.Encode(k, &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(keyPair),
|
||||
})
|
||||
cfg.CertPrivateKey = k.String()
|
||||
}
|
||||
|
||||
// certificate certificate is missing, create it
|
||||
if len(cfg.CertCert) == 0 {
|
||||
log.Printf("[TRACE] No server certificate detected")
|
||||
csr, err := certificate.CreateCSR(cfg.CertPrivateKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Create CSR (%v)", err)
|
||||
}
|
||||
cert, err := certificate.SignCert(cfg.CACert, cfg.CAPrivateKey, csr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Sign Cert (%v)", err)
|
||||
}
|
||||
cfg.CertCert = cert
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user