diff --git a/main.go b/main.go index 618235b..c0065bb 100644 --- a/main.go +++ b/main.go @@ -29,7 +29,6 @@ func main() { log.Fatal("no listen address given") } - secrets = &SecretsFile{filepath.Join(*dataDir, "secret.yaml")} casStore = cas.NewDir(filepath.Join(*dataDir, "cache")) go casCleaner() diff --git a/render-context.go b/render-context.go index bb18b69..c96ce35 100644 --- a/render-context.go +++ b/render-context.go @@ -10,8 +10,10 @@ import ( "log" "path/filepath" + cfsslconfig "github.com/cloudflare/cfssl/config" "github.com/cloudflare/cfssl/csr" yaml "gopkg.in/yaml.v2" + "novit.nc/direktil/pkg/clustersconfig" "novit.nc/direktil/pkg/config" ) @@ -63,7 +65,7 @@ func (ctx *renderContext) Config() (ba []byte, cfg *config.Config, err error) { ctxMap := ctx.asMap() - sslCfg, err := sslConfig(ctx.clusterConfig) + sslCfg, err := cfsslconfig.LoadConfig([]byte(ctx.clusterConfig.SSLConfig)) if err != nil { return } @@ -188,21 +190,6 @@ func (ctx *renderContext) Config() (ba []byte, cfg *config.Config, err error) { return } - // bind secrets in config - for idx, file := range cfg.Files { - if file.Secret == "" { - continue - } - - v, err2 := getSecret(file.Secret, ctx) - if err2 != nil { - err = err2 - return - } - - cfg.Files[idx].Content = v - } - return } diff --git a/secrets.go b/secrets.go index 1239f07..723842d 100644 --- a/secrets.go +++ b/secrets.go @@ -3,12 +3,9 @@ package main import ( "encoding/json" "errors" - "fmt" "io/ioutil" - "log" "os" "path/filepath" - "strings" "github.com/cloudflare/cfssl/config" "github.com/cloudflare/cfssl/csr" @@ -16,11 +13,6 @@ import ( "github.com/cloudflare/cfssl/initca" "github.com/cloudflare/cfssl/signer" "github.com/cloudflare/cfssl/signer/local" - yaml "gopkg.in/yaml.v2" -) - -var ( - secrets SecretBackend ) type SecretData struct { @@ -196,126 +188,3 @@ func (ca *CA) Signer(policy *config.Signing) (result *local.Signer, err error) { return local.NewSigner(caKey, caCert, signer.DefaultSigAlgo(caKey), policy) } - -type SecretBackend interface { - Get(ref string) (string, error) - Set(ref, value string) error -} - -type SecretsFile struct { - Path string -} - -func (sf *SecretsFile) readData() (map[string]string, error) { - ba, err := ioutil.ReadFile(sf.Path) - if err != nil { - return nil, err - } - - data := map[string]string{} - yaml.Unmarshal(ba, &data) - - return data, nil -} - -func (sf *SecretsFile) Get(ref string) (string, error) { - data, err := sf.readData() - - if os.IsNotExist(err) { - return "", nil - - } else if err != nil { - log.Printf("secret file: failed to read: %v", err) - return "", err - } - - return data[ref], nil -} - -func (sf *SecretsFile) Set(ref, value string) (err error) { - data, err := sf.readData() - - if os.IsNotExist(err) { - data = map[string]string{} - - } else if err != nil { - log.Printf("secret file: failed to read: %v", err) - return - } - - data[ref] = value - - ba, err := yaml.Marshal(data) - if err != nil { - log.Printf("secret file: failed to encode: %v", err) - return - } - - os.Rename(sf.Path, sf.Path+".old") - - err = ioutil.WriteFile(sf.Path, ba, 0600) - if err != nil { - log.Printf("secret file: failed to write: %v", err) - return - } - - return -} - -func getSecret(ref string, ctx *renderContext) (string, error) { - fullRef := fmt.Sprintf("%s/%s", ctx.Cluster.Name, ref) - - v, err := secrets.Get(fullRef) - - if err != nil { - return "", err - } - - if v != "" { - return v, nil - } - - // no value, generate - split := strings.SplitN(ref, ":", 2) - kind, path := split[0], split[1] - - switch kind { - case "tls-key": - _, ba := PrivateKeyPEM() - v = string(ba) - - case "tls-self-signed-cert": - caKey, err := loadPrivateKey(path, ctx) - if err != nil { - return "", err - } - - ba := SelfSignedCertificatePEM(5, caKey) - v = string(ba) - - case "tls-host-cert": - hostKey, err := loadPrivateKey(path, ctx) - if err != nil { - return "", err - } - - ba, err := HostCertificatePEM(3, hostKey, ctx) - if err != nil { - return "", err - } - v = string(ba) - - default: - return "", fmt.Errorf("unknown secret kind: %q", kind) - } - - if v == "" { - panic("value not generated?!") - } - - if err := secrets.Set(fullRef, v); err != nil { - return "", err - } - - return v, nil -} diff --git a/ssl.go b/ssl.go deleted file mode 100644 index 0f702b4..0000000 --- a/ssl.go +++ /dev/null @@ -1,195 +0,0 @@ -package main - -import ( - "bytes" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "fmt" - "log" - "math/big" - "net" - "time" - - "github.com/cloudflare/cfssl/config" - - "novit.nc/direktil/pkg/clustersconfig" -) - -const ( - // From Kubernetes: - // ECPrivateKeyBlockType is a possible value for pem.Block.Type. - ECPrivateKeyBlockType = "EC PRIVATE KEY" -) - -func sslConfig(cfg *clustersconfig.Config) (*config.Config, error) { - return config.LoadConfig([]byte(cfg.SSLConfig)) -} - -func PrivateKeyPEM() (*ecdsa.PrivateKey, []byte) { - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - log.Fatal("Failed to generate the key: ", err) - } - - b, err := x509.MarshalECPrivateKey(key) - if err != nil { - log.Fatal("Unable to mashal EC key: ", err) - } - - buf := &bytes.Buffer{} - - if err := pem.Encode(buf, &pem.Block{ - Type: ECPrivateKeyBlockType, - Bytes: b, - }); err != nil { - log.Fatal("Failed to write encode key: ", err) - } - - return key, buf.Bytes() -} - -func SelfSignedCertificatePEM(ttlYears int, key *ecdsa.PrivateKey) []byte { - notBefore := time.Now() - notAfter := notBefore.AddDate(ttlYears, 0, 0).Truncate(24 * time.Hour) - - serialNumber, err := rand.Int(rand.Reader, big.NewInt(0xffffffff)) - if err != nil { - log.Fatal("Failed to generate serial number: ", err) - } - - certTemplate := &x509.Certificate{ - SerialNumber: serialNumber, - NotBefore: notBefore, - NotAfter: notAfter, - IsCA: true, - Subject: pkix.Name{}, - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{}, - BasicConstraintsValid: true, - } - parentTemplate := certTemplate // self-signed - publicKey := key.Public() - - derBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, parentTemplate, publicKey, key) - if err != nil { - log.Fatal("Failed to generate certificate: ", err) - } - - buf := &bytes.Buffer{} - - if err := pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { - log.Fatal("Failed to write certificate: ", err) - } - - return buf.Bytes() -} - -func HostCertificatePEM(ttlYears int, key *ecdsa.PrivateKey, ctx *renderContext) ([]byte, error) { - caKey, err := loadPrivateKey("ca", ctx) - if err != nil { - return nil, err - } - caCrt, err := loadCertificate("ca", ctx) - if err != nil { - return nil, err - } - - notBefore := time.Now() - notAfter := notBefore.AddDate(ttlYears, 0, 0).Truncate(24 * time.Hour) - - serialNumber, err := rand.Int(rand.Reader, big.NewInt(0xffffffff)) - if err != nil { - log.Fatal("Failed to generate serial number: ", err) - } - - dnsNames := []string{ctx.Host.Name} - ips := []net.IP{net.ParseIP(ctx.Host.IP)} - - if ctx.Group.Master { - dnsNames = append(dnsNames, - "kubernetes", - "kubernetes.kube-system", - "kubernetes.kube-system.svc."+ctx.Cluster.Domain, - ) - ips = append(ips, ctx.Cluster.KubernetesSvcIP()) - } - - certTemplate := &x509.Certificate{ - SerialNumber: serialNumber, - NotBefore: notBefore, - NotAfter: notAfter, - IsCA: false, - Subject: pkix.Name{ - CommonName: ctx.Host.Name, - }, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - DNSNames: dnsNames, - IPAddresses: ips, - } - parentTemplate := caCrt - publicKey := key.Public() - - derBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, parentTemplate, publicKey, caKey) - if err != nil { - log.Fatal("Failed to generate certificate: ", err) - } - - f := &bytes.Buffer{} - if err := pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { - log.Fatal("Failed to write certificate: ", err) - } - - return f.Bytes(), nil -} - -func loadPrivateKey(path string, ctx *renderContext) (*ecdsa.PrivateKey, error) { - keyS, err := getSecret("tls-key:"+path, ctx) - if err != nil { - return nil, err - } - - keyBytes := []byte(keyS) - if len(keyBytes) == 0 { - return nil, fmt.Errorf("%s is empty", path) - } - - p, _ := pem.Decode(keyBytes) - if p.Type != ECPrivateKeyBlockType { - return nil, fmt.Errorf("wrong type in %s: %s", path, p.Type) - } - - key, err := x509.ParseECPrivateKey(p.Bytes) - if err != nil { - return nil, fmt.Errorf("unable to parse key in %s: %v", path, err) - } - return key, nil -} - -func loadCertificate(path string, ctx *renderContext) (*x509.Certificate, error) { - crtS, err := getSecret("tls-self-signed-cert:"+path, ctx) - if err != nil { - return nil, err - } - - crtBytes := []byte(crtS) - if len(crtBytes) == 0 { - return nil, fmt.Errorf("%s is empty", path) - } - - p, _ := pem.Decode(crtBytes) - if p.Type != "CERTIFICATE" { - return nil, fmt.Errorf("wrong type in %s: %s", path, p.Type) - } - - crt, err := x509.ParseCertificate(p.Bytes) - if err != nil { - return nil, fmt.Errorf("unable to parse certificate in %s: %v", path, err) - } - - return crt, nil -}