Merge pull request #707 from smallstep/password-flags

Add support for setting individual password for ssh and tls keys
This commit is contained in:
Mariano Cano 2021-09-16 13:50:03 -07:00 committed by GitHub
commit ebf1afa96e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 136 additions and 26 deletions

View file

@ -43,6 +43,8 @@ type Authority struct {
linkedCAToken string
// X509 CA
password []byte
issuerPassword []byte
x509CAService cas.CertificateAuthorityService
rootX509Certs []*x509.Certificate
rootX509CertPool *x509.CertPool
@ -53,6 +55,8 @@ type Authority struct {
scepService *scep.Service
// SSH CA
sshHostPassword []byte
sshUserPassword []byte
sshCAUserCertSignKey ssh.Signer
sshCAHostCertSignKey ssh.Signer
sshCAUserCerts []ssh.PublicKey
@ -206,6 +210,21 @@ func (a *Authority) init() error {
var err error
// Set password if they are not set.
var configPassword []byte
if a.config.Password != "" {
configPassword = []byte(a.config.Password)
}
if configPassword != nil && a.password == nil {
a.password = configPassword
}
if a.sshHostPassword == nil {
a.sshHostPassword = a.password
}
if a.sshUserPassword == nil {
a.sshUserPassword = a.password
}
// Automatically enable admin for all linked cas.
if a.linkedCAToken != "" {
a.config.AuthorityConfig.EnableAdmin = true
@ -238,6 +257,11 @@ func (a *Authority) init() error {
options = *a.config.AuthorityConfig.Options
}
// Set the issuer password if passed in the flags.
if options.CertificateIssuer != nil && a.issuerPassword != nil {
options.CertificateIssuer.Password = string(a.issuerPassword)
}
// Read intermediate and create X509 signer for default CAS.
if options.Is(casapi.SoftCAS) {
options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert)
@ -246,7 +270,7 @@ func (a *Authority) init() error {
}
options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
SigningKey: a.config.IntermediateKey,
Password: []byte(a.config.Password),
Password: []byte(a.password),
})
if err != nil {
return err
@ -315,7 +339,7 @@ func (a *Authority) init() error {
if a.config.SSH.HostKey != "" {
signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
SigningKey: a.config.SSH.HostKey,
Password: []byte(a.config.Password),
Password: []byte(a.sshHostPassword),
})
if err != nil {
return err
@ -341,7 +365,7 @@ func (a *Authority) init() error {
if a.config.SSH.UserKey != "" {
signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
SigningKey: a.config.SSH.UserKey,
Password: []byte(a.config.Password),
Password: []byte(a.sshUserPassword),
})
if err != nil {
return err
@ -420,7 +444,7 @@ func (a *Authority) init() error {
}
options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
SigningKey: a.config.IntermediateKey,
Password: []byte(a.config.Password),
Password: []byte(a.password),
})
if err != nil {
return err
@ -429,7 +453,7 @@ func (a *Authority) init() error {
if km, ok := a.keyManager.(kmsapi.Decrypter); ok {
options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
DecryptionKey: a.config.IntermediateKey,
Password: []byte(a.config.Password),
Password: []byte(a.password),
})
if err != nil {
return err
@ -475,7 +499,7 @@ func (a *Authority) init() error {
}
if len(provs) == 0 && !strings.EqualFold(a.config.AuthorityConfig.DeploymentType, "linked") {
// Create First Provisioner
prov, err := CreateFirstProvisioner(context.Background(), a.adminDB, a.config.Password)
prov, err := CreateFirstProvisioner(context.Background(), a.adminDB, string(a.password))
if err != nil {
return admin.WrapErrorISE(err, "error creating first provisioner")
}

View file

@ -38,6 +38,42 @@ func WithConfigFile(filename string) Option {
}
}
// WithPassword set the password to decrypt the intermediate key as well as the
// ssh host and user keys if they are not overridden by other options.
func WithPassword(password []byte) Option {
return func(a *Authority) (err error) {
a.password = password
return
}
}
// WithSSHHostPassword set the password to decrypt the key used to sign SSH host
// certificates.
func WithSSHHostPassword(password []byte) Option {
return func(a *Authority) (err error) {
a.sshHostPassword = password
return
}
}
// WithSSHUserPassword set the password to decrypt the key used to sign SSH user
// certificates.
func WithSSHUserPassword(password []byte) Option {
return func(a *Authority) (err error) {
a.sshUserPassword = password
return
}
}
// WithIssuerPassword set the password to decrypt the certificate issuer private
// key used in RA mode.
func WithIssuerPassword(password []byte) Option {
return func(a *Authority) (err error) {
a.issuerPassword = password
return
}
}
// WithDatabase sets an already initialized authority database to a new
// authority. This option is intended to be use on graceful reloads.
func WithDatabase(db db.AuthDB) Option {

View file

@ -29,11 +29,13 @@ import (
)
type options struct {
configFile string
linkedCAToken string
password []byte
issuerPassword []byte
database db.AuthDB
configFile string
linkedCAToken string
password []byte
issuerPassword []byte
sshHostPassword []byte
sshUserPassword []byte
database db.AuthDB
}
func (o *options) apply(opts []Option) {
@ -61,6 +63,22 @@ func WithPassword(password []byte) Option {
}
}
// WithSSHHostPassword sets the given password to decrypt the key used to sign
// ssh host certificates.
func WithSSHHostPassword(password []byte) Option {
return func(o *options) {
o.sshHostPassword = password
}
}
// WithSSHUserPassword sets the given password to decrypt the key used to sign
// ssh user certificates.
func WithSSHUserPassword(password []byte) Option {
return func(o *options) {
o.sshUserPassword = password
}
}
// WithIssuerPassword sets the given password as the configured certificate
// issuer password in the CA options.
func WithIssuerPassword(password []byte) Option {
@ -106,19 +124,14 @@ func New(config *config.Config, opts ...Option) (*CA, error) {
// Init initializes the CA with the given configuration.
func (ca *CA) Init(config *config.Config) (*CA, error) {
// Intermediate Password.
if len(ca.opts.password) > 0 {
ca.config.Password = string(ca.opts.password)
// Set password, it's ok to set nil password, the ca will prompt for them if
// they are required.
opts := []authority.Option{
authority.WithPassword(ca.opts.password),
authority.WithSSHHostPassword(ca.opts.sshHostPassword),
authority.WithSSHUserPassword(ca.opts.sshUserPassword),
authority.WithIssuerPassword(ca.opts.issuerPassword),
}
// Certificate issuer password for RA mode.
if len(ca.opts.issuerPassword) > 0 {
if ca.config.AuthorityConfig != nil && ca.config.AuthorityConfig.CertificateIssuer != nil {
ca.config.AuthorityConfig.CertificateIssuer.Password = string(ca.opts.issuerPassword)
}
}
var opts []authority.Option
if ca.opts.linkedCAToken != "" {
opts = append(opts, authority.WithLinkedCAToken(ca.opts.linkedCAToken))
}
@ -337,6 +350,8 @@ func (ca *CA) Reload() error {
newCA, err := New(config,
WithPassword(ca.opts.password),
WithSSHHostPassword(ca.opts.sshHostPassword),
WithSSHUserPassword(ca.opts.sshUserPassword),
WithIssuerPassword(ca.opts.issuerPassword),
WithLinkedCAToken(ca.opts.linkedCAToken),
WithConfigFile(ca.opts.configFile),

View file

@ -107,7 +107,9 @@ func main() {
app.HelpName = "step-ca"
app.Version = config.Version()
app.Usage = "an online certificate authority for secure automated certificate management"
app.UsageText = `**step-ca** <config> [**--password-file**=<file>] [**--issuer-password-file**=<file>] [**--resolver**=<addr>] [**--help**] [**--version**]`
app.UsageText = `**step-ca** <config> [**--password-file**=<file>]
[**--ssh-host-password-file**=<file>] [**--ssh-user-password-file**=<file>]
[**--issuer-password-file**=<file>] [**--resolver**=<addr>] [**--help**] [**--version**]`
app.Description = `**step-ca** runs the Step Online Certificate Authority
(Step CA) using the given configuration.
See the README.md for more detailed configuration documentation.

View file

@ -23,13 +23,26 @@ import (
var AppCommand = cli.Command{
Name: "start",
Action: appAction,
UsageText: `**step-ca** <config>
[**--password-file**=<file>] [**--issuer-password-file**=<file>] [**--resolver**=<addr>]`,
UsageText: `**step-ca** <config> [**--password-file**=<file>]
[**--ssh-host-password-file**=<file>] [**--ssh-user-password-file**=<file>]
[**--issuer-password-file**=<file>] [**--resolver**=<addr>]`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "password-file",
Usage: `path to the <file> containing the password to decrypt the
intermediate private key.`,
},
cli.StringFlag{
Name: "ssh-host-password-file",
Usage: `path to the <file> containing the password to decrypt the
private key used to sign SSH host certificates. If the flag is not passed it
will default to --password-file.`,
},
cli.StringFlag{
Name: "ssh-user-password-file",
Usage: `path to the <file> containing the password to decrypt the
private key used to sign SSH user certificates. If the flag is not passed it
will default to --password-file.`,
},
cli.StringFlag{
Name: "issuer-password-file",
@ -51,6 +64,8 @@ certificate issuer private key used in the RA mode.`,
// AppAction is the action used when the top command runs.
func appAction(ctx *cli.Context) error {
passFile := ctx.String("password-file")
sshHostPassFile := ctx.String("ssh-host-password-file")
sshUserPassFile := ctx.String("ssh-user-password-file")
issuerPassFile := ctx.String("issuer-password-file")
resolver := ctx.String("resolver")
token := ctx.String("token")
@ -89,6 +104,22 @@ To get a linked authority token:
password = bytes.TrimRightFunc(password, unicode.IsSpace)
}
var sshHostPassword []byte
if sshHostPassFile != "" {
if sshHostPassword, err = ioutil.ReadFile(sshHostPassFile); err != nil {
fatal(errors.Wrapf(err, "error reading %s", sshHostPassFile))
}
sshHostPassword = bytes.TrimRightFunc(sshHostPassword, unicode.IsSpace)
}
var sshUserPassword []byte
if sshUserPassFile != "" {
if sshUserPassword, err = ioutil.ReadFile(sshUserPassFile); err != nil {
fatal(errors.Wrapf(err, "error reading %s", sshUserPassFile))
}
sshUserPassword = bytes.TrimRightFunc(sshUserPassword, unicode.IsSpace)
}
var issuerPassword []byte
if issuerPassFile != "" {
if issuerPassword, err = ioutil.ReadFile(issuerPassFile); err != nil {
@ -108,6 +139,8 @@ To get a linked authority token:
srv, err := ca.New(config,
ca.WithConfigFile(configFile),
ca.WithPassword(password),
ca.WithSSHHostPassword(sshHostPassword),
ca.WithSSHUserPassword(sshUserPassword),
ca.WithIssuerPassword(issuerPassword),
ca.WithLinkedCAToken(token))
if err != nil {