Work in progress implementation of PKI with helm support

This commit is contained in:
Mariano Cano 2021-08-04 20:15:26 -07:00
parent 798b90c359
commit 50f7a0d0c0
2 changed files with 369 additions and 138 deletions

150
pki/helm.go Normal file
View file

@ -0,0 +1,150 @@
package pki
import (
"io"
"text/template"
"github.com/Masterminds/sprig/v3"
"github.com/pkg/errors"
authconfig "github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/provisioner"
"go.step.sm/linkedca"
)
type helmVariables struct {
linkedca.Configuration
Defaults linkedca.Defaults
Password string
SSH struct {
Enabled bool
}
TLS authconfig.TLSOptions
Provisioners []provisioner.Interface
}
func (p *PKI) WriteHelmTemplate(w io.Writer) error {
tmpl, err := template.New("helm").Funcs(sprig.TxtFuncMap()).Parse(helmTemplate)
if err != nil {
return errors.Wrap(err, "error writing helm template")
}
// Delete ssh section if it is not enabled
if !p.options.enableSSH {
p.Ssh = nil
}
if err := tmpl.Execute(w, helmVariables{
Configuration: p.Configuration,
Defaults: p.Defaults,
Password: "asdf",
TLS: authconfig.DefaultTLSOptions,
Provisioners: []provisioner.Interface{
&provisioner.JWK{
Name: p.options.provisioner,
Type: "JWK",
Key: p.ottPublicKey,
EncryptedKey: "",
},
},
}); err != nil {
return errors.Wrap(err, "error executing helm template")
}
return nil
}
const helmTemplate = `# Helm template
inject:
enabled: true
# Config contains the configuration files ca.json and defaults.json
config:
files:
ca.json:
root: {{ first .Root }}
federateRoots: []
crt: {{ .Intermediate }}
key: {{ .IntermediateKey }}
{{- if .SSH.Enabled }}
ssh:
hostKey: {{ .Ssh.HostKey }}
userKey: {{ .Ssh.UserKey }}
{{- end }}
address: {{ .Address }}
dnsNames:
{{- range .DnsNames }}
- {{ . }}
{{- end }}
logger:
format: json
db:
type: badger
dataSource: /home/step/db
authority:
provisioners:
{{- range .Provisioners }}
- {{ . | toJson }}
{{- end }}
tls:
cipherSuites:
{{- range .TLS.CipherSuites }}
- {{ . }}
{{- end }}
minVersion: {{ .TLS.MinVersion }}
maxVersion: {{ .TLS.MaxVersion }}
renegotiation: {{ .TLS.Renegotiation }}
defaults.json:
ca-url: {{ .Defaults.CaUrl }}
ca-config: {{ .Defaults.CaConfig }}
fingerprint: {{ .Defaults.Fingerprint }}
root: {{ .Defaults.Root }}
# Certificates contains the root and intermediate certificate and
# optionally the SSH host and user public keys
certificates:
# intermediate_ca contains the text of the intermediate CA Certificate
intermediate_ca: |
{{- index .Files .Intermediate | toString | nindent 6 }}
# root_ca contains the text of the root CA Certificate
root_ca: |
{{- first .Root | index .Files | toString | nindent 6 }}
{{- if .Ssh }}
# ssh_host_ca contains the text of the public ssh key for the SSH root CA
ssh_host_ca: {{ index .Files .Ssh.HostPublicKey | toString }}
# ssh_user_ca contains the text of the public ssh key for the SSH root CA
ssh_user_ca: {{ index .Files .Ssh.UserPublicKey | toString }}
{{- end }}
# Secrets contains the root and intermediate keys and optionally the SSH
# private keys
secrets:
# ca_password contains the password used to encrypt x509.intermediate_ca_key, ssh.host_ca_key and ssh.user_ca_key
# This value must be base64 encoded.
ca_password: {{ .Password | b64enc }}
provisioner_password: {{ .Password | b64enc}}
x509:
# intermediate_ca_key contains the contents of your encrypted intermediate CA key
intermediate_ca_key: |
{{- index .Files .IntermediateKey | toString | nindent 8 }}
# root_ca_key contains the contents of your encrypted root CA key
# Note that this value can be omitted without impacting the functionality of step-certificates
# If supplied, this should be encrypted using a unique password that is not used for encrypting
# the intermediate_ca_key, ssh.host_ca_key or ssh.user_ca_key.
root_ca_key: |
{{- first .RootKey | index .Files | toString | nindent 8 }}
{{- if .Ssh }}
ssh:
# ssh_host_ca_key contains the contents of your encrypted SSH Host CA key
host_ca_key: |
{{- index .Files .Ssh.HostKey | toString | nindent 8 }}
# ssh_user_ca_key contains the contents of your encrypted SSH User CA key
user_ca_key: |
{{- index .Files .Ssh.UserKey | toString | nindent 8 }}
{{- end }}
`

View file

@ -156,96 +156,108 @@ func GetProvisionerKey(caURL, rootFile, kid string) (string, error) {
} }
type options struct { type options struct {
address string // address string
caURL string // caURL string
dnsNames []string // dnsNames []string
provisioner string provisioner string
enableACME bool enableACME bool
enableSSH bool enableSSH bool
enableAdmin bool enableAdmin bool
noDB bool noDB bool
isHelm bool
deploymentType DeploymentType deploymentType DeploymentType
} }
// PKIOption is the type of a configuration option on the pki constructor. // PKIOption is the type of a configuration option on the pki constructor.
type PKIOption func(o *options) type PKIOption func(p *PKI)
// WithAddress sets the listen address of step-ca. // WithAddress sets the listen address of step-ca.
func WithAddress(s string) PKIOption { func WithAddress(s string) PKIOption {
return func(o *options) { return func(p *PKI) {
o.address = s p.Address = s
} }
} }
// WithCaUrl sets the default ca-url of step-ca. // WithCaUrl sets the default ca-url of step-ca.
func WithCaUrl(s string) PKIOption { func WithCaUrl(s string) PKIOption {
return func(o *options) { return func(p *PKI) {
o.caURL = s p.Defaults.CaUrl = s
} }
} }
// WithDNSNames sets the SANs of step-ca. // WithDNSNames sets the SANs of step-ca.
func WithDNSNames(s []string) PKIOption { func WithDNSNames(s []string) PKIOption {
return func(o *options) { return func(p *PKI) {
o.dnsNames = s p.DnsNames = s
} }
} }
// WithProvisioner defines the name of the default provisioner. // WithProvisioner defines the name of the default provisioner.
func WithProvisioner(s string) PKIOption { func WithProvisioner(s string) PKIOption {
return func(o *options) { return func(p *PKI) {
o.provisioner = s p.options.provisioner = s
} }
} }
// WithACME enables acme provisioner in step-ca. // WithACME enables acme provisioner in step-ca.
func WithACME() PKIOption { func WithACME() PKIOption {
return func(o *options) { return func(p *PKI) {
o.enableACME = true p.options.enableACME = true
} }
} }
// WithSSH enables ssh in step-ca. // WithSSH enables ssh in step-ca.
func WithSSH() PKIOption { func WithSSH() PKIOption {
return func(o *options) { return func(p *PKI) {
o.enableSSH = true p.options.enableSSH = true
} }
} }
// WithAdmin enables the admin api in step-ca. // WithAdmin enables the admin api in step-ca.
func WithAdmin() PKIOption { func WithAdmin() PKIOption {
return func(o *options) { return func(p *PKI) {
o.enableAdmin = true p.options.enableAdmin = true
} }
} }
// WithNoDB disables the db in step-ca. // WithNoDB disables the db in step-ca.
func WithNoDB() PKIOption { func WithNoDB() PKIOption {
return func(o *options) { return func(p *PKI) {
o.noDB = true p.options.noDB = true
}
}
// WithHelm configures the pki to create a helm values.yaml.
func WithHelm() PKIOption {
return func(p *PKI) {
p.options.isHelm = true
} }
} }
// WithDeploymentType defines the deployment type of step-ca. // WithDeploymentType defines the deployment type of step-ca.
func WithDeploymentType(dt DeploymentType) PKIOption { func WithDeploymentType(dt DeploymentType) PKIOption {
return func(o *options) { return func(p *PKI) {
o.deploymentType = dt p.options.deploymentType = dt
} }
} }
// PKI represents the Public Key Infrastructure used by a certificate authority. // PKI represents the Public Key Infrastructure used by a certificate authority.
type PKI struct { type PKI struct {
casOptions apiv1.Options linkedca.Configuration
caService apiv1.CertificateAuthorityService Defaults linkedca.Defaults
caCreator apiv1.CertificateAuthorityCreator casOptions apiv1.Options
root, rootKey, rootFingerprint string caService apiv1.CertificateAuthorityService
intermediate, intermediateKey string caCreator apiv1.CertificateAuthorityCreator
sshHostPubKey, sshHostKey string // root, rootKey, rootFingerprint string
sshUserPubKey, sshUserKey string // intermediate, intermediateKey string
config, defaults string // sshHostPubKey, sshHostKey string
ottPublicKey *jose.JSONWebKey // sshUserPubKey, sshUserKey string
ottPrivateKey *jose.JSONWebEncryption config string
options *options defaults string
// rootFingerprint string
ottPublicKey *jose.JSONWebKey
ottPrivateKey *jose.JSONWebEncryption
options *options
} }
// New creates a new PKI configuration. // New creates a new PKI configuration.
@ -264,20 +276,6 @@ func New(o apiv1.Options, opts ...PKIOption) (*PKI, error) {
caCreator = creator caCreator = creator
} }
public := GetPublicPath()
private := GetSecretsPath()
config := GetConfigPath()
// Create directories
dirs := []string{public, private, config, GetTemplatesPath()}
for _, name := range dirs {
if _, err := os.Stat(name); os.IsNotExist(err) {
if err = os.MkdirAll(name, 0700); err != nil {
return nil, errs.FileError(err, name)
}
}
}
// get absolute path for dir/name // get absolute path for dir/name
getPath := func(dir string, name string) (string, error) { getPath := func(dir string, name string) (string, error) {
s, err := filepath.Abs(filepath.Join(dir, name)) s, err := filepath.Abs(filepath.Join(dir, name))
@ -285,52 +283,96 @@ func New(o apiv1.Options, opts ...PKIOption) (*PKI, error) {
} }
p := &PKI{ p := &PKI{
Configuration: linkedca.Configuration{
Address: "127.0.0.1:9000",
DnsNames: []string{"127.0.0.1"},
Ssh: &linkedca.SSH{},
Files: make(map[string][]byte),
},
casOptions: o, casOptions: o,
caCreator: caCreator, caCreator: caCreator,
caService: caService, caService: caService,
options: &options{ options: &options{
provisioner: "step-cli", provisioner: "step-cli",
address: "127.0.0.1:9000",
dnsNames: []string{"127.0.0.1"},
}, },
} }
for _, fn := range opts { for _, fn := range opts {
fn(p.options) fn(p)
} }
if p.root, err = getPath(public, "root_ca.crt"); err != nil { // Use /home/step as the step path in helm configurations.
return nil, err // Use the current step path when creating pki in files.
} var public, private, config string
if p.rootKey, err = getPath(private, "root_ca_key"); err != nil { if p.options.isHelm {
return nil, err public = "/home/step/certs"
} private = "/home/step/secrets"
if p.intermediate, err = getPath(public, "intermediate_ca.crt"); err != nil { config = "/home/step/config"
return nil, err } else {
} public = GetPublicPath()
if p.intermediateKey, err = getPath(private, "intermediate_ca_key"); err != nil { private = GetSecretsPath()
return nil, err config = GetConfigPath()
} // Create directories
if p.sshHostPubKey, err = getPath(public, "ssh_host_ca_key.pub"); err != nil { dirs := []string{public, private, config, GetTemplatesPath()}
return nil, err for _, name := range dirs {
} if _, err := os.Stat(name); os.IsNotExist(err) {
if p.sshUserPubKey, err = getPath(public, "ssh_user_ca_key.pub"); err != nil { if err = os.MkdirAll(name, 0700); err != nil {
return nil, err return nil, errs.FileError(err, name)
} }
if p.sshHostKey, err = getPath(private, "ssh_host_ca_key"); err != nil { }
return nil, err
}
if p.sshUserKey, err = getPath(private, "ssh_user_ca_key"); err != nil {
return nil, err
}
if len(config) > 0 {
if p.config, err = getPath(config, "ca.json"); err != nil {
return nil, err
}
if p.defaults, err = getPath(config, "defaults.json"); err != nil {
return nil, err
} }
} }
if p.Defaults.CaUrl == "" {
p.Defaults.CaUrl = p.DnsNames[0]
_, port, err := net.SplitHostPort(p.Address)
if err != nil {
return nil, errors.Wrapf(err, "error parsing %s", p.Address)
}
if port == "443" {
p.Defaults.CaUrl = fmt.Sprintf("https://%s", p.Defaults.CaUrl)
} else {
p.Defaults.CaUrl = fmt.Sprintf("https://%s:%s", p.Defaults.CaUrl, port)
}
}
root, err := getPath(public, "root_ca.crt")
if err != nil {
return nil, err
}
rootKey, err := getPath(private, "root_ca_key")
if err != nil {
return nil, err
}
p.Root = []string{root}
p.RootKey = []string{rootKey}
p.Defaults.Root = root
if p.Intermediate, err = getPath(public, "intermediate_ca.crt"); err != nil {
return nil, err
}
if p.IntermediateKey, err = getPath(private, "intermediate_ca_key"); err != nil {
return nil, err
}
if p.Ssh.HostPublicKey, err = getPath(public, "ssh_host_ca_key.pub"); err != nil {
return nil, err
}
if p.Ssh.UserPublicKey, err = getPath(public, "ssh_user_ca_key.pub"); err != nil {
return nil, err
}
if p.Ssh.HostKey, err = getPath(private, "ssh_host_ca_key"); err != nil {
return nil, err
}
if p.Ssh.UserKey, err = getPath(private, "ssh_user_ca_key"); err != nil {
return nil, err
}
if p.defaults, err = getPath(config, "defaults.json"); err != nil {
return nil, err
}
if p.config, err = getPath(config, "ca.json"); err != nil {
return nil, err
}
p.Defaults.CaConfig = p.config
return p, nil return p, nil
} }
@ -341,7 +383,7 @@ func (p *PKI) GetCAConfigPath() string {
// GetRootFingerprint returns the root fingerprint. // GetRootFingerprint returns the root fingerprint.
func (p *PKI) GetRootFingerprint() string { func (p *PKI) GetRootFingerprint() string {
return p.rootFingerprint return p.Defaults.Fingerprint
} }
// SetProvisioner sets the provisioner name of the OTT keys. // SetProvisioner sets the provisioner name of the OTT keys.
@ -355,21 +397,21 @@ func (p *PKI) SetProvisioner(s string) {
// //
// Deprecated: this method is deprecated in favor of WithAddress. // Deprecated: this method is deprecated in favor of WithAddress.
func (p *PKI) SetAddress(s string) { func (p *PKI) SetAddress(s string) {
p.options.address = s p.Address = s
} }
// SetDNSNames sets the dns names of the CA. // SetDNSNames sets the dns names of the CA.
// //
// Deprecated: this method is deprecated in favor of WithDNSNames. // Deprecated: this method is deprecated in favor of WithDNSNames.
func (p *PKI) SetDNSNames(s []string) { func (p *PKI) SetDNSNames(s []string) {
p.options.dnsNames = s p.DnsNames = s
} }
// SetCAURL sets the ca-url to use in the defaults.json. // SetCAURL sets the ca-url to use in the defaults.json.
// //
// Deprecated: this method is deprecated in favor of WithCaUrl. // Deprecated: this method is deprecated in favor of WithCaUrl.
func (p *PKI) SetCAURL(s string) { func (p *PKI) SetCAURL(s string) {
p.options.caURL = s p.Defaults.CaUrl = s
} }
// GenerateKeyPairs generates the key pairs used by the certificate authority. // GenerateKeyPairs generates the key pairs used by the certificate authority.
@ -408,11 +450,19 @@ func (p *PKI) GenerateRootCertificate(name, org, resource string, pass []byte) (
return nil, err return nil, err
} }
// PrivateKey will only be set if we have access to it (SoftCAS). sum := sha256.Sum256(resp.Certificate.Raw)
if err := p.WriteRootCertificate(resp.Certificate, resp.PrivateKey, pass); err != nil { p.Defaults.Fingerprint = strings.ToLower(hex.EncodeToString(sum[:]))
p.Files[p.Root[0]] = encodeCertificate(resp.Certificate)
p.Files[p.RootKey[0]], err = encodePrivateKey(resp.PrivateKey, pass)
if err != nil {
return nil, err return nil, err
} }
// PrivateKey will only be set if we have access to it (SoftCAS).
// if err := p.WriteRootCertificate(resp.Certificate, resp.PrivateKey, pass); err != nil {
// return nil, err
// }
return resp, nil return resp, nil
} }
@ -442,12 +492,24 @@ func (p *PKI) GenerateIntermediateCertificate(name, org, resource string, parent
} }
p.casOptions.CertificateAuthority = resp.Name p.casOptions.CertificateAuthority = resp.Name
return p.WriteIntermediateCertificate(resp.Certificate, resp.PrivateKey, pass) p.Files[p.Intermediate] = encodeCertificate(resp.Certificate)
p.Files[p.IntermediateKey], err = encodePrivateKey(resp.PrivateKey, pass)
if err != nil {
return err
}
return nil
// return p.WriteIntermediateCertificate(resp.Certificate, resp.PrivateKey, pass)
} }
// WriteRootCertificate writes to disk the given certificate and key. // WriteRootCertificate writes to disk the given certificate and key.
func (p *PKI) WriteRootCertificate(rootCrt *x509.Certificate, rootKey interface{}, pass []byte) error { func (p *PKI) WriteRootCertificate(rootCrt *x509.Certificate, rootKey interface{}, pass []byte) error {
if err := fileutil.WriteFile(p.root, pem.EncodeToMemory(&pem.Block{ fmt.Println(p.options.isHelm)
if p.options.isHelm {
return nil
}
if err := fileutil.WriteFile(p.Root[0], pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE", Type: "CERTIFICATE",
Bytes: rootCrt.Raw, Bytes: rootCrt.Raw,
}), 0600); err != nil { }), 0600); err != nil {
@ -455,28 +517,32 @@ func (p *PKI) WriteRootCertificate(rootCrt *x509.Certificate, rootKey interface{
} }
if rootKey != nil { if rootKey != nil {
_, err := pemutil.Serialize(rootKey, pemutil.WithPassword(pass), pemutil.ToFile(p.rootKey, 0600)) _, err := pemutil.Serialize(rootKey, pemutil.WithPassword(pass), pemutil.ToFile(p.RootKey[0], 0600))
if err != nil { if err != nil {
return err return err
} }
} }
sum := sha256.Sum256(rootCrt.Raw) sum := sha256.Sum256(rootCrt.Raw)
p.rootFingerprint = strings.ToLower(hex.EncodeToString(sum[:])) p.Defaults.Fingerprint = strings.ToLower(hex.EncodeToString(sum[:]))
return nil return nil
} }
// WriteIntermediateCertificate writes to disk the given certificate and key. // WriteIntermediateCertificate writes to disk the given certificate and key.
func (p *PKI) WriteIntermediateCertificate(crt *x509.Certificate, key interface{}, pass []byte) error { func (p *PKI) WriteIntermediateCertificate(crt *x509.Certificate, key interface{}, pass []byte) error {
if err := fileutil.WriteFile(p.intermediate, pem.EncodeToMemory(&pem.Block{ if p.options.isHelm {
return nil
}
if err := fileutil.WriteFile(p.Intermediate, pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE", Type: "CERTIFICATE",
Bytes: crt.Raw, Bytes: crt.Raw,
}), 0600); err != nil { }), 0600); err != nil {
return err return err
} }
if key != nil { if key != nil {
_, err := pemutil.Serialize(key, pemutil.WithPassword(pass), pemutil.ToFile(p.intermediateKey, 0600)) _, err := pemutil.Serialize(key, pemutil.WithPassword(pass), pemutil.ToFile(p.IntermediateKey, 0600))
if err != nil { if err != nil {
return err return err
} }
@ -516,8 +582,8 @@ func (p *PKI) GetCertificateAuthority() error {
} }
// Issuer is in the RA // Issuer is in the RA
p.intermediate = "" p.Intermediate = ""
p.intermediateKey = "" p.IntermediateKey = ""
return nil return nil
} }
@ -525,8 +591,8 @@ func (p *PKI) GetCertificateAuthority() error {
// GenerateSSHSigningKeys generates and encrypts a private key used for signing // GenerateSSHSigningKeys generates and encrypts a private key used for signing
// SSH user certificates and a private key used for signing host certificates. // SSH user certificates and a private key used for signing host certificates.
func (p *PKI) GenerateSSHSigningKeys(password []byte) error { func (p *PKI) GenerateSSHSigningKeys(password []byte) error {
var pubNames = []string{p.sshHostPubKey, p.sshUserPubKey} var pubNames = []string{p.Ssh.HostPublicKey, p.Ssh.UserPublicKey}
var privNames = []string{p.sshHostKey, p.sshUserKey} var privNames = []string{p.Ssh.HostKey, p.Ssh.UserKey}
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
pub, priv, err := keyutil.GenerateDefaultKeyPair() pub, priv, err := keyutil.GenerateDefaultKeyPair()
if err != nil { if err != nil {
@ -539,13 +605,19 @@ func (p *PKI) GenerateSSHSigningKeys(password []byte) error {
if err != nil { if err != nil {
return errors.Wrapf(err, "error converting public key") return errors.Wrapf(err, "error converting public key")
} }
_, err = pemutil.Serialize(priv, pemutil.WithFilename(privNames[i]), pemutil.WithPassword(password)) p.Files[pubNames[i]] = ssh.MarshalAuthorizedKey(sshKey)
p.Files[privNames[i]], err = encodePrivateKey(priv, password)
if err != nil { if err != nil {
return err return err
} }
if err = fileutil.WriteFile(pubNames[i], ssh.MarshalAuthorizedKey(sshKey), 0600); err != nil {
return err // _, err = pemutil.Serialize(priv, pemutil.WithFilename(privNames[i]), pemutil.WithPassword(password))
} // if err != nil {
// return err
// }
// if err = fileutil.WriteFile(pubNames[i], ssh.MarshalAuthorizedKey(sshKey), 0600); err != nil {
// return err
// }
} }
p.options.enableSSH = true p.options.enableSSH = true
return nil return nil
@ -575,22 +647,22 @@ func (p *PKI) TellPKI() {
func (p *PKI) tellPKI() { func (p *PKI) tellPKI() {
ui.Println() ui.Println()
if p.casOptions.Is(apiv1.SoftCAS) { if p.casOptions.Is(apiv1.SoftCAS) {
ui.PrintSelected("Root certificate", p.root) ui.PrintSelected("Root certificate", p.Root[0])
ui.PrintSelected("Root private key", p.rootKey) ui.PrintSelected("Root private key", p.RootKey[0])
ui.PrintSelected("Root fingerprint", p.rootFingerprint) ui.PrintSelected("Root fingerprint", p.Defaults.Fingerprint)
ui.PrintSelected("Intermediate certificate", p.intermediate) ui.PrintSelected("Intermediate certificate", p.Intermediate)
ui.PrintSelected("Intermediate private key", p.intermediateKey) ui.PrintSelected("Intermediate private key", p.IntermediateKey)
} else if p.rootFingerprint != "" { } else if p.Defaults.Fingerprint != "" {
ui.PrintSelected("Root certificate", p.root) ui.PrintSelected("Root certificate", p.Root[0])
ui.PrintSelected("Root fingerprint", p.rootFingerprint) ui.PrintSelected("Root fingerprint", p.Defaults.Fingerprint)
} else { } else {
ui.Printf(`{{ "%s" | red }} {{ "Root certificate:" | bold }} failed to retrieve it from RA`+"\n", ui.IconBad) ui.Printf(`{{ "%s" | red }} {{ "Root certificate:" | bold }} failed to retrieve it from RA`+"\n", ui.IconBad)
} }
if p.options.enableSSH { if p.options.enableSSH {
ui.PrintSelected("SSH user root certificate", p.sshUserPubKey) ui.PrintSelected("SSH user public key", p.Ssh.UserPublicKey)
ui.PrintSelected("SSH user root private key", p.sshUserKey) ui.PrintSelected("SSH user private key", p.Ssh.UserKey)
ui.PrintSelected("SSH host root certificate", p.sshHostPubKey) ui.PrintSelected("SSH host public key", p.Ssh.HostPublicKey)
ui.PrintSelected("SSH host root private key", p.sshHostKey) ui.PrintSelected("SSH host private key", p.Ssh.HostKey)
} }
} }
@ -637,12 +709,12 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) {
} }
config := &authconfig.Config{ config := &authconfig.Config{
Root: []string{p.root}, Root: p.Root,
FederatedRoots: []string{}, FederatedRoots: p.FederatedRoots,
IntermediateCert: p.intermediate, IntermediateCert: p.Intermediate,
IntermediateKey: p.intermediateKey, IntermediateKey: p.IntermediateKey,
Address: p.options.address, Address: p.Address,
DNSNames: p.options.dnsNames, DNSNames: p.DnsNames,
Logger: []byte(`{"format": "text"}`), Logger: []byte(`{"format": "text"}`),
DB: &db.Config{ DB: &db.Config{
Type: "badger", Type: "badger",
@ -685,8 +757,8 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) {
if p.options.enableSSH { if p.options.enableSSH {
enableSSHCA := true enableSSHCA := true
config.SSH = &authconfig.SSHConfig{ config.SSH = &authconfig.SSHConfig{
HostKey: p.sshHostKey, HostKey: p.Ssh.HostKey,
UserKey: p.sshUserKey, UserKey: p.Ssh.UserKey,
} }
// Enable SSH authorization for default JWK provisioner // Enable SSH authorization for default JWK provisioner
prov.Claims = &provisioner.Claims{ prov.Claims = &provisioner.Claims{
@ -776,26 +848,12 @@ func (p *PKI) Save(opt ...Option) error {
return errs.FileError(err, p.config) return errs.FileError(err, p.config)
} }
// Generate the CA URL.
if p.options.caURL == "" {
p.options.caURL = p.options.dnsNames[0]
_, port, err := net.SplitHostPort(p.options.address)
if err != nil {
return errors.Wrapf(err, "error parsing %s", p.options.address)
}
if port == "443" {
p.options.caURL = fmt.Sprintf("https://%s", p.options.caURL)
} else {
p.options.caURL = fmt.Sprintf("https://%s:%s", p.options.caURL, port)
}
}
// Generate and write defaults.json // Generate and write defaults.json
defaults := &caDefaults{ defaults := &caDefaults{
Root: p.root, Root: p.Defaults.Root,
CAConfig: p.config, CAConfig: p.Defaults.CaConfig,
CAUrl: p.options.caURL, CAUrl: p.Defaults.CaUrl,
Fingerprint: p.rootFingerprint, Fingerprint: p.Defaults.Fingerprint,
} }
b, err = json.MarshalIndent(defaults, "", "\t") b, err = json.MarshalIndent(defaults, "", "\t")
if err != nil { if err != nil {
@ -830,3 +888,26 @@ func (p *PKI) Save(opt ...Option) error {
return nil return nil
} }
func encodeCertificate(c *x509.Certificate) []byte {
return pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: c.Raw,
})
}
func encodePublicKey(key crypto.PublicKey) ([]byte, error) {
block, err := pemutil.Serialize(key)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(block), nil
}
func encodePrivateKey(key crypto.PrivateKey, pass []byte) ([]byte, error) {
block, err := pemutil.Serialize(key, pemutil.WithPassword(pass))
if err != nil {
return nil, err
}
return pem.EncodeToMemory(block), nil
}