smallstep-certificates/authority/authority.go
Mariano Cano 7e95fc0e45 Strip ports on audience check.
Services might have proxies behind them so we cannot rely on them.
Fixes #17
2018-12-21 15:27:22 -08:00

123 lines
3.2 KiB
Go

package authority
import (
"crypto/sha256"
realx509 "crypto/x509"
"encoding/hex"
"fmt"
"sync"
"time"
"github.com/smallstep/cli/crypto/pemutil"
"github.com/smallstep/cli/crypto/x509util"
)
const legacyAuthority = "step-certificate-authority"
// Authority implements the Certificate Authority internal interface.
type Authority struct {
config *Config
rootX509Crt *realx509.Certificate
intermediateIdentity *x509util.Identity
validateOnce bool
certificates *sync.Map
ottMap *sync.Map
startTime time.Time
provisionerIDIndex *sync.Map
encryptedKeyIndex *sync.Map
provisionerKeySetIndex *sync.Map
sortedProvisioners provisionerSlice
audiences []string
// Do not re-initialize
initOnce bool
}
// New creates and initiates a new Authority type.
func New(config *Config) (*Authority, error) {
err := config.Validate()
if err != nil {
return nil, err
}
// Get sorted provisioners
var sorted provisionerSlice
if config.AuthorityConfig != nil {
sorted, err = newSortedProvisioners(config.AuthorityConfig.Provisioners)
if err != nil {
return nil, err
}
}
// Define audiences: legacy + possible urls without the ports.
// The CA might have proxies in front so we cannot rely on the port.
audiences := []string{legacyAuthority}
for _, name := range config.DNSNames {
audiences = append(audiences, fmt.Sprintf("https://%s/sign", name), fmt.Sprintf("https://%s/1.0/sign", name))
}
var a = &Authority{
config: config,
certificates: new(sync.Map),
ottMap: new(sync.Map),
provisionerIDIndex: new(sync.Map),
encryptedKeyIndex: new(sync.Map),
provisionerKeySetIndex: new(sync.Map),
sortedProvisioners: sorted,
audiences: audiences,
}
if err := a.init(); err != nil {
return nil, err
}
return a, nil
}
// init performs validation and initializes the fields of an Authority struct.
func (a *Authority) init() error {
// Check if handler has already been validated/initialized.
if a.initOnce {
return nil
}
var err error
// First load the root using our modified pem/x509 package.
a.rootX509Crt, err = pemutil.ReadCertificate(a.config.Root)
if err != nil {
return err
}
// Add root certificate to the certificate map
sum := sha256.Sum256(a.rootX509Crt.Raw)
a.certificates.Store(hex.EncodeToString(sum[:]), a.rootX509Crt)
// Decrypt and load intermediate public / private key pair.
if len(a.config.Password) > 0 {
a.intermediateIdentity, err = x509util.LoadIdentityFromDisk(
a.config.IntermediateCert,
a.config.IntermediateKey,
pemutil.WithPassword([]byte(a.config.Password)),
)
if err != nil {
return err
}
} else {
a.intermediateIdentity, err = x509util.LoadIdentityFromDisk(a.config.IntermediateCert, a.config.IntermediateKey)
if err != nil {
return err
}
}
for _, p := range a.config.AuthorityConfig.Provisioners {
a.provisionerIDIndex.Store(p.ID(), p)
if len(p.EncryptedKey) != 0 {
a.encryptedKeyIndex.Store(p.Key.KeyID, p.EncryptedKey)
}
}
a.startTime = time.Now()
// Set flag indicating that initialization has been completed, and should
// not be repeated.
a.initOnce = true
return nil
}