mirror of https://github.com/docker/docs.git
145 lines
4.5 KiB
Go
145 lines
4.5 KiB
Go
package trustmanager
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
const certExtension string = "crt"
|
|
|
|
// ErrNoCertificatesFound is returned when no certificates are found for a
|
|
// GetCertificatesBy*
|
|
type ErrNoCertificatesFound struct {
|
|
query string
|
|
}
|
|
|
|
// ErrNoCertificatesFound is returned when no certificates are found for a
|
|
// GetCertificatesBy*
|
|
func (err ErrNoCertificatesFound) Error() string {
|
|
return fmt.Sprintf("error, no certificates found in the keystore match: %s", err.query)
|
|
}
|
|
|
|
// ErrCertValidation is returned when a certificate doesn't pass the store specific
|
|
// validations
|
|
type ErrCertValidation struct {
|
|
}
|
|
|
|
// ErrCertValidation is returned when a certificate doesn't pass the store specific
|
|
// validations
|
|
func (err ErrCertValidation) Error() string {
|
|
return fmt.Sprintf("store-specific certificate validations failed")
|
|
}
|
|
|
|
// ErrCertExists is returned when a Certificate already exists in the key store
|
|
type ErrCertExists struct {
|
|
}
|
|
|
|
// ErrCertExists is returned when a Certificate already exists in the key store
|
|
func (err ErrCertExists) Error() string {
|
|
return fmt.Sprintf("certificate already in the store")
|
|
}
|
|
|
|
// ErrBadCertificateStore is returned when there is an internal inconsistency
|
|
// in our x509 store
|
|
type ErrBadCertificateStore struct {
|
|
}
|
|
|
|
// ErrBadCertificateStore is returned when there is an internal inconsistency
|
|
// in our x509 store
|
|
func (err ErrBadCertificateStore) Error() string {
|
|
return fmt.Sprintf("inconsistent certificate store")
|
|
}
|
|
|
|
// X509Store is the interface for all X509Stores
|
|
type X509Store interface {
|
|
AddCert(cert *x509.Certificate) error
|
|
AddCertFromPEM(pemCerts []byte) error
|
|
AddCertFromFile(filename string) error
|
|
RemoveCert(cert *x509.Certificate) error
|
|
RemoveAll() error
|
|
GetCertificateByCertID(certID string) (*x509.Certificate, error)
|
|
GetCertificatesByCN(cn string) ([]*x509.Certificate, error)
|
|
GetCertificates() []*x509.Certificate
|
|
GetCertificatePool() *x509.CertPool
|
|
GetVerifyOptions(dnsName string) (x509.VerifyOptions, error)
|
|
}
|
|
|
|
// CertID represent the ID used to identify certificates
|
|
type CertID string
|
|
|
|
// Validator is a convenience type to create validating function that filters
|
|
// certificates that get added to the store
|
|
type Validator interface {
|
|
Validate(cert *x509.Certificate) bool
|
|
}
|
|
|
|
// ValidatorFunc is a convenience type to create functions that implement
|
|
// the Validator interface
|
|
type ValidatorFunc func(cert *x509.Certificate) bool
|
|
|
|
// Validate implements the Validator interface to allow for any func() bool method
|
|
// to be passed as a Validator
|
|
func (vf ValidatorFunc) Validate(cert *x509.Certificate) bool {
|
|
return vf(cert)
|
|
}
|
|
|
|
// Verify operates on an X509Store and validates the existence of a chain of trust
|
|
// between a leafCertificate and a CA present inside of the X509 Store.
|
|
// It requires at least two certificates in certList, a leaf Certificate and an
|
|
// intermediate CA certificate.
|
|
func Verify(s X509Store, dnsName string, certList []*x509.Certificate) error {
|
|
// If we have no Certificates loaded return error (we don't want to revert to using
|
|
// system CAs).
|
|
if len(s.GetCertificates()) == 0 {
|
|
return errors.New("no root CAs available")
|
|
}
|
|
|
|
// At a minimum we should be provided a leaf cert and an intermediate.
|
|
if len(certList) < 2 {
|
|
return errors.New("certificate and at least one intermediate needed")
|
|
}
|
|
|
|
// Get the VerifyOptions from the keystore for a base dnsName
|
|
opts, err := s.GetVerifyOptions(dnsName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create a Certificate Pool for our intermediate certificates
|
|
intPool := x509.NewCertPool()
|
|
var leafCert *x509.Certificate
|
|
|
|
// Iterate through all the certificates
|
|
for _, c := range certList {
|
|
// If the cert is a CA, we add it to the intermediates pool. If not, we call
|
|
// it the leaf cert
|
|
if c.IsCA {
|
|
intPool.AddCert(c)
|
|
continue
|
|
}
|
|
// Certificate is not a CA, it must be our leaf certificate.
|
|
// If we already found one, bail with error
|
|
if leafCert != nil {
|
|
return errors.New("more than one leaf certificate found")
|
|
}
|
|
leafCert = c
|
|
}
|
|
|
|
// We exited the loop with no leaf certificates
|
|
if leafCert == nil {
|
|
return errors.New("no leaf certificates found")
|
|
}
|
|
|
|
// We have one leaf certificate and at least one intermediate. Lets add this
|
|
// Cert Pool as the Intermediates list on our VerifyOptions
|
|
opts.Intermediates = intPool
|
|
|
|
// Finally, let's call Verify on our leafCert with our fully configured options
|
|
chains, err := leafCert.Verify(opts)
|
|
if len(chains) == 0 || err != nil {
|
|
return fmt.Errorf("certificate verification failed: %v", err)
|
|
}
|
|
return nil
|
|
}
|