Allow to use custom SSH user/host key files.

This commit is contained in:
Mariano Cano 2019-08-01 15:04:56 -07:00
parent 00ebee870b
commit 004ea12212
4 changed files with 73 additions and 15 deletions

View file

@ -8,6 +8,7 @@ import (
"sync"
"time"
"github.com/pkg/errors"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/db"
"github.com/smallstep/cli/crypto/pemutil"
@ -120,8 +121,21 @@ func (a *Authority) init() error {
}
}
a.sshCAHostCertSignKey = a.intermediateIdentity.Key.(crypto.Signer)
a.sshCAUserCertSignKey = a.intermediateIdentity.Key.(crypto.Signer)
// Decrypt and load SSH keys
if a.config.SSH != nil {
if a.config.SSH.HostKey != "" {
a.sshCAHostCertSignKey, err = parseCryptoSigner(a.config.SSH.HostKey, a.config.Password)
if err != nil {
return err
}
}
if a.config.SSH.UserKey != "" {
a.sshCAUserCertSignKey, err = parseCryptoSigner(a.config.SSH.UserKey, a.config.Password)
if err != nil {
return err
}
}
}
// Store all the provisioners
for _, p := range a.config.AuthorityConfig.Provisioners {
@ -149,3 +163,19 @@ func (a *Authority) GetDatabase() db.AuthDB {
func (a *Authority) Shutdown() error {
return a.db.Shutdown()
}
func parseCryptoSigner(filename, password string) (crypto.Signer, error) {
var opts []pemutil.Options
if password != "" {
opts = append(opts, pemutil.WithPassword([]byte(password)))
}
key, err := pemutil.Read(filename, opts...)
if err != nil {
return nil, err
}
signer, ok := key.(crypto.Signer)
if !ok {
return nil, errors.Errorf("key %s of type %T cannot be used for signing operations", filename, key)
}
return signer, nil
}

View file

@ -78,20 +78,13 @@ func (a *Authority) authorizeToken(ott string) (provisioner.Interface, error) {
func (a *Authority) Authorize(ctx context.Context, ott string) ([]provisioner.SignOption, error) {
var errContext = apiCtx{"ott": ott}
switch m := provisioner.MethodFromContext(ctx); m {
case provisioner.SignMethod, provisioner.SignSSHMethod:
p, err := a.authorizeToken(ott)
if err != nil {
return nil, &apiError{errors.Wrap(err, "authorizeSign"), http.StatusUnauthorized, errContext}
case provisioner.SignMethod:
return a.authorizeSign(ctx, ott)
case provisioner.SignSSHMethod:
if a.sshCAHostCertSignKey == nil && a.sshCAUserCertSignKey == nil {
return nil, &apiError{errors.New("authorize: ssh signing is not enabled"), http.StatusNotImplemented, errContext}
}
// Call the provisioner AuthorizeSign method to apply provisioner specific
// auth claims and get the signing options.
opts, err := p.AuthorizeSign(ctx, ott)
if err != nil {
return nil, &apiError{errors.Wrap(err, "authorizeSign"), http.StatusUnauthorized, errContext}
}
return opts, nil
return a.authorizeSign(ctx, ott)
case provisioner.RevokeMethod:
return nil, &apiError{errors.New("authorize: revoke method is not supported"), http.StatusInternalServerError, errContext}
default:
@ -99,6 +92,22 @@ func (a *Authority) Authorize(ctx context.Context, ott string) ([]provisioner.Si
}
}
// authorizeSign loads the provisioner from the token, checks that it has not
// been used again and calls the provisioner AuthorizeSign method. returns a
// list of methods to apply to the signing flow.
func (a *Authority) authorizeSign(ctx context.Context, ott string) ([]provisioner.SignOption, error) {
var errContext = apiCtx{"ott": ott}
p, err := a.authorizeToken(ott)
if err != nil {
return nil, &apiError{errors.Wrap(err, "authorizeSign"), http.StatusUnauthorized, errContext}
}
opts, err := p.AuthorizeSign(ctx, ott)
if err != nil {
return nil, &apiError{errors.Wrap(err, "authorizeSign"), http.StatusUnauthorized, errContext}
}
return opts, nil
}
// AuthorizeSign authorizes a signature request by validating and authenticating
// a OTT that must be sent w/ the request.
func (a *Authority) AuthorizeSign(ott string) ([]provisioner.SignOption, error) {

View file

@ -50,6 +50,7 @@ type Config struct {
IntermediateKey string `json:"key"`
Address string `json:"address"`
DNSNames []string `json:"dnsNames"`
SSH *SSHConfig `json:"ssh,omitempty"`
Logger json.RawMessage `json:"logger,omitempty"`
DB *db.Config `json:"db,omitempty"`
Monitoring json.RawMessage `json:"monitoring,omitempty"`
@ -98,6 +99,12 @@ func (c *AuthConfig) Validate(audiences provisioner.Audiences) error {
return nil
}
// SSHConfig contains the user and host keys.
type SSHConfig struct {
HostKey string `json:"hostKey"`
UserKey string `json:"userKey"`
}
// LoadConfiguration parses the given filename in JSON format and returns the
// configuration struct.
func LoadConfiguration(filename string) (*Config, error) {

View file

@ -81,8 +81,20 @@ func (a *Authority) SignSSH(key ssh.PublicKey, opts provisioner.SSHOptions, sign
var signer ssh.Signer
switch cert.CertType {
case ssh.UserCert:
if a.sshCAUserCertSignKey == nil {
return nil, &apiError{
err: errors.New("signSSH: user certificate signing is not enabled"),
code: http.StatusNotImplemented,
}
}
signer, err = ssh.NewSignerFromSigner(a.sshCAUserCertSignKey)
case ssh.HostCert:
if a.sshCAHostCertSignKey == nil {
return nil, &apiError{
err: errors.New("signSSH: host certificate signing is not enabled"),
code: http.StatusNotImplemented,
}
}
signer, err = ssh.NewSignerFromSigner(a.sshCAHostCertSignKey)
default:
return nil, &apiError{