Skip to content

Commit

Permalink
Merge pull request #633 from smallstep/linkedca
Browse files Browse the repository at this point in the history
Linkedca
  • Loading branch information
maraino authored Aug 25, 2021
2 parents 2317bf1 + 2c5080a commit 9e57e4d
Show file tree
Hide file tree
Showing 26 changed files with 2,238 additions and 491 deletions.
55 changes: 39 additions & 16 deletions authority/authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,40 @@ import (
"crypto/x509"
"encoding/hex"
"log"
"strings"
"sync"
"time"

"github.com/smallstep/certificates/cas"
"github.com/smallstep/certificates/scep"
"go.step.sm/linkedca"

"github.com/pkg/errors"
"github.com/smallstep/certificates/authority/admin"
adminDBNosql "github.com/smallstep/certificates/authority/admin/db/nosql"
"github.com/smallstep/certificates/authority/administrator"
"github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/cas"
casapi "github.com/smallstep/certificates/cas/apiv1"
"github.com/smallstep/certificates/db"
"github.com/smallstep/certificates/kms"
kmsapi "github.com/smallstep/certificates/kms/apiv1"
"github.com/smallstep/certificates/kms/sshagentkms"
"github.com/smallstep/certificates/scep"
"github.com/smallstep/certificates/templates"
"github.com/smallstep/nosql"
"go.step.sm/crypto/pemutil"
"go.step.sm/linkedca"
"golang.org/x/crypto/ssh"
)

// Authority implements the Certificate Authority internal interface.
type Authority struct {
config *config.Config
keyManager kms.KeyManager
provisioners *provisioner.Collection
admins *administrator.Collection
db db.AuthDB
adminDB admin.DB
templates *templates.Templates
config *config.Config
keyManager kms.KeyManager
provisioners *provisioner.Collection
admins *administrator.Collection
db db.AuthDB
adminDB admin.DB
templates *templates.Templates
linkedCAToken string

// X509 CA
x509CAService cas.CertificateAuthorityService
Expand Down Expand Up @@ -205,6 +206,11 @@ func (a *Authority) init() error {

var err error

// Automatically enable admin for all linked cas.
if a.linkedCAToken != "" {
a.config.AuthorityConfig.EnableAdmin = true
}

// Initialize step-ca Database if it's not already initialized with WithDB.
// If a.config.DB is nil then a simple, barebones in memory DB will be used.
if a.db == nil {
Expand Down Expand Up @@ -442,18 +448,32 @@ func (a *Authority) init() error {
// Initialize step-ca Admin Database if it's not already initialized using
// WithAdminDB.
if a.adminDB == nil {
// Check if AuthConfig already exists
a.adminDB, err = adminDBNosql.New(a.db.(nosql.DB), admin.DefaultAuthorityID)
if err != nil {
return err
if a.linkedCAToken == "" {
// Check if AuthConfig already exists
a.adminDB, err = adminDBNosql.New(a.db.(nosql.DB), admin.DefaultAuthorityID)
if err != nil {
return err
}
} else {
// Use the linkedca client as the admindb.
client, err := newLinkedCAClient(a.linkedCAToken)
if err != nil {
return err
}
// If authorityId is configured make sure it matches the one in the token
if id := a.config.AuthorityConfig.AuthorityID; id != "" && !strings.EqualFold(id, client.authorityID) {
return errors.New("error initializing linkedca: token authority and configured authority do not match")
}
client.Run()
a.adminDB = client
}
}

provs, err := a.adminDB.GetProvisioners(context.Background())
if err != nil {
return admin.WrapErrorISE(err, "error loading provisioners to initialize authority")
}
if len(provs) == 0 {
if len(provs) == 0 && !strings.EqualFold(a.config.AuthorityConfig.DeploymentType, "linked") {
// Create First Provisioner
prov, err := CreateFirstProvisioner(context.Background(), a.adminDB, a.config.Password)
if err != nil {
Expand Down Expand Up @@ -527,6 +547,9 @@ func (a *Authority) CloseForReload() {
if err := a.keyManager.Close(); err != nil {
log.Printf("error closing the key manager: %v", err)
}
if client, ok := a.adminDB.(*linkedCaClient); ok {
client.Stop()
}
}

// requiresDecrypter returns whether the Authority
Expand Down
34 changes: 33 additions & 1 deletion authority/authorize.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/x509"
"encoding/hex"
"net/http"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -273,10 +274,19 @@ func (a *Authority) authorizeRevoke(ctx context.Context, token string) error {
//
// TODO(mariano): should we authorize by default?
func (a *Authority) authorizeRenew(cert *x509.Certificate) error {
var err error
var isRevoked bool
var opts = []interface{}{errs.WithKeyVal("serialNumber", cert.SerialNumber.String())}

// Check the passive revocation table.
isRevoked, err := a.db.IsRevoked(cert.SerialNumber.String())
serial := cert.SerialNumber.String()
if lca, ok := a.adminDB.(interface {
IsRevoked(string) (bool, error)
}); ok {
isRevoked, err = lca.IsRevoked(serial)
} else {
isRevoked, err = a.db.IsRevoked(serial)
}
if err != nil {
return errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeRenew", opts...)
}
Expand All @@ -294,6 +304,28 @@ func (a *Authority) authorizeRenew(cert *x509.Certificate) error {
return nil
}

// authorizeSSHCertificate returns an error if the given certificate is revoked.
func (a *Authority) authorizeSSHCertificate(ctx context.Context, cert *ssh.Certificate) error {
var err error
var isRevoked bool

serial := strconv.FormatUint(cert.Serial, 10)
if lca, ok := a.adminDB.(interface {
IsSSHRevoked(string) (bool, error)
}); ok {
isRevoked, err = lca.IsSSHRevoked(serial)
} else {
isRevoked, err = a.db.IsSSHRevoked(serial)
}
if err != nil {
return errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeSSHCertificate", errs.WithKeyVal("serialNumber", serial))
}
if isRevoked {
return errs.Unauthorized("authority.authorizeSSHCertificate: certificate has been revoked", errs.WithKeyVal("serialNumber", serial))
}
return nil
}

// authorizeSSHSign loads the provisioner from the token, checks that it has not
// been used again and calls the provisioner AuthorizeSSHSign method. Returns a
// list of methods to apply to the signing flow.
Expand Down
11 changes: 7 additions & 4 deletions authority/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ type ASN1DN struct {
Locality string `json:"locality,omitempty"`
Province string `json:"province,omitempty"`
StreetAddress string `json:"streetAddress,omitempty"`
SerialNumber string `json:"serialNumber,omitempty"`
CommonName string `json:"commonName,omitempty"`
}

Expand All @@ -83,8 +84,9 @@ type ASN1DN struct {
// cas.Options.
type AuthConfig struct {
*cas.Options
AuthorityID string `json:"authorityID,omitempty"`
Provisioners provisioner.List `json:"provisioners"`
AuthorityID string `json:"authorityId,omitempty"`
DeploymentType string `json:"deploymentType,omitempty"`
Provisioners provisioner.List `json:"provisioners,omitempty"`
Admins []*linkedca.Admin `json:"-"`
Template *ASN1DN `json:"template,omitempty"`
Claims *provisioner.Claims `json:"claims,omitempty"`
Expand Down Expand Up @@ -188,9 +190,10 @@ func (c *Config) Validate() error {
switch {
case c.Address == "":
return errors.New("address cannot be empty")

case len(c.DNSNames) == 0:
return errors.New("dnsNames cannot be empty")
case c.AuthorityConfig == nil:
return errors.New("authority cannot be nil")
}

// Options holds the RA/CAS configuration.
Expand Down Expand Up @@ -222,7 +225,7 @@ func (c *Config) Validate() error {
c.TLS.MaxVersion = DefaultTLSOptions.MaxVersion
}
if c.TLS.MinVersion == 0 {
c.TLS.MinVersion = c.TLS.MaxVersion
c.TLS.MinVersion = DefaultTLSOptions.MinVersion
}
if c.TLS.MinVersion > c.TLS.MaxVersion {
return errors.New("tls minVersion cannot exceed tls maxVersion")
Expand Down
72 changes: 40 additions & 32 deletions authority/config/tls_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ var (
// DefaultTLSRenegotiation default TLS connection renegotiation policy.
DefaultTLSRenegotiation = false // Never regnegotiate.
// DefaultTLSCipherSuites specifies default step ciphersuite(s).
// These are TLS 1.0 - 1.2 cipher suites.
DefaultTLSCipherSuites = CipherSuites{
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
}
// ApprovedTLSCipherSuites smallstep approved ciphersuites.
Expand All @@ -26,25 +27,21 @@ var (
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
}
// DefaultTLSOptions represents the default TLS version as well as the cipher
// suites used in the TLS certificates.
DefaultTLSOptions = TLSOptions{
CipherSuites: CipherSuites{
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
},
MinVersion: 1.2,
MaxVersion: 1.2,
Renegotiation: false,
CipherSuites: DefaultTLSCipherSuites,
MinVersion: DefaultTLSMinVersion,
MaxVersion: DefaultTLSMaxVersion,
Renegotiation: DefaultTLSRenegotiation,
}
)

Expand Down Expand Up @@ -119,27 +116,38 @@ func (c CipherSuites) Value() []uint16 {

// cipherSuites has the list of supported cipher suites.
var cipherSuites = map[string]uint16{
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
// TLS 1.0 - 1.2 cipher suites.
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,

// TLS 1.3 cipher sutes.
"TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256,
"TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384,
"TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256,

// Legacy names.
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
}

// TLSOptions represents the TLS options that can be specified on *tls.Config
Expand Down
Loading

0 comments on commit 9e57e4d

Please sign in to comment.