local-server/cmd/dkl-local-server/secrets.go

244 lines
4.3 KiB
Go
Raw Normal View History

2018-06-12 10:09:47 +00:00
package main
import (
2018-06-19 06:48:28 +00:00
"crypto/rand"
"encoding/base32"
2018-06-16 11:45:27 +00:00
"encoding/json"
"errors"
2018-08-09 13:07:53 +00:00
"fmt"
2018-06-12 10:09:47 +00:00
"io/ioutil"
2018-08-09 13:07:53 +00:00
"net"
2018-06-12 10:09:47 +00:00
"os"
2018-06-16 11:45:27 +00:00
"path/filepath"
2018-06-12 10:09:47 +00:00
2018-06-16 11:45:27 +00:00
"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/csr"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/initca"
"github.com/cloudflare/cfssl/signer"
"github.com/cloudflare/cfssl/signer/local"
2018-08-09 13:07:53 +00:00
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
2018-06-12 10:09:47 +00:00
)
2018-06-16 11:45:27 +00:00
type SecretData struct {
clusters map[string]*ClusterSecrets
changed bool
config *config.Config
}
type ClusterSecrets struct {
2018-06-19 06:48:28 +00:00
CAs map[string]*CA
Tokens map[string]string
2018-06-16 11:45:27 +00:00
}
type CA struct {
Key []byte
Cert []byte
Signed map[string]*KeyCert
}
type KeyCert struct {
Key []byte
Cert []byte
ReqHash string
2018-06-16 11:45:27 +00:00
}
func loadSecretData(config *config.Config) (*SecretData, error) {
sd := &SecretData{
clusters: make(map[string]*ClusterSecrets),
changed: false,
config: config,
}
ba, err := ioutil.ReadFile(filepath.Join(*dataDir, "secret-data.json"))
if err != nil {
if os.IsNotExist(err) {
sd.changed = true
return sd, nil
}
return nil, err
}
if err := json.Unmarshal(ba, &sd.clusters); err != nil {
return nil, err
}
return sd, nil
}
func (sd *SecretData) Changed() bool {
return sd.changed
}
func (sd *SecretData) Save() error {
ba, err := json.Marshal(sd.clusters)
if err != nil {
return err
}
return ioutil.WriteFile(filepath.Join(*dataDir, "secret-data.json"), ba, 0600)
}
func (sd *SecretData) cluster(name string) (cs *ClusterSecrets) {
cs, ok := sd.clusters[name]
if ok {
return
}
cs = &ClusterSecrets{
2018-06-19 06:48:28 +00:00
CAs: make(map[string]*CA),
Tokens: make(map[string]string),
2018-06-16 11:45:27 +00:00
}
sd.clusters[name] = cs
sd.changed = true
return
}
2018-06-19 06:48:28 +00:00
func (sd *SecretData) Token(cluster, name string) (token string, err error) {
cs := sd.cluster(cluster)
token = cs.Tokens[name]
if token != "" {
return
}
b := make([]byte, 16)
_, err = rand.Read(b)
if err != nil {
return
}
token = base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b)
cs.Tokens[name] = token
sd.changed = true
return
}
2018-06-16 11:45:27 +00:00
func (sd *SecretData) CA(cluster, name string) (ca *CA, err error) {
cs := sd.cluster(cluster)
ca, ok := cs.CAs[name]
if ok {
return
}
req := &csr.CertificateRequest{
CN: "Direktil Local Server",
KeyRequest: &csr.BasicKeyRequest{
A: "ecdsa",
S: 521, // 256, 384, 521
},
Names: []csr.Name{
{
C: "NC",
O: "novit.nc",
},
},
}
cert, _, key, err := initca.New(req)
if err != nil {
return
}
ca = &CA{
Key: key,
Cert: cert,
Signed: make(map[string]*KeyCert),
}
cs.CAs[name] = ca
sd.changed = true
return
}
func (sd *SecretData) KeyCert(cluster, caName, name, profile, label string, req *csr.CertificateRequest) (kc *KeyCert, err error) {
2018-08-09 13:07:53 +00:00
for idx, host := range req.Hosts {
if ip := net.ParseIP(host); ip != nil {
// valid IP (v4 or v6)
continue
}
if host == "*" {
continue
}
if errs := validation.IsDNS1123Subdomain(host); len(errs) == 0 {
continue
}
if errs := validation.IsWildcardDNS1123Subdomain(host); len(errs) == 0 {
continue
}
path := field.NewPath(cluster, name, "hosts").Index(idx)
return nil, fmt.Errorf("%v: %q is not an IP or FQDN", path, host)
}
2018-06-16 11:45:27 +00:00
if req.CA != nil {
err = errors.New("no CA section allowed here")
return
}
ca, err := sd.CA(cluster, caName)
if err != nil {
return
}
rh := hash(req)
2018-06-16 11:45:27 +00:00
kc, ok := ca.Signed[name]
if ok && rh == kc.ReqHash {
2018-06-16 11:45:27 +00:00
return
}
sgr, err := ca.Signer(sd.config.Signing)
if err != nil {
return
}
generator := &csr.Generator{Validator: func(_ *csr.CertificateRequest) error { return nil }}
csr, key, err := generator.ProcessRequest(req)
if err != nil {
return
}
signReq := signer.SignRequest{
Request: string(csr),
Profile: profile,
Label: label,
}
cert, err := sgr.Sign(signReq)
if err != nil {
return
}
kc = &KeyCert{
Key: key,
Cert: cert,
ReqHash: rh,
2018-06-16 11:45:27 +00:00
}
ca.Signed[name] = kc
sd.changed = true
return
}
func (ca *CA) Signer(policy *config.Signing) (result *local.Signer, err error) {
caCert, err := helpers.ParseCertificatePEM(ca.Cert)
if err != nil {
return
}
caKey, err := helpers.ParsePrivateKeyPEM(ca.Key)
if err != nil {
return
}
return local.NewSigner(caKey, caCert, signer.DefaultSigAlgo(caKey), policy)
}