func (a *Authority) CheckSSHHost(ctx context.Context, principal, token string) (bool, error) {
	if a.sshCheckHostFunc != nil {
		exists, err := a.sshCheckHostFunc(ctx, principal, token, a.GetRootCertificates())
		if err != nil {
			return false, errs.Wrap(http.StatusInternalServerError, err,
				"checkSSHHost: error from injected checkSSHHost func")
		}
		return exists, nil
	}
	exists, err := a.db.IsSSHHost(principal)
	if err != nil {
		if err == db.ErrNotImplemented {
			return false, errs.Wrap(http.StatusNotImplemented, err,
				"checkSSHHost: isSSHHost is not implemented")
		}
		return false, errs.Wrap(http.StatusInternalServerError, err,
			"checkSSHHost: error checking if hosts exists")
	}
	return exists, nil
}
CheckSSHHost checks the given principal has been registered before.
package authority
import (
	"context"
	"crypto/rand"
	"crypto/x509"
	"encoding/binary"
	"net/http"
	"strings"
	"time"
	"github.com/pkg/errors"
	"github.com/smallstep/certificates/authority/config"
	"github.com/smallstep/certificates/authority/provisioner"
	"github.com/smallstep/certificates/db"
	"github.com/smallstep/certificates/errs"
	"github.com/smallstep/certificates/templates"
	"go.step.sm/crypto/randutil"
	"go.step.sm/crypto/sshutil"
	"golang.org/x/crypto/ssh"
)
const (
	SSHAddUserPrincipal = "provisioner"
	SSHAddUserCommand = "sudo useradd -m <principal>; nc -q0 localhost 22"
)
func (a *Authority) GetSSHRoots(context.Context) (*config.SSHKeys, error) {
	return &config.SSHKeys{
		HostKeys: a.sshCAHostCerts,
		UserKeys: a.sshCAUserCerts,
	}, nil
}
func (a *Authority) GetSSHFederation(context.Context) (*config.SSHKeys, error) {
	return &config.SSHKeys{
		HostKeys: a.sshCAHostFederatedCerts,
		UserKeys: a.sshCAUserFederatedCerts,
	}, nil
}
func (a *Authority) GetSSHConfig(ctx context.Context, typ string, data map[string]string) ([]templates.Output, error) {
	if a.sshCAUserCertSignKey == nil && a.sshCAHostCertSignKey == nil {
		return nil, errs.NotFound("getSSHConfig: ssh is not configured")
	}
	if a.templates == nil {
		return nil, errs.NotFound("getSSHConfig: ssh templates are not configured")
	}
	var ts []templates.Template
	switch typ {
	case provisioner.SSHUserCert:
		if a.templates != nil && a.templates.SSH != nil {
			ts = a.templates.SSH.User
		}
	case provisioner.SSHHostCert:
		if a.templates != nil && a.templates.SSH != nil {
			ts = a.templates.SSH.Host
		}
	default:
		return nil, errs.BadRequest("getSSHConfig: type %s is not valid", typ)
	}
	var mergedData map[string]interface{}
	if len(data) == 0 {
		mergedData = a.templates.Data
	} else {
		mergedData = make(map[string]interface{}, len(a.templates.Data)+1)
		mergedData["User"] = data
		for k, v := range a.templates.Data {
			mergedData[k] = v
		}
	}
	output := []templates.Output{}
	for _, t := range ts {
		if err := t.Load(); err != nil {
			return nil, err
		}
		if err := t.ValidateRequiredData(data); err != nil {
			return nil, errs.BadRequestErr(err, errs.WithMessage("%v, please use `--set <key=value>` flag", err))
		}
		o, err := t.Output(mergedData)
		if err != nil {
			return nil, err
		}
		output = append(output, o)
	}
	return output, nil
}
func (a *Authority) GetSSHBastion(ctx context.Context, user, hostname string) (*config.Bastion, error) {
	if a.sshBastionFunc != nil {
		bs, err := a.sshBastionFunc(ctx, user, hostname)
		return bs, errs.Wrap(http.StatusInternalServerError, err, "authority.GetSSHBastion")
	}
	if a.config.SSH != nil {
		if a.config.SSH.Bastion != nil && a.config.SSH.Bastion.Hostname != "" {
			if !strings.EqualFold(hostname, a.config.SSH.Bastion.Hostname) {
				return a.config.SSH.Bastion, nil
			}
		}
		return nil, nil
	}
	return nil, errs.NotFound("authority.GetSSHBastion; ssh is not configured")
}
func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisioner.SignSSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) {
	var (
		certOptions []sshutil.Option
		mods        []provisioner.SSHCertModifier
		validators  []provisioner.SSHCertValidator
	)
	if err := opts.Validate(); err != nil {
		return nil, errs.Wrap(http.StatusBadRequest, err, "authority.SignSSH")
	}
	opts.Backdate = a.config.AuthorityConfig.Backdate.Duration
	for _, op := range signOpts {
		switch o := op.(type) {
		case provisioner.SSHCertificateOptions:
			certOptions = append(certOptions, o.Options(opts)...)
		case provisioner.SSHCertModifier:
			mods = append(mods, o)
		case provisioner.SSHCertValidator:
			validators = append(validators, o)
		case provisioner.SSHCertOptionsValidator:
			if err := o.Valid(opts); err != nil {
				return nil, errs.Wrap(http.StatusForbidden, err, "authority.SignSSH")
			}
		default:
			return nil, errs.InternalServer("authority.SignSSH: invalid extra option type %T", o)
		}
	}
	cr := sshutil.CertificateRequest{
		Type:       opts.CertType,
		KeyID:      opts.KeyID,
		Principals: opts.Principals,
		Key:        key,
	}
	certificate, err := sshutil.NewCertificate(cr, certOptions...)
	if err != nil {
		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")
	}
	certTpl := certificate.GetCertificate()
	if err := opts.ModifyValidity(certTpl); err != nil {
		return nil, errs.Wrap(http.StatusBadRequest, err, "authority.SignSSH")
	}
	for _, m := range mods {
		if err := m.Modify(certTpl, opts); err != nil {
			return nil, errs.Wrap(http.StatusForbidden, err, "authority.SignSSH")
		}
	}
	var signer ssh.Signer
	switch certTpl.CertType {
	case ssh.UserCert:
		if a.sshCAUserCertSignKey == nil {
			return nil, errs.NotImplemented("authority.SignSSH: user certificate signing is not enabled")
		}
		signer = a.sshCAUserCertSignKey
	case ssh.HostCert:
		if a.sshCAHostCertSignKey == nil {
			return nil, errs.NotImplemented("authority.SignSSH: host certificate signing is not enabled")
		}
		signer = a.sshCAHostCertSignKey
	default:
		return nil, errs.InternalServer("authority.SignSSH: unexpected ssh certificate type: %d", certTpl.CertType)
	}
	cert, err := sshutil.CreateCertificate(certTpl, signer)
	if err != nil {
		return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.SignSSH: error signing certificate")
	}
	for _, v := range validators {
		if err := v.Valid(cert, opts); err != nil {
			return nil, errs.Wrap(http.StatusForbidden, err, "authority.SignSSH")
		}
	}
	if err = a.storeSSHCertificate(cert); err != nil && err != db.ErrNotImplemented {
		return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.SignSSH: error storing certificate in db")
	}
	return cert, nil
}
func (a *Authority) RenewSSH(ctx context.Context, oldCert *ssh.Certificate) (*ssh.Certificate, error) {
	if oldCert.ValidAfter == 0 || oldCert.ValidBefore == 0 {
		return nil, errs.BadRequest("renewSSH: cannot renew certificate without validity period")
	}
	if err := a.authorizeSSHCertificate(ctx, oldCert); err != nil {
		return nil, err
	}
	backdate := a.config.AuthorityConfig.Backdate.Duration
	duration := time.Duration(oldCert.ValidBefore-oldCert.ValidAfter) * time.Second
	now := time.Now()
	va := now.Add(-1 * backdate)
	vb := now.Add(duration - backdate)
	certTpl := &ssh.Certificate{
		Key:             oldCert.Key,
		CertType:        oldCert.CertType,
		KeyId:           oldCert.KeyId,
		ValidPrincipals: oldCert.ValidPrincipals,
		Permissions:     oldCert.Permissions,
		Reserved:        oldCert.Reserved,
		ValidAfter:      uint64(va.Unix()),
		ValidBefore:     uint64(vb.Unix()),
	}
	var signer ssh.Signer
	switch certTpl.CertType {
	case ssh.UserCert:
		if a.sshCAUserCertSignKey == nil {
			return nil, errs.NotImplemented("renewSSH: user certificate signing is not enabled")
		}
		signer = a.sshCAUserCertSignKey
	case ssh.HostCert:
		if a.sshCAHostCertSignKey == nil {
			return nil, errs.NotImplemented("renewSSH: host certificate signing is not enabled")
		}
		signer = a.sshCAHostCertSignKey
	default:
		return nil, errs.InternalServer("renewSSH: unexpected ssh certificate type: %d", certTpl.CertType)
	}
	cert, err := sshutil.CreateCertificate(certTpl, signer)
	if err != nil {
		return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSH: error signing certificate")
	}
	if err = a.storeSSHCertificate(cert); err != nil && err != db.ErrNotImplemented {
		return nil, errs.Wrap(http.StatusInternalServerError, err, "renewSSH: error storing certificate in db")
	}
	return cert, nil
}
func (a *Authority) RekeySSH(ctx context.Context, oldCert *ssh.Certificate, pub ssh.PublicKey, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) {
	var validators []provisioner.SSHCertValidator
	for _, op := range signOpts {
		switch o := op.(type) {
		case provisioner.SSHCertValidator:
			validators = append(validators, o)
		default:
			return nil, errs.InternalServer("rekeySSH; invalid extra option type %T", o)
		}
	}
	if oldCert.ValidAfter == 0 || oldCert.ValidBefore == 0 {
		return nil, errs.BadRequest("rekeySSH; cannot rekey certificate without validity period")
	}
	if err := a.authorizeSSHCertificate(ctx, oldCert); err != nil {
		return nil, err
	}
	backdate := a.config.AuthorityConfig.Backdate.Duration
	duration := time.Duration(oldCert.ValidBefore-oldCert.ValidAfter) * time.Second
	now := time.Now()
	va := now.Add(-1 * backdate)
	vb := now.Add(duration - backdate)
	cert := &ssh.Certificate{
		Key:             pub,
		CertType:        oldCert.CertType,
		KeyId:           oldCert.KeyId,
		ValidPrincipals: oldCert.ValidPrincipals,
		Permissions:     oldCert.Permissions,
		Reserved:        oldCert.Reserved,
		ValidAfter:      uint64(va.Unix()),
		ValidBefore:     uint64(vb.Unix()),
	}
	var signer ssh.Signer
	switch cert.CertType {
	case ssh.UserCert:
		if a.sshCAUserCertSignKey == nil {
			return nil, errs.NotImplemented("rekeySSH; user certificate signing is not enabled")
		}
		signer = a.sshCAUserCertSignKey
	case ssh.HostCert:
		if a.sshCAHostCertSignKey == nil {
			return nil, errs.NotImplemented("rekeySSH; host certificate signing is not enabled")
		}
		signer = a.sshCAHostCertSignKey
	default:
		return nil, errs.BadRequest("rekeySSH; unexpected ssh certificate type: %d", cert.CertType)
	}
	var err error
	cert, err = sshutil.CreateCertificate(cert, signer)
	if err != nil {
		return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSH: error signing certificate")
	}
	for _, v := range validators {
		if err := v.Valid(cert, provisioner.SignSSHOptions{Backdate: backdate}); err != nil {
			return nil, errs.Wrap(http.StatusForbidden, err, "rekeySSH")
		}
	}
	if err = a.storeSSHCertificate(cert); err != nil && err != db.ErrNotImplemented {
		return nil, errs.Wrap(http.StatusInternalServerError, err, "rekeySSH; error storing certificate in db")
	}
	return cert, nil
}
func (a *Authority) storeSSHCertificate(cert *ssh.Certificate) error {
	type sshCertificateStorer interface {
		StoreSSHCertificate(crt *ssh.Certificate) error
	}
	if s, ok := a.adminDB.(sshCertificateStorer); ok {
		return s.StoreSSHCertificate(cert)
	}
	return a.db.StoreSSHCertificate(cert)
}
func IsValidForAddUser(cert *ssh.Certificate) error {
	if cert.CertType != ssh.UserCert {
		return errors.New("certificate is not a user certificate")
	}
	switch len(cert.ValidPrincipals) {
	case 0:
		return errors.New("certificate does not have any principals")
	case 1:
		return nil
	case 2:
		if strings.Index(cert.ValidPrincipals[1], "@") > 0 {
			return nil
		}
		return errors.New("certificate does not have only one principal")
	default:
		return errors.New("certificate does not have only one principal")
	}
}
func (a *Authority) SignSSHAddUser(ctx context.Context, key ssh.PublicKey, subject *ssh.Certificate) (*ssh.Certificate, error) {
	if a.sshCAUserCertSignKey == nil {
		return nil, errs.NotImplemented("signSSHAddUser: user certificate signing is not enabled")
	}
	if err := IsValidForAddUser(subject); err != nil {
		return nil, errs.Wrap(http.StatusForbidden, err, "signSSHAddUser")
	}
	nonce, err := randutil.ASCII(32)
	if err != nil {
		return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSHAddUser")
	}
	var serial uint64
	if err := binary.Read(rand.Reader, binary.BigEndian, &serial); err != nil {
		return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSHAddUser: error reading random number")
	}
	signer := a.sshCAUserCertSignKey
	principal := subject.ValidPrincipals[0]
	addUserPrincipal := a.getAddUserPrincipal()
	cert := &ssh.Certificate{
		Nonce:           []byte(nonce),
		Key:             key,
		Serial:          serial,
		CertType:        ssh.UserCert,
		KeyId:           principal + "-" + addUserPrincipal,
		ValidPrincipals: []string{addUserPrincipal},
		ValidAfter:      subject.ValidAfter,
		ValidBefore:     subject.ValidBefore,
		Permissions: ssh.Permissions{
			CriticalOptions: map[string]string{
				"force-command": a.getAddUserCommand(principal),
			},
		},
		SignatureKey: signer.PublicKey(),
	}
	data := cert.Marshal()
	data = data[:len(data)-4]
	sig, err := signer.Sign(rand.Reader, data)
	if err != nil {
		return nil, err
	}
	cert.Signature = sig
	if err = a.storeSSHCertificate(cert); err != nil && err != db.ErrNotImplemented {
		return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSHAddUser: error storing certificate in db")
	}
	return cert, nil
}