github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/internal/enforcer/applicationproxy/tcp/verifier/verifier.go (about)

     1  package verifier
     2  
     3  import (
     4  	"crypto/x509"
     5  	"encoding/asn1"
     6  	"encoding/json"
     7  	"fmt"
     8  	"sync"
     9  
    10  	"go.aporeto.io/enforcerd/trireme-lib/collector"
    11  	"go.aporeto.io/enforcerd/trireme-lib/policy"
    12  )
    13  
    14  // aporetoASNTagsExtension holds the value of the Aporeto Tags Extension
    15  var aporetoASNTagsExtension asn1.ObjectIdentifier
    16  
    17  // aporetoPingExtension holds the value of the Aporeto Ping Extension
    18  var aporetoPingExtension asn1.ObjectIdentifier
    19  
    20  // PolicyReporter is the interface to allow looking up policies and report stats
    21  type PolicyReporter interface {
    22  	IDLookup(remoteContoller, remotePUID string, tags *policy.TagStore) bool
    23  	IPLookup() bool
    24  	Policy(tags *policy.TagStore) (*policy.FlowPolicy, *policy.FlowPolicy)
    25  	ReportStats(remoteType collector.EndPointType, remoteController string, remotePUID string, mode string, report *policy.FlowPolicy, packet *policy.FlowPolicy, accept bool)
    26  }
    27  
    28  // Verifier interface defines the methods a verifier must implement
    29  type Verifier interface {
    30  
    31  	// TrustCA replaces the trusted CA list.
    32  	TrustCAs(caPool *x509.CertPool)
    33  
    34  	// VerifyPeerCertificate verifies if this TLS connection should be admitted.
    35  	VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate, policy PolicyReporter, mustHaveClientIDCert bool) error
    36  }
    37  
    38  // verifier implements the Verifier interface
    39  type verifier struct {
    40  	sync.RWMutex
    41  	// trustedCAs stores the list of certs to be trusted
    42  	trustedCAPool *x509.CertPool
    43  }
    44  
    45  func init() {
    46  	aporetoASNTagsExtension = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 50798, 1, 1}
    47  	aporetoPingExtension = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 50798, 1, 4}
    48  }
    49  
    50  // New returns a new instance of Verifier
    51  func New(caPool *x509.CertPool) Verifier {
    52  	return &verifier{
    53  		trustedCAPool: caPool,
    54  	}
    55  }
    56  
    57  // certHasDNSOrIPSAN checks if a given name exists in a SAN for the certificate.
    58  func certHasDNSOrIPSAN(san string, cert *x509.Certificate) bool {
    59  
    60  	// san found in SAN in certs
    61  	for _, name := range cert.DNSNames {
    62  		if san == name {
    63  			return true
    64  		}
    65  	}
    66  
    67  	for _, ip := range cert.IPAddresses {
    68  		if san == ip.String() {
    69  			return true
    70  		}
    71  	}
    72  
    73  	return false
    74  }
    75  
    76  // TrustCA replaces the trusted CA list.
    77  func (v *verifier) TrustCAs(caPool *x509.CertPool) {
    78  
    79  	// Update verifier
    80  	v.Lock()
    81  	v.trustedCAPool = caPool
    82  	v.Unlock()
    83  }
    84  
    85  // VerifyPeerCertificate validates that policies allow mTLS between two enforcers based on
    86  // aporeto-tags. If no aporeto tags are found, it applies IP based ACLs.
    87  //
    88  func (v *verifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate, pr PolicyReporter, mustHaveClientIDCert bool) error {
    89  
    90  	v.RLock()
    91  	opts := x509.VerifyOptions{
    92  		Roots: v.trustedCAPool,
    93  		KeyUsages: []x509.ExtKeyUsage{
    94  			x509.ExtKeyUsageServerAuth,
    95  			x509.ExtKeyUsageClientAuth,
    96  		},
    97  	}
    98  	v.RUnlock()
    99  
   100  	// Is this an Aporeto Cert we are trusting
   101  	if opts.Roots != nil {
   102  		for _, certChain := range verifiedChains {
   103  			tags := []string{}
   104  			ping := false
   105  			for _, cert := range certChain {
   106  				for _, e := range cert.Extensions {
   107  					if e.Id.Equal(aporetoPingExtension) {
   108  						ping = true
   109  						continue
   110  					}
   111  
   112  					// If there is an Aporeto extension, get the value
   113  					if e.Id.Equal(aporetoASNTagsExtension) {
   114  						if err := json.Unmarshal(e.Value, &tags); err == nil {
   115  							if ping {
   116  								break
   117  							}
   118  						}
   119  					}
   120  				}
   121  
   122  				// No Aporeto tags
   123  				if len(tags) == 0 {
   124  					continue
   125  				}
   126  
   127  				rtags := policy.NewTagStoreFromSlice(tags)
   128  
   129  				// check if we have remote controller
   130  				rcontroller, ok := rtags.Get(policy.TagKeyController)
   131  				if !ok {
   132  					continue
   133  				}
   134  
   135  				// check if $identity == processingunit
   136  				if pu, ok := rtags.Get(policy.TagKeyIdentity); !ok && pu != policy.TagValueProcessingUnit {
   137  					continue
   138  				}
   139  
   140  				// check if we have remote puid
   141  				rpuid, ok := rtags.Get(policy.TagKeyID)
   142  				if !ok {
   143  					continue
   144  				}
   145  
   146  				if _, err := cert.Verify(opts); err != nil {
   147  					continue
   148  				}
   149  
   150  				// TODO: Check controller against verified CA
   151  				// fmt.Println(strings.Join(tags, " "))
   152  				// if !certHasDNSOrIPSAN(controller, cert) {
   153  				// 	fmt.Println("No IP or DNS SAN", strings.Join(cert.DNSNames, " "))
   154  				// 	continue
   155  				// }
   156  
   157  				// If ping is enabled in the certificate, we defer the policy lookup and the server
   158  				// application will never receive any packets related to ping irrespective of policy.
   159  				if ping {
   160  					return nil
   161  				}
   162  
   163  				if !pr.IDLookup(rcontroller, rpuid, rtags) {
   164  					return fmt.Errorf("ID policy lookup rejection")
   165  				}
   166  
   167  				return nil
   168  			}
   169  		}
   170  	}
   171  
   172  	if mustHaveClientIDCert {
   173  		return fmt.Errorf("ID lookup not performed")
   174  	}
   175  
   176  	if !pr.IPLookup() {
   177  		return fmt.Errorf("IP policy lookup rejection")
   178  	}
   179  
   180  	return nil
   181  }