mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-10 13:28:36 +02:00
Return an error from DownstreamMTLSSettings.validate() if both CA and CAFile are populated, or if both CRL and CRLFile are populated.
178 lines
5.2 KiB
Go
178 lines
5.2 KiB
Go
package config
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/pomerium/pomerium/internal/log"
|
|
"github.com/pomerium/pomerium/pkg/cryptutil"
|
|
"github.com/pomerium/pomerium/pkg/grpc/config"
|
|
)
|
|
|
|
// MTLSEnforcement represents a client certificate enforcement behavior.
|
|
type MTLSEnforcement string
|
|
|
|
const (
|
|
// MTLSEnforcementPolicy specifies no default client certificate
|
|
// enforcement: any requirements must be explicitly specified in a policy.
|
|
MTLSEnforcementPolicy MTLSEnforcement = "policy"
|
|
|
|
// MTLSEnforcementPolicyWithDefaultDeny specifies that client certificate
|
|
// requirements will be enforced by route policy, with a default
|
|
// invalid_client_certificate deny rule added to each policy.
|
|
MTLSEnforcementPolicyWithDefaultDeny MTLSEnforcement = "policy_with_default_deny"
|
|
|
|
// MTLSEnforcementRejectConnection specifies that client certificate
|
|
// requirements will be enforced by rejecting any connection attempts
|
|
// without a trusted certificate.
|
|
MTLSEnforcementRejectConnection MTLSEnforcement = "reject_connection"
|
|
)
|
|
|
|
// DownstreamMTLSSettings specify the downstream client certificate requirements.
|
|
type DownstreamMTLSSettings struct {
|
|
// CA is the base64-encoded certificate authority (or bundle of certificate
|
|
// authorities) that should serve as the trust root(s). These will be
|
|
// advertised in the initial TLS handshake.
|
|
CA string `mapstructure:"ca" yaml:"ca"`
|
|
|
|
// CAFile is the path to a file containing the certificate authority (or
|
|
// bundle of certificate authorities) that should serve as the trust
|
|
// root(s). These will be advertised in the initial TLS handshake.
|
|
CAFile string `mapstructure:"ca_file" yaml:"ca_file"`
|
|
|
|
// CRL is the base64-encoded certificate revocation list (or bundle of
|
|
// CRLs) to use when validating client certificates.
|
|
CRL string `mapstructure:"crl" yaml:"crl,omitempty"`
|
|
|
|
// CRLFile is the path to a file containing the certificate revocation
|
|
// list (or bundle of CRLs) to use when validating client certificates.
|
|
CRLFile string `mapstructure:"crl_file" yaml:"crl_file,omitempty"`
|
|
|
|
// Enforcement indicates the behavior applied to requests without a valid
|
|
// client certificate.
|
|
Enforcement MTLSEnforcement `mapstructure:"enforcement" yaml:"enforcement,omitempty"`
|
|
|
|
// MaxVerifyDepth is the maximum allowed depth of a certificate trust chain
|
|
// (not counting the leaf certificate). The value 0 indicates no maximum.
|
|
MaxVerifyDepth *uint32 `mapstructure:"max_verify_depth" yaml:"max_verify_depth,omitempty"`
|
|
}
|
|
|
|
// GetCA returns the certificate authority (or nil if unset).
|
|
func (s *DownstreamMTLSSettings) GetCA() ([]byte, error) {
|
|
if s.CA != "" {
|
|
ca, err := base64.StdEncoding.DecodeString(s.CA)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("CA: %w", err)
|
|
}
|
|
return ca, nil
|
|
}
|
|
if s.CAFile != "" {
|
|
ca, err := os.ReadFile(s.CAFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("CA file: %w", err)
|
|
}
|
|
return ca, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// GetCRL returns the certificate revocation list bundle (or nil if unset).
|
|
func (s *DownstreamMTLSSettings) GetCRL() ([]byte, error) {
|
|
if s.CRL != "" {
|
|
crl, err := base64.StdEncoding.DecodeString(s.CRL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("CRL: %w", err)
|
|
}
|
|
return crl, nil
|
|
}
|
|
if s.CRLFile != "" {
|
|
crl, err := os.ReadFile(s.CRLFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("CRL file: %w", err)
|
|
}
|
|
return crl, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// GetEnforcement returns the enforcement behavior to apply.
|
|
func (s *DownstreamMTLSSettings) GetEnforcement() MTLSEnforcement {
|
|
if s.Enforcement == "" {
|
|
return MTLSEnforcementPolicyWithDefaultDeny
|
|
}
|
|
return s.Enforcement
|
|
}
|
|
|
|
// GetMaxVerifyDepth returns the maximum certificate chain depth. The value 0
|
|
// indicates no maximum.
|
|
func (s *DownstreamMTLSSettings) GetMaxVerifyDepth() uint32 {
|
|
if s.MaxVerifyDepth == nil {
|
|
return 1
|
|
}
|
|
return *s.MaxVerifyDepth
|
|
}
|
|
|
|
func (s *DownstreamMTLSSettings) validate() error {
|
|
if s.CA != "" && s.CAFile != "" {
|
|
return errors.New("cannot set both ca and ca_file")
|
|
} else if _, err := s.GetCA(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if s.CRL != "" && s.CRLFile != "" {
|
|
return errors.New("cannot set both crl and crl_file")
|
|
}
|
|
crl, err := s.GetCRL()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(crl) > 0 {
|
|
if _, err := cryptutil.DecodeCRL(crl); err != nil {
|
|
return fmt.Errorf("CRL: %w", err)
|
|
}
|
|
}
|
|
|
|
switch s.Enforcement {
|
|
case "",
|
|
MTLSEnforcementPolicy,
|
|
MTLSEnforcementPolicyWithDefaultDeny,
|
|
MTLSEnforcementRejectConnection: // OK
|
|
default:
|
|
return errors.New("unknown enforcement option")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *DownstreamMTLSSettings) applySettingsProto(
|
|
ctx context.Context, p *config.DownstreamMtlsSettings,
|
|
) {
|
|
if p == nil {
|
|
return
|
|
}
|
|
set(&s.CA, p.Ca)
|
|
set(&s.CRL, p.Crl)
|
|
s.Enforcement = mtlsEnforcementFromProtoEnum(ctx, p.Enforcement)
|
|
}
|
|
|
|
func mtlsEnforcementFromProtoEnum(
|
|
ctx context.Context, mode *config.MtlsEnforcementMode,
|
|
) MTLSEnforcement {
|
|
if mode == nil {
|
|
return ""
|
|
}
|
|
switch *mode {
|
|
case config.MtlsEnforcementMode_POLICY:
|
|
return MTLSEnforcementPolicy
|
|
case config.MtlsEnforcementMode_POLICY_WITH_DEFAULT_DENY:
|
|
return MTLSEnforcementPolicyWithDefaultDeny
|
|
case config.MtlsEnforcementMode_REJECT_CONNECTION:
|
|
return MTLSEnforcementRejectConnection
|
|
default:
|
|
log.Error(ctx).Msgf("unknown mTLS enforcement mode %s", mode)
|
|
return ""
|
|
}
|
|
}
|