Use sshutil for SSH certificate signing.

This commit is contained in:
Mariano Cano 2020-07-27 15:43:41 -07:00
parent 570ede45e7
commit b66d123572

View file

@ -205,56 +205,66 @@ func (a *Authority) GetSSHBastion(ctx context.Context, user string, hostname str
// SignSSH creates a signed SSH certificate with the given public key and options. // SignSSH creates a signed SSH certificate with the given public key and options.
func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisioner.SignSSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) { func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisioner.SignSSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) {
var mods []provisioner.SSHCertModifier var (
var validators []provisioner.SSHCertValidator certOptions []sshutil.Option
mods []provisioner.SSHCertModifier
validators []provisioner.SSHCertValidator
)
// Set backdate with the configured value // Set backdate with the configured value
opts.Backdate = a.config.AuthorityConfig.Backdate.Duration opts.Backdate = a.config.AuthorityConfig.Backdate.Duration
for _, op := range signOpts { for _, op := range signOpts {
switch o := op.(type) { switch o := op.(type) {
// add options to NewCertificate
case provisioner.SSHCertificateOptions:
certOptions = append(certOptions, o.Options(opts)...)
// modify the ssh.Certificate // modify the ssh.Certificate
case provisioner.SSHCertModifier: case provisioner.SSHCertModifier:
mods = append(mods, o) mods = append(mods, o)
// modify the ssh.Certificate given the SSHOptions // modify the ssh.Certificate given the SSHOptions
case provisioner.SSHCertOptionModifier: case provisioner.SSHCertOptionModifier:
mods = append(mods, o.Option(opts)) mods = append(mods, o.Option(opts))
// validate the ssh.Certificate // validate the ssh.Certificate
case provisioner.SSHCertValidator: case provisioner.SSHCertValidator:
validators = append(validators, o) validators = append(validators, o)
// validate the given SSHOptions // validate the given SSHOptions
case provisioner.SSHCertOptionsValidator: case provisioner.SSHCertOptionsValidator:
if err := o.Valid(opts); err != nil { if err := o.Valid(opts); err != nil {
return nil, errs.Wrap(http.StatusForbidden, err, "signSSH") return nil, errs.Wrap(http.StatusForbidden, err, "signSSH")
} }
default: default:
return nil, errs.InternalServer("signSSH: invalid extra option type %T", o) return nil, errs.InternalServer("signSSH: invalid extra option type %T", o)
} }
} }
nonce, err := randutil.ASCII(32) // Create certificate from template.
certificate, err := sshutil.NewCertificate(key, certOptions...)
if err != nil { if err != nil {
return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSH") if _, ok := err.(*sshutil.TemplateError); ok {
return nil, errs.NewErr(http.StatusBadRequest, err,
errs.WithMessage(err.Error()),
errs.WithKeyVal("signOptions", signOpts),
)
}
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.SignSSH")
} }
var serial uint64 // Get actual *ssh.Certificate and continue with user and provisioner
if err := binary.Read(rand.Reader, binary.BigEndian, &serial); err != nil { // modifiers.
return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSH: error reading random number") cert := certificate.GetCertificate()
}
// Build base certificate with the key and some random values // Use SignSSHOptions to modify the certificate.
cert := &ssh.Certificate{
Nonce: []byte(nonce),
Key: key,
Serial: serial,
}
// Use opts to modify the certificate
if err := opts.Modify(cert); err != nil { if err := opts.Modify(cert); err != nil {
return nil, errs.Wrap(http.StatusForbidden, err, "signSSH") return nil, errs.Wrap(http.StatusForbidden, err, "signSSH")
} }
// Use provisioner modifiers // Use provisioner modifiers.
for _, m := range mods { for _, m := range mods {
if err := m.Modify(cert); err != nil { if err := m.Modify(cert); err != nil {
return nil, errs.Wrap(http.StatusForbidden, err, "signSSH") return nil, errs.Wrap(http.StatusForbidden, err, "signSSH")
@ -277,20 +287,14 @@ func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisi
default: default:
return nil, errs.InternalServer("signSSH: unexpected ssh certificate type: %d", cert.CertType) return nil, errs.InternalServer("signSSH: unexpected ssh certificate type: %d", cert.CertType)
} }
cert.SignatureKey = signer.PublicKey()
// Get bytes for signing trailing the signature length. // Sign certificate.
data := cert.Marshal() cert, err = sshutil.CreateCertificate(cert, signer)
data = data[:len(data)-4]
// Sign the certificate
sig, err := signer.Sign(rand.Reader, data)
if err != nil { if err != nil {
return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSH: error signing certificate") return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSH: error signing certificate")
} }
cert.Signature = sig
// User provisioners validators // User provisioners validators.
for _, v := range validators { for _, v := range validators {
if err := v.Valid(cert, opts); err != nil { if err := v.Valid(cert, opts); err != nil {
return nil, errs.Wrap(http.StatusForbidden, err, "signSSH") return nil, errs.Wrap(http.StatusForbidden, err, "signSSH")