forked from TrueCloudLab/certificates
Allow to use custom SSH user/host key files.
This commit is contained in:
parent
00ebee870b
commit
004ea12212
4 changed files with 73 additions and 15 deletions
|
@ -8,6 +8,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/certificates/db"
|
"github.com/smallstep/certificates/db"
|
||||||
"github.com/smallstep/cli/crypto/pemutil"
|
"github.com/smallstep/cli/crypto/pemutil"
|
||||||
|
@ -120,8 +121,21 @@ func (a *Authority) init() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.sshCAHostCertSignKey = a.intermediateIdentity.Key.(crypto.Signer)
|
// Decrypt and load SSH keys
|
||||||
a.sshCAUserCertSignKey = a.intermediateIdentity.Key.(crypto.Signer)
|
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
|
// Store all the provisioners
|
||||||
for _, p := range a.config.AuthorityConfig.Provisioners {
|
for _, p := range a.config.AuthorityConfig.Provisioners {
|
||||||
|
@ -149,3 +163,19 @@ func (a *Authority) GetDatabase() db.AuthDB {
|
||||||
func (a *Authority) Shutdown() error {
|
func (a *Authority) Shutdown() error {
|
||||||
return a.db.Shutdown()
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -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) {
|
func (a *Authority) Authorize(ctx context.Context, ott string) ([]provisioner.SignOption, error) {
|
||||||
var errContext = apiCtx{"ott": ott}
|
var errContext = apiCtx{"ott": ott}
|
||||||
switch m := provisioner.MethodFromContext(ctx); m {
|
switch m := provisioner.MethodFromContext(ctx); m {
|
||||||
case provisioner.SignMethod, provisioner.SignSSHMethod:
|
case provisioner.SignMethod:
|
||||||
p, err := a.authorizeToken(ott)
|
return a.authorizeSign(ctx, ott)
|
||||||
if err != nil {
|
case provisioner.SignSSHMethod:
|
||||||
return nil, &apiError{errors.Wrap(err, "authorizeSign"), http.StatusUnauthorized, errContext}
|
if a.sshCAHostCertSignKey == nil && a.sshCAUserCertSignKey == nil {
|
||||||
|
return nil, &apiError{errors.New("authorize: ssh signing is not enabled"), http.StatusNotImplemented, errContext}
|
||||||
}
|
}
|
||||||
|
return a.authorizeSign(ctx, ott)
|
||||||
// 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
|
|
||||||
case provisioner.RevokeMethod:
|
case provisioner.RevokeMethod:
|
||||||
return nil, &apiError{errors.New("authorize: revoke method is not supported"), http.StatusInternalServerError, errContext}
|
return nil, &apiError{errors.New("authorize: revoke method is not supported"), http.StatusInternalServerError, errContext}
|
||||||
default:
|
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
|
// AuthorizeSign authorizes a signature request by validating and authenticating
|
||||||
// a OTT that must be sent w/ the request.
|
// a OTT that must be sent w/ the request.
|
||||||
func (a *Authority) AuthorizeSign(ott string) ([]provisioner.SignOption, error) {
|
func (a *Authority) AuthorizeSign(ott string) ([]provisioner.SignOption, error) {
|
||||||
|
|
|
@ -50,6 +50,7 @@ type Config struct {
|
||||||
IntermediateKey string `json:"key"`
|
IntermediateKey string `json:"key"`
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
DNSNames []string `json:"dnsNames"`
|
DNSNames []string `json:"dnsNames"`
|
||||||
|
SSH *SSHConfig `json:"ssh,omitempty"`
|
||||||
Logger json.RawMessage `json:"logger,omitempty"`
|
Logger json.RawMessage `json:"logger,omitempty"`
|
||||||
DB *db.Config `json:"db,omitempty"`
|
DB *db.Config `json:"db,omitempty"`
|
||||||
Monitoring json.RawMessage `json:"monitoring,omitempty"`
|
Monitoring json.RawMessage `json:"monitoring,omitempty"`
|
||||||
|
@ -98,6 +99,12 @@ func (c *AuthConfig) Validate(audiences provisioner.Audiences) error {
|
||||||
return nil
|
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
|
// LoadConfiguration parses the given filename in JSON format and returns the
|
||||||
// configuration struct.
|
// configuration struct.
|
||||||
func LoadConfiguration(filename string) (*Config, error) {
|
func LoadConfiguration(filename string) (*Config, error) {
|
||||||
|
|
|
@ -81,8 +81,20 @@ func (a *Authority) SignSSH(key ssh.PublicKey, opts provisioner.SSHOptions, sign
|
||||||
var signer ssh.Signer
|
var signer ssh.Signer
|
||||||
switch cert.CertType {
|
switch cert.CertType {
|
||||||
case ssh.UserCert:
|
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)
|
signer, err = ssh.NewSignerFromSigner(a.sshCAUserCertSignKey)
|
||||||
case ssh.HostCert:
|
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)
|
signer, err = ssh.NewSignerFromSigner(a.sshCAHostCertSignKey)
|
||||||
default:
|
default:
|
||||||
return nil, &apiError{
|
return nil, &apiError{
|
||||||
|
|
Loading…
Reference in a new issue