github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/pkg/pkiverifier/pkiverifier.go (about) 1 package pkiverifier 2 3 import ( 4 "crypto/ecdsa" 5 "crypto/elliptic" 6 "crypto/x509" 7 "errors" 8 "math/big" 9 "time" 10 11 jwt "github.com/dgrijalva/jwt-go" 12 "go.aporeto.io/enforcerd/trireme-lib/utils/cache" 13 "go.uber.org/zap" 14 ) 15 16 const ( 17 // defaultValidity is the default cache validity in seconds 18 defaultValidity = 1 19 ) 20 21 // PKITokenIssuer is the interface of an object that can issue a PKI token. 22 type PKITokenIssuer interface { 23 CreateTokenFromCertificate(*x509.Certificate, []string) ([]byte, error) 24 } 25 26 // PKITokenVerifier is the interface of an object that can verify a PKI token. 27 type PKITokenVerifier interface { 28 Verify([]byte) (*DatapathKey, error) 29 } 30 31 type verifierClaims struct { 32 X *big.Int 33 Y *big.Int 34 Tags []string `json:"tags,omitempty"` 35 jwt.StandardClaims 36 } 37 38 // PKIControllerInfo holds the controller information about public keys 39 type PKIControllerInfo struct { 40 Namespace string // The namespace of the public key. 41 Controller string // The controller or control plane of the public key. 42 SameController bool // Does the public key come from the same controller 43 } 44 45 // PKIPublicKey holds information about public keys 46 type PKIPublicKey struct { 47 PublicKey *ecdsa.PublicKey 48 Controller *PKIControllerInfo 49 } 50 51 type tokenManager struct { 52 publicKeys []*PKIPublicKey 53 privateKey *ecdsa.PrivateKey 54 signMethod jwt.SigningMethod 55 keycache cache.DataStore 56 validity time.Duration 57 } 58 59 // DatapathKey holds the data path key with the corresponding claims. 60 type DatapathKey struct { 61 PublicKey *ecdsa.PublicKey 62 Tags []string 63 Expiration time.Time 64 Controller *PKIControllerInfo 65 } 66 67 // NewPKIIssuer initializes a new signer structure 68 func NewPKIIssuer(privateKey *ecdsa.PrivateKey) PKITokenIssuer { 69 70 return &tokenManager{ 71 privateKey: privateKey, 72 signMethod: jwt.SigningMethodES256, 73 } 74 } 75 76 // NewPKIVerifier returns a new PKIConfiguration. 77 func NewPKIVerifier(publicKeys []*PKIPublicKey, cacheValidity time.Duration) PKITokenVerifier { 78 79 validity := defaultValidity * time.Second 80 if cacheValidity > 0 { 81 validity = cacheValidity 82 } 83 84 return &tokenManager{ 85 publicKeys: publicKeys, 86 signMethod: jwt.SigningMethodES256, 87 keycache: cache.NewCacheWithExpiration("PKIVerifierKey", validity), 88 validity: validity, 89 } 90 } 91 92 // Verify verifies a token and returns the public key 93 func (p *tokenManager) Verify(token []byte) (*DatapathKey, error) { 94 95 tokenString := string(token) 96 if pk, err := p.keycache.Get(tokenString); err == nil { 97 return pk.(*DatapathKey), err 98 } 99 100 claims := &verifierClaims{} 101 var t *jwt.Token 102 var err error 103 for _, pk := range p.publicKeys { 104 105 if pk == nil || pk.PublicKey == nil { 106 continue 107 } 108 109 t, err = jwt.ParseWithClaims(tokenString, claims, func(*jwt.Token) (interface{}, error) { // nolint 110 return pk.PublicKey, nil 111 }) 112 if err != nil || !t.Valid { 113 continue 114 } 115 116 expTime := time.Unix(claims.ExpiresAt, 0) 117 dp := &DatapathKey{ 118 PublicKey: &ecdsa.PublicKey{ 119 Curve: elliptic.P256(), 120 X: claims.X, 121 Y: claims.Y, 122 }, 123 Tags: claims.Tags, 124 Expiration: expTime, 125 Controller: pk.Controller, 126 } 127 128 p.keycache.AddOrUpdate(tokenString, dp) 129 130 // if the token expires before our default validity, update the timer 131 // so that we expire it no longer than its validity. 132 if time.Now().Add(p.validity).After(expTime) { 133 if err := p.keycache.SetTimeOut(tokenString, time.Until(expTime)); err != nil { 134 zap.L().Warn("Failed to update cache validity for token", zap.Error(err)) 135 } 136 } 137 138 return dp, nil 139 } 140 141 return nil, errors.New("unable to verify token against any available public key") 142 } 143 144 // CreateTokenFromCertificate creates and signs a token 145 func (p *tokenManager) CreateTokenFromCertificate(cert *x509.Certificate, tags []string) ([]byte, error) { 146 147 // Combine the application claims with the standard claims 148 claims := &verifierClaims{ 149 X: cert.PublicKey.(*ecdsa.PublicKey).X, 150 Y: cert.PublicKey.(*ecdsa.PublicKey).Y, 151 Tags: tags, 152 } 153 claims.ExpiresAt = cert.NotAfter.Unix() 154 155 // Create the token and sign with our key 156 strtoken, err := jwt.NewWithClaims(p.signMethod, claims).SignedString(p.privateKey) 157 if err != nil { 158 return []byte{}, err 159 } 160 161 return []byte(strtoken), nil 162 }