You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
boundary/internal/servers/worker/cluster_tls.go

147 lines
3.6 KiB

package worker
import (
"context"
"crypto/ed25519"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"math/big"
mathrand "math/rand"
"time"
"github.com/hashicorp/vault/sdk/helper/base62"
"github.com/hashicorp/watchtower/internal/cmd/base"
"google.golang.org/protobuf/proto"
)
func (c Worker) workerAuthTLSConfig() (*tls.Config, error) {
info := new(base.WorkerAuthCertInfo)
_, caKey, err := ed25519.GenerateKey(c.conf.SecureRandomReader)
if err != nil {
return nil, err
}
caHost, err := base62.Random(20)
if err != nil {
return nil, err
}
caCertTemplate := &x509.Certificate{
Subject: pkix.Name{
CommonName: caHost,
},
DNSNames: []string{caHost},
KeyUsage: x509.KeyUsage(x509.KeyUsageCertSign | x509.KeyUsageCRLSign),
SerialNumber: big.NewInt(mathrand.Int63()),
NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: time.Now().Add(3 * time.Minute),
BasicConstraintsValid: true,
IsCA: true,
}
caBytes, err := x509.CreateCertificate(c.conf.SecureRandomReader, caCertTemplate, caCertTemplate, caKey.Public(), caKey)
if err != nil {
return nil, err
}
caCertPEMBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
}
info.CACertPEM = pem.EncodeToMemory(caCertPEMBlock)
caCert, err := x509.ParseCertificate(caBytes)
if err != nil {
return nil, err
}
//
// Certs generation
//
_, key, err := ed25519.GenerateKey(c.conf.SecureRandomReader)
if err != nil {
return nil, err
}
host, err := base62.Random(20)
if err != nil {
return nil, err
}
certTemplate := &x509.Certificate{
Subject: pkix.Name{
CommonName: host,
},
DNSNames: []string{host},
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
x509.ExtKeyUsageClientAuth,
},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement,
SerialNumber: big.NewInt(mathrand.Int63()),
NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: time.Now().Add(2 * time.Minute),
}
certBytes, err := x509.CreateCertificate(c.conf.SecureRandomReader, certTemplate, caCert, key.Public(), caKey)
if err != nil {
return nil, err
}
certPEMBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
info.CertPEM = pem.EncodeToMemory(certPEMBlock)
marshaledKey, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return nil, err
}
keyPEMBlock := &pem.Block{
Type: "PRIVATE KEY",
Bytes: marshaledKey,
}
info.KeyPEM = pem.EncodeToMemory(keyPEMBlock)
// Marshal and encrypt
marshaledInfo, err := json.Marshal(info)
if err != nil {
return nil, err
}
encInfo, err := c.conf.WorkerAuthKMS.Encrypt(context.Background(), marshaledInfo, nil)
if err != nil {
return nil, err
}
marshaledEncInfo, err := proto.Marshal(encInfo)
if err != nil {
return nil, err
}
b64alpn := base64.RawStdEncoding.EncodeToString(marshaledEncInfo)
var nextProtos []string
var count int
for i := 0; i < len(b64alpn); i += 230 {
end := i + 230
if end > len(b64alpn) {
end = len(b64alpn)
}
nextProtos = append(nextProtos, fmt.Sprintf("v1workerauth-%02d-%s", count, b64alpn[i:end]))
count++
}
// Build local tls config
rootCAs := x509.NewCertPool()
rootCAs.AddCert(caCert)
tlsCert, err := tls.X509KeyPair(info.CertPEM, info.KeyPEM)
if err != nil {
return nil, err
}
tlsConfig := &tls.Config{
ServerName: host,
Certificates: []tls.Certificate{tlsCert},
RootCAs: rootCAs,
NextProtos: nextProtos,
MinVersion: tls.VersionTLS13,
}
tlsConfig.BuildNameToCertificate()
return tlsConfig, nil
}