Add support for setting individual password for ssh and tls keys

This change add the following flags:
 * --ssh-host-password-file
 * --ssh-user-password-file

Fixes #693
This commit is contained in:
Mariano Cano 2021-09-16 11:55:41 -07:00
parent e3acea9704
commit 6729c79253
4 changed files with 130 additions and 23 deletions

View file

@ -43,6 +43,8 @@ type Authority struct {
linkedCAToken string linkedCAToken string
// X509 CA // X509 CA
password []byte
issuerPassword []byte
x509CAService cas.CertificateAuthorityService x509CAService cas.CertificateAuthorityService
rootX509Certs []*x509.Certificate rootX509Certs []*x509.Certificate
rootX509CertPool *x509.CertPool rootX509CertPool *x509.CertPool
@ -53,6 +55,8 @@ type Authority struct {
scepService *scep.Service scepService *scep.Service
// SSH CA // SSH CA
sshHostPassword []byte
sshUserPassword []byte
sshCAUserCertSignKey ssh.Signer sshCAUserCertSignKey ssh.Signer
sshCAHostCertSignKey ssh.Signer sshCAHostCertSignKey ssh.Signer
sshCAUserCerts []ssh.PublicKey sshCAUserCerts []ssh.PublicKey
@ -206,6 +210,21 @@ func (a *Authority) init() error {
var err 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. // Automatically enable admin for all linked cas.
if a.linkedCAToken != "" { if a.linkedCAToken != "" {
a.config.AuthorityConfig.EnableAdmin = true a.config.AuthorityConfig.EnableAdmin = true
@ -238,6 +257,11 @@ func (a *Authority) init() error {
options = *a.config.AuthorityConfig.Options 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. // Read intermediate and create X509 signer for default CAS.
if options.Is(casapi.SoftCAS) { if options.Is(casapi.SoftCAS) {
options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) 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{ options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
SigningKey: a.config.IntermediateKey, SigningKey: a.config.IntermediateKey,
Password: []byte(a.config.Password), Password: []byte(a.password),
}) })
if err != nil { if err != nil {
return err return err
@ -315,7 +339,7 @@ func (a *Authority) init() error {
if a.config.SSH.HostKey != "" { if a.config.SSH.HostKey != "" {
signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
SigningKey: a.config.SSH.HostKey, SigningKey: a.config.SSH.HostKey,
Password: []byte(a.config.Password), Password: []byte(a.sshHostPassword),
}) })
if err != nil { if err != nil {
return err return err
@ -341,7 +365,7 @@ func (a *Authority) init() error {
if a.config.SSH.UserKey != "" { if a.config.SSH.UserKey != "" {
signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
SigningKey: a.config.SSH.UserKey, SigningKey: a.config.SSH.UserKey,
Password: []byte(a.config.Password), Password: []byte(a.sshUserPassword),
}) })
if err != nil { if err != nil {
return err return err
@ -420,7 +444,7 @@ func (a *Authority) init() error {
} }
options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
SigningKey: a.config.IntermediateKey, SigningKey: a.config.IntermediateKey,
Password: []byte(a.config.Password), Password: []byte(a.password),
}) })
if err != nil { if err != nil {
return err return err
@ -429,7 +453,7 @@ func (a *Authority) init() error {
if km, ok := a.keyManager.(kmsapi.Decrypter); ok { if km, ok := a.keyManager.(kmsapi.Decrypter); ok {
options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
DecryptionKey: a.config.IntermediateKey, DecryptionKey: a.config.IntermediateKey,
Password: []byte(a.config.Password), Password: []byte(a.password),
}) })
if err != nil { if err != nil {
return err return err
@ -475,7 +499,7 @@ func (a *Authority) init() error {
} }
if len(provs) == 0 && !strings.EqualFold(a.config.AuthorityConfig.DeploymentType, "linked") { if len(provs) == 0 && !strings.EqualFold(a.config.AuthorityConfig.DeploymentType, "linked") {
// Create First Provisioner // 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 { if err != nil {
return admin.WrapErrorISE(err, "error creating first provisioner") 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 // WithDatabase sets an already initialized authority database to a new
// authority. This option is intended to be use on graceful reloads. // authority. This option is intended to be use on graceful reloads.
func WithDatabase(db db.AuthDB) Option { func WithDatabase(db db.AuthDB) Option {

View file

@ -29,11 +29,13 @@ import (
) )
type options struct { type options struct {
configFile string configFile string
linkedCAToken string linkedCAToken string
password []byte password []byte
issuerPassword []byte issuerPassword []byte
database db.AuthDB sshHostPassword []byte
sshUserPassword []byte
database db.AuthDB
} }
func (o *options) apply(opts []Option) { 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 // WithIssuerPassword sets the given password as the configured certificate
// issuer password in the CA options. // issuer password in the CA options.
func WithIssuerPassword(password []byte) Option { 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. // Init initializes the CA with the given configuration.
func (ca *CA) Init(config *config.Config) (*CA, error) { func (ca *CA) Init(config *config.Config) (*CA, error) {
// Intermediate Password. // Set password, it's ok to set nil password, the ca will prompt for them if
if len(ca.opts.password) > 0 { // they are required.
ca.config.Password = string(ca.opts.password) 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 != "" { if ca.opts.linkedCAToken != "" {
opts = append(opts, authority.WithLinkedCAToken(ca.opts.linkedCAToken)) opts = append(opts, authority.WithLinkedCAToken(ca.opts.linkedCAToken))
} }
@ -337,6 +350,8 @@ func (ca *CA) Reload() error {
newCA, err := New(config, newCA, err := New(config,
WithPassword(ca.opts.password), WithPassword(ca.opts.password),
WithSSHHostPassword(ca.opts.sshHostPassword),
WithSSHUserPassword(ca.opts.sshUserPassword),
WithIssuerPassword(ca.opts.issuerPassword), WithIssuerPassword(ca.opts.issuerPassword),
WithLinkedCAToken(ca.opts.linkedCAToken), WithLinkedCAToken(ca.opts.linkedCAToken),
WithConfigFile(ca.opts.configFile), WithConfigFile(ca.opts.configFile),

View file

@ -30,6 +30,18 @@ var AppCommand = cli.Command{
Name: "password-file", Name: "password-file",
Usage: `path to the <file> containing the password to decrypt the Usage: `path to the <file> containing the password to decrypt the
intermediate private key.`, 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{ cli.StringFlag{
Name: "issuer-password-file", Name: "issuer-password-file",
@ -51,6 +63,8 @@ certificate issuer private key used in the RA mode.`,
// AppAction is the action used when the top command runs. // AppAction is the action used when the top command runs.
func appAction(ctx *cli.Context) error { func appAction(ctx *cli.Context) error {
passFile := ctx.String("password-file") 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") issuerPassFile := ctx.String("issuer-password-file")
resolver := ctx.String("resolver") resolver := ctx.String("resolver")
token := ctx.String("token") token := ctx.String("token")
@ -89,6 +103,22 @@ To get a linked authority token:
password = bytes.TrimRightFunc(password, unicode.IsSpace) 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 var issuerPassword []byte
if issuerPassFile != "" { if issuerPassFile != "" {
if issuerPassword, err = ioutil.ReadFile(issuerPassFile); err != nil { if issuerPassword, err = ioutil.ReadFile(issuerPassFile); err != nil {
@ -108,6 +138,8 @@ To get a linked authority token:
srv, err := ca.New(config, srv, err := ca.New(config,
ca.WithConfigFile(configFile), ca.WithConfigFile(configFile),
ca.WithPassword(password), ca.WithPassword(password),
ca.WithSSHHostPassword(sshHostPassword),
ca.WithSSHUserPassword(sshUserPassword),
ca.WithIssuerPassword(issuerPassword), ca.WithIssuerPassword(issuerPassword),
ca.WithLinkedCAToken(token)) ca.WithLinkedCAToken(token))
if err != nil { if err != nil {