2018-11-21 21:29:18 +00:00
|
|
|
package ca
|
|
|
|
|
|
|
|
import (
|
2019-01-08 01:48:56 +00:00
|
|
|
"crypto"
|
2018-11-21 21:29:18 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
2019-01-08 01:48:56 +00:00
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/smallstep/certificates/api"
|
2018-11-21 21:29:18 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// TLSOption defines the type of a function that modifies a tls.Config.
|
2019-01-09 21:20:28 +00:00
|
|
|
type TLSOption func(ctx *TLSOptionCtx) error
|
2018-11-21 21:29:18 +00:00
|
|
|
|
2019-01-09 21:20:28 +00:00
|
|
|
// TLSOptionCtx is the context modified on TLSOption methods.
|
|
|
|
type TLSOptionCtx struct {
|
|
|
|
Client *Client
|
|
|
|
Transport http.RoundTripper
|
|
|
|
Config *tls.Config
|
|
|
|
OnRenewFunc []TLSOption
|
|
|
|
}
|
|
|
|
|
|
|
|
// newTLSOptionCtx creates the TLSOption context.
|
|
|
|
func newTLSOptionCtx(c *Client, sign *api.SignResponse, pk crypto.PrivateKey, config *tls.Config) (*TLSOptionCtx, error) {
|
2019-01-08 01:48:56 +00:00
|
|
|
tr, err := getTLSOptionsTransport(sign, pk)
|
|
|
|
if err != nil {
|
2019-01-09 21:20:28 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &TLSOptionCtx{
|
|
|
|
Client: c,
|
|
|
|
Transport: tr,
|
|
|
|
Config: config,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *TLSOptionCtx) apply(options []TLSOption) error {
|
|
|
|
for _, fn := range options {
|
|
|
|
if err := fn(ctx); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-08 01:48:56 +00:00
|
|
|
}
|
2019-01-09 21:20:28 +00:00
|
|
|
return nil
|
|
|
|
}
|
2019-01-08 01:48:56 +00:00
|
|
|
|
2019-01-09 21:20:28 +00:00
|
|
|
func (ctx *TLSOptionCtx) applyRenew(tr http.RoundTripper) error {
|
|
|
|
ctx.Transport = tr
|
|
|
|
for _, fn := range ctx.OnRenewFunc {
|
|
|
|
if err := fn(ctx); err != nil {
|
2018-11-21 21:29:18 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-01-08 01:48:56 +00:00
|
|
|
// getTLSOptionsTransport is the transport used by TLSOptions. It is used to get
|
|
|
|
// root certificates using a mTLS connection with the CA.
|
|
|
|
func getTLSOptionsTransport(sign *api.SignResponse, pk crypto.PrivateKey) (http.RoundTripper, error) {
|
|
|
|
cert, err := TLSCertificate(sign, pk)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build default transport with fixed certificate
|
|
|
|
tlsConfig := getDefaultTLSConfig(sign)
|
|
|
|
tlsConfig.Certificates = []tls.Certificate{*cert}
|
|
|
|
tlsConfig.PreferServerCipherSuites = true
|
|
|
|
// Build RootCAs with given root certificate
|
|
|
|
if pool := getCertPool(sign); pool != nil {
|
|
|
|
tlsConfig.RootCAs = pool
|
|
|
|
}
|
|
|
|
|
|
|
|
return getDefaultTransport(tlsConfig)
|
|
|
|
}
|
|
|
|
|
2018-11-21 21:29:18 +00:00
|
|
|
// RequireAndVerifyClientCert is a tls.Config option used on servers to enforce
|
|
|
|
// a valid TLS client certificate. This is the default option for mTLS servers.
|
|
|
|
func RequireAndVerifyClientCert() TLSOption {
|
2019-01-09 21:20:28 +00:00
|
|
|
return func(ctx *TLSOptionCtx) error {
|
|
|
|
ctx.Config.ClientAuth = tls.RequireAndVerifyClientCert
|
2018-11-21 21:29:18 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// VerifyClientCertIfGiven is a tls.Config option used on on servers to validate
|
|
|
|
// a TLS client certificate if it is provided. It does not requires a certificate.
|
|
|
|
func VerifyClientCertIfGiven() TLSOption {
|
2019-01-09 21:20:28 +00:00
|
|
|
return func(ctx *TLSOptionCtx) error {
|
|
|
|
ctx.Config.ClientAuth = tls.VerifyClientCertIfGiven
|
2018-11-21 21:29:18 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddRootCA adds to the tls.Config RootCAs the given certificate. RootCAs
|
|
|
|
// defines the set of root certificate authorities that clients use when
|
|
|
|
// verifying server certificates.
|
|
|
|
func AddRootCA(cert *x509.Certificate) TLSOption {
|
2019-01-09 21:20:28 +00:00
|
|
|
return func(ctx *TLSOptionCtx) error {
|
|
|
|
if ctx.Config.RootCAs == nil {
|
|
|
|
ctx.Config.RootCAs = x509.NewCertPool()
|
2018-11-21 21:29:18 +00:00
|
|
|
}
|
2019-01-09 21:20:28 +00:00
|
|
|
ctx.Config.RootCAs.AddCert(cert)
|
2018-11-21 21:29:18 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddClientCA adds to the tls.Config ClientCAs the given certificate. ClientCAs
|
|
|
|
// defines the set of root certificate authorities that servers use if required
|
|
|
|
// to verify a client certificate by the policy in ClientAuth.
|
|
|
|
func AddClientCA(cert *x509.Certificate) TLSOption {
|
2019-01-09 21:20:28 +00:00
|
|
|
return func(ctx *TLSOptionCtx) error {
|
|
|
|
if ctx.Config.ClientCAs == nil {
|
|
|
|
ctx.Config.ClientCAs = x509.NewCertPool()
|
2019-01-05 01:51:32 +00:00
|
|
|
}
|
2019-01-09 21:20:28 +00:00
|
|
|
ctx.Config.ClientCAs.AddCert(cert)
|
2019-01-05 01:51:32 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-08 01:48:56 +00:00
|
|
|
// AddRootsToRootCAs does a roots request and adds to the tls.Config RootCAs all
|
|
|
|
// the certificates in the response. RootCAs defines the set of root certificate
|
|
|
|
// authorities that clients use when verifying server certificates.
|
2019-01-08 02:55:40 +00:00
|
|
|
//
|
|
|
|
// BootstrapServer and BootstrapClient methods include this option by default.
|
2019-01-08 01:48:56 +00:00
|
|
|
func AddRootsToRootCAs() TLSOption {
|
2019-01-09 21:20:28 +00:00
|
|
|
fn := func(ctx *TLSOptionCtx) error {
|
|
|
|
certs, err := ctx.Client.Roots(ctx.Transport)
|
2019-01-08 01:48:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-09 21:20:28 +00:00
|
|
|
if ctx.Config.RootCAs == nil {
|
|
|
|
ctx.Config.RootCAs = x509.NewCertPool()
|
2019-01-05 01:51:32 +00:00
|
|
|
}
|
2019-01-08 01:48:56 +00:00
|
|
|
for _, cert := range certs.Certificates {
|
2019-01-09 21:20:28 +00:00
|
|
|
ctx.Config.RootCAs.AddCert(cert.Certificate)
|
2019-01-08 01:48:56 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2019-01-09 21:20:28 +00:00
|
|
|
return func(ctx *TLSOptionCtx) error {
|
|
|
|
ctx.OnRenewFunc = append(ctx.OnRenewFunc, fn)
|
|
|
|
return fn(ctx)
|
|
|
|
}
|
2019-01-08 01:48:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddRootsToClientCAs does a roots request and adds to the tls.Config ClientCAs
|
|
|
|
// all the certificates in the response. ClientCAs defines the set of root
|
|
|
|
// certificate authorities that servers use if required to verify a client
|
|
|
|
// certificate by the policy in ClientAuth.
|
2019-01-08 02:55:40 +00:00
|
|
|
//
|
|
|
|
// BootstrapServer method includes this option by default.
|
2019-01-08 01:48:56 +00:00
|
|
|
func AddRootsToClientCAs() TLSOption {
|
2019-01-09 21:20:28 +00:00
|
|
|
fn := func(ctx *TLSOptionCtx) error {
|
|
|
|
certs, err := ctx.Client.Roots(ctx.Transport)
|
2019-01-08 01:48:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-09 21:20:28 +00:00
|
|
|
if ctx.Config.ClientCAs == nil {
|
|
|
|
ctx.Config.ClientCAs = x509.NewCertPool()
|
2019-01-08 01:48:56 +00:00
|
|
|
}
|
|
|
|
for _, cert := range certs.Certificates {
|
2019-01-09 21:20:28 +00:00
|
|
|
ctx.Config.ClientCAs.AddCert(cert.Certificate)
|
2019-01-08 01:48:56 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2019-01-09 21:20:28 +00:00
|
|
|
return func(ctx *TLSOptionCtx) error {
|
|
|
|
ctx.OnRenewFunc = append(ctx.OnRenewFunc, fn)
|
|
|
|
return fn(ctx)
|
|
|
|
}
|
2019-01-08 01:48:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddFederationToRootCAs does a federation request and adds to the tls.Config
|
|
|
|
// RootCAs all the certificates in the response. RootCAs defines the set of root
|
|
|
|
// certificate authorities that clients use when verifying server certificates.
|
|
|
|
func AddFederationToRootCAs() TLSOption {
|
2019-01-09 21:20:28 +00:00
|
|
|
fn := func(ctx *TLSOptionCtx) error {
|
|
|
|
certs, err := ctx.Client.Federation(ctx.Transport)
|
2019-01-05 01:51:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-09 21:20:28 +00:00
|
|
|
if ctx.Config.RootCAs == nil {
|
|
|
|
ctx.Config.RootCAs = x509.NewCertPool()
|
2019-01-08 01:48:56 +00:00
|
|
|
}
|
2019-01-05 01:51:32 +00:00
|
|
|
for _, cert := range certs.Certificates {
|
2019-01-09 21:20:28 +00:00
|
|
|
ctx.Config.RootCAs.AddCert(cert.Certificate)
|
2019-01-05 01:51:32 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2019-01-09 21:20:28 +00:00
|
|
|
return func(ctx *TLSOptionCtx) error {
|
|
|
|
ctx.OnRenewFunc = append(ctx.OnRenewFunc, fn)
|
|
|
|
return fn(ctx)
|
|
|
|
}
|
2019-01-05 01:51:32 +00:00
|
|
|
}
|
|
|
|
|
2019-01-08 01:48:56 +00:00
|
|
|
// AddFederationToClientCAs does a federation request and adds to the tls.Config
|
2019-01-05 01:51:32 +00:00
|
|
|
// ClientCAs all the certificates in the response. ClientCAs defines the set of
|
|
|
|
// root certificate authorities that servers use if required to verify a client
|
|
|
|
// certificate by the policy in ClientAuth.
|
2019-01-08 01:48:56 +00:00
|
|
|
func AddFederationToClientCAs() TLSOption {
|
2019-01-09 21:20:28 +00:00
|
|
|
fn := func(ctx *TLSOptionCtx) error {
|
|
|
|
certs, err := ctx.Client.Federation(ctx.Transport)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ctx.Config.ClientCAs == nil {
|
|
|
|
ctx.Config.ClientCAs = x509.NewCertPool()
|
|
|
|
}
|
|
|
|
for _, cert := range certs.Certificates {
|
|
|
|
ctx.Config.ClientCAs.AddCert(cert.Certificate)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return func(ctx *TLSOptionCtx) error {
|
|
|
|
ctx.OnRenewFunc = append(ctx.OnRenewFunc, fn)
|
|
|
|
return fn(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddRootsToCAs does a roots request and adds the resulting certs to the
|
|
|
|
// tls.Config RootCAs and ClientCAs. Combines the functionality of
|
|
|
|
// AddRootsToRootCAs and AddRootsToClientCAs.
|
|
|
|
func AddRootsToCAs() TLSOption {
|
|
|
|
fn := func(ctx *TLSOptionCtx) error {
|
|
|
|
certs, err := ctx.Client.Roots(ctx.Transport)
|
2019-01-05 01:51:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-09 21:20:28 +00:00
|
|
|
if ctx.Config.ClientCAs == nil {
|
|
|
|
ctx.Config.ClientCAs = x509.NewCertPool()
|
|
|
|
}
|
|
|
|
if ctx.Config.RootCAs == nil {
|
|
|
|
ctx.Config.RootCAs = x509.NewCertPool()
|
2019-01-08 01:48:56 +00:00
|
|
|
}
|
2019-01-05 01:51:32 +00:00
|
|
|
for _, cert := range certs.Certificates {
|
2019-01-09 21:20:28 +00:00
|
|
|
ctx.Config.ClientCAs.AddCert(cert.Certificate)
|
|
|
|
ctx.Config.RootCAs.AddCert(cert.Certificate)
|
2018-11-21 21:29:18 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2019-01-09 21:20:28 +00:00
|
|
|
return func(ctx *TLSOptionCtx) error {
|
|
|
|
ctx.OnRenewFunc = append(ctx.OnRenewFunc, fn)
|
|
|
|
return fn(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddFederationToCAs does a federation request and adds the resulting certs to the
|
|
|
|
// tls.Config RootCAs and ClientCAs. Combines the functionality of
|
|
|
|
// AddFederationToRootCAs and AddFederationToClientCAs.
|
|
|
|
func AddFederationToCAs() TLSOption {
|
|
|
|
fn := func(ctx *TLSOptionCtx) error {
|
|
|
|
certs, err := ctx.Client.Federation(ctx.Transport)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ctx.Config.ClientCAs == nil {
|
|
|
|
ctx.Config.ClientCAs = x509.NewCertPool()
|
|
|
|
}
|
|
|
|
if ctx.Config.RootCAs == nil {
|
|
|
|
ctx.Config.RootCAs = x509.NewCertPool()
|
|
|
|
}
|
|
|
|
for _, cert := range certs.Certificates {
|
|
|
|
ctx.Config.ClientCAs.AddCert(cert.Certificate)
|
|
|
|
ctx.Config.RootCAs.AddCert(cert.Certificate)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return func(ctx *TLSOptionCtx) error {
|
|
|
|
ctx.OnRenewFunc = append(ctx.OnRenewFunc, fn)
|
|
|
|
return fn(ctx)
|
|
|
|
}
|
2018-11-21 21:29:18 +00:00
|
|
|
}
|