1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
package pki
import (
"fmt"
"io"
"text/template"
"github.com/pkg/errors"
"github.com/smallstep/certificates/authority"
authconfig "github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/templates"
"github.com/smallstep/linkedca"
)
type helmVariables struct {
*linkedca.Configuration
Defaults *linkedca.Defaults
Password string
EnableSSH bool
EnableAdmin bool
TLS authconfig.TLSOptions
Provisioners []provisioner.Interface
}
// WriteHelmTemplate a helm template to configure the
// smallstep/step-certificates helm chart.
func (p *PKI) WriteHelmTemplate(w io.Writer) error {
tmpl, err := template.New("helm").Funcs(templates.StepFuncMap()).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
}
// Convert provisioners to ca.json representation
provisioners := []provisioner.Interface{}
for _, p := range p.Authority.Provisioners {
pp, err := authority.ProvisionerToCertificates(p)
if err != nil {
return err
}
provisioners = append(provisioners, pp)
}
// Add default ACME provisioner if enabled. Note that this logic is similar
// to what's in p.GenerateConfig(), but that codepath isn't taken when
// writing the Helm template. The default JWK provisioner is added earlier in
// the process and that's part of the provisioners above.
//
// To prevent name clashes for the default ACME provisioner, we append "-1" to
// the name if it already exists. See https://github.com/smallstep/cli/issues/1018
// for the reason.
//
// TODO(hs): consider refactoring the initialization, so that this becomes
// easier to reason about and maintain.
if p.options.enableACME {
acmeProvisionerName := "acme"
for _, prov := range provisioners {
if prov.GetName() == acmeProvisionerName {
acmeProvisionerName = fmt.Sprintf("%s-1", acmeProvisionerName)
break
}
}
provisioners = append(provisioners, &provisioner.ACME{
Type: "ACME",
Name: acmeProvisionerName,
})
}
// Add default SSHPOP provisioner if enabled. Similar to the above, this is
// the same as what happens in p.GenerateConfig(). To prevent name clashes for the
// default SSHPOP provisioner, we append "-1" to it if it already exists. See
// https://github.com/smallstep/cli/issues/1018 for the reason.
if p.options.enableSSH {
sshProvisionerName := "sshpop"
for _, prov := range provisioners {
if prov.GetName() == sshProvisionerName {
sshProvisionerName = fmt.Sprintf("%s-1", sshProvisionerName)
break
}
}
provisioners = append(provisioners, &provisioner.SSHPOP{
Type: "SSHPOP",
Name: sshProvisionerName,
Claims: &provisioner.Claims{
EnableSSHCA: &p.options.enableSSH,
},
})
}
if err := tmpl.Execute(w, helmVariables{
Configuration: &p.Configuration,
Defaults: &p.Defaults,
Password: "",
EnableSSH: p.options.enableSSH,
EnableAdmin: p.options.enableAdmin,
TLS: authconfig.DefaultTLSOptions,
Provisioners: provisioners,
}); 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 .Kms }}
kms:
type: {{ lower (.Kms.Type | toString) }}
{{- end }}
{{- if .EnableSSH }}
ssh:
hostKey: {{ .Ssh.HostKey }}
userKey: {{ .Ssh.UserKey }}
{{- end }}
address: {{ .Address }}
dnsNames:
{{- range .DnsNames }}
- {{ . }}
{{- end }}
logger:
format: json
db:
type: badgerv2
dataSource: /home/step/db
authority:
enableAdmin: {{ .EnableAdmin }}
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 }}
`
|