forked from TrueCloudLab/certificates
Merge pull request #707 from smallstep/password-flags
Add support for setting individual password for ssh and tls keys
This commit is contained in:
commit
ebf1afa96e
5 changed files with 136 additions and 26 deletions
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
49
ca/ca.go
49
ca/ca.go
|
@ -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),
|
||||||
|
|
|
@ -107,7 +107,9 @@ func main() {
|
||||||
app.HelpName = "step-ca"
|
app.HelpName = "step-ca"
|
||||||
app.Version = config.Version()
|
app.Version = config.Version()
|
||||||
app.Usage = "an online certificate authority for secure automated certificate management"
|
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
|
app.Description = `**step-ca** runs the Step Online Certificate Authority
|
||||||
(Step CA) using the given configuration.
|
(Step CA) using the given configuration.
|
||||||
See the README.md for more detailed configuration documentation.
|
See the README.md for more detailed configuration documentation.
|
||||||
|
|
|
@ -23,13 +23,26 @@ import (
|
||||||
var AppCommand = cli.Command{
|
var AppCommand = cli.Command{
|
||||||
Name: "start",
|
Name: "start",
|
||||||
Action: appAction,
|
Action: appAction,
|
||||||
UsageText: `**step-ca** <config>
|
UsageText: `**step-ca** <config> [**--password-file**=<file>]
|
||||||
[**--password-file**=<file>] [**--issuer-password-file**=<file>] [**--resolver**=<addr>]`,
|
[**--ssh-host-password-file**=<file>] [**--ssh-user-password-file**=<file>]
|
||||||
|
[**--issuer-password-file**=<file>] [**--resolver**=<addr>]`,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
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 +64,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 +104,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 +139,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 {
|
||||||
|
|
Loading…
Reference in a new issue