github.com/blend/go-sdk@v1.20220411.3/envoyutil/identity_processor.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package envoyutil 9 10 import ( 11 "fmt" 12 "strings" 13 14 "github.com/blend/go-sdk/collections" 15 "github.com/blend/go-sdk/ex" 16 "github.com/blend/go-sdk/spiffeutil" 17 ) 18 19 // NOTE: Ensure that 20 // - `IdentityProcessor.KubernetesIdentityFormatter` satisfies `IdentityFormatter` 21 // - `IdentityProcessor.IdentityProvider` satisfies `IdentityProvider` 22 var ( 23 _ IdentityFormatter = IdentityProcessor{}.KubernetesIdentityFormatter 24 _ IdentityProvider = IdentityProcessor{}.IdentityProvider 25 ) 26 27 // IdentityProcessorOption mutates an identity processor. 28 type IdentityProcessorOption func(*IdentityProcessor) 29 30 // OptIdentityType sets the identity type for the processor. 31 func OptIdentityType(it IdentityType) IdentityProcessorOption { 32 return func(ip *IdentityProcessor) { 33 ip.Type = it 34 } 35 } 36 37 // OptAllowedTrustDomains adds allowed trust domains to the processor. 38 func OptAllowedTrustDomains(trustDomains ...string) IdentityProcessorOption { 39 return func(ip *IdentityProcessor) { 40 ip.AllowedTrustDomains = append(ip.AllowedTrustDomains, trustDomains...) 41 } 42 } 43 44 // OptDeniedTrustDomains adds denied trust domains to the processor. 45 func OptDeniedTrustDomains(trustDomains ...string) IdentityProcessorOption { 46 return func(ip *IdentityProcessor) { 47 ip.DeniedTrustDomains = append(ip.DeniedTrustDomains, trustDomains...) 48 } 49 } 50 51 // OptAllowedIdentities adds allowed identities to the processor. 52 func OptAllowedIdentities(identities ...string) IdentityProcessorOption { 53 return func(ip *IdentityProcessor) { 54 ip.AllowedIdentities = ip.AllowedIdentities.Union( 55 collections.NewSetOfString(identities...), 56 ) 57 } 58 } 59 60 // OptDeniedIdentities adds denied identities to the processor. 61 func OptDeniedIdentities(identities ...string) IdentityProcessorOption { 62 return func(ip *IdentityProcessor) { 63 ip.DeniedIdentities = ip.DeniedIdentities.Union( 64 collections.NewSetOfString(identities...), 65 ) 66 } 67 } 68 69 // OptFormatIdentity sets the `FormatIdentity` on the processor. 70 func OptFormatIdentity(formatter IdentityFormatter) IdentityProcessorOption { 71 return func(ip *IdentityProcessor) { 72 ip.FormatIdentity = formatter 73 } 74 } 75 76 // IdentityFormatter describes functions that will produce an identity string 77 // from a parsed SPIFFE URI. 78 type IdentityFormatter = func(XFCCElement, *spiffeutil.ParsedURI) (string, error) 79 80 // IdentityType represents the type of identity that will be extracted by an 81 // `IdentityProcessor`. It can either be a client or server identity. 82 type IdentityType int 83 84 const ( 85 // ClientIdentity represents client identity. 86 ClientIdentity IdentityType = 0 87 // ServerIdentity represents server identity. 88 ServerIdentity IdentityType = 1 89 ) 90 91 // IdentityProcessor provides configurable fields that can be used to 92 // help validate a parsed SPIFFE URI and produce and validate an identity from 93 // a parsed SPIFFE URI. The `Type` field determines if a client or server 94 // identity should be provided; by default the type will be client identity. 95 type IdentityProcessor struct { 96 Type IdentityType 97 AllowedTrustDomains []string 98 DeniedTrustDomains []string 99 AllowedIdentities collections.SetOfString 100 DeniedIdentities collections.SetOfString 101 FormatIdentity IdentityFormatter 102 } 103 104 // IdentityProvider returns a client or server identity; it uses the configured 105 // rules to validate and format the identity by parsing the `URI` field (for 106 // client identity) or `By` field (for server identity) of the XFCC element. If 107 // `FormatIdentity` has not been specified, the `KubernetesIdentityFormatter()` 108 // method will be used as a fallback. 109 // 110 // This method satisfies the `IdentityProvider` interface. 111 func (ip IdentityProcessor) IdentityProvider(xfcc XFCCElement) (string, error) { 112 uriValue := ip.getURIForIdentity(xfcc) 113 114 if uriValue == "" { 115 return "", &XFCCValidationError{ 116 Class: ip.errInvalidIdentity(), 117 XFCC: xfcc.String(), 118 } 119 } 120 121 pu, err := spiffeutil.Parse(uriValue) 122 // NOTE: The `pu == nil` check is redundant, we expect `spiffeutil.Parse()` 123 // not to violate the invariant that `pu != nil` when `err == nil`. 124 if err != nil || pu == nil { 125 return "", &XFCCExtractionError{ 126 Class: ip.errInvalidIdentity(), 127 XFCC: xfcc.String(), 128 } 129 } 130 131 if err := ip.ProcessAllowedTrustDomains(xfcc, pu); err != nil { 132 return "", err 133 } 134 if err := ip.ProcessDeniedTrustDomains(xfcc, pu); err != nil { 135 return "", err 136 } 137 138 identity, err := ip.formatIdentity(xfcc, pu) 139 if err != nil { 140 return "", err 141 } 142 143 if err := ip.ProcessAllowedIdentities(xfcc, identity); err != nil { 144 return "", err 145 } 146 if err := ip.ProcessDeniedIdentities(xfcc, identity); err != nil { 147 return "", err 148 } 149 return identity, nil 150 } 151 152 // KubernetesIdentityFormatter assumes the SPIFFE URI contains a Kubernetes 153 // workload ID of the form `ns/{namespace}/sa/{serviceAccount}` and formats the 154 // identity as `{serviceAccount}.{namespace}`. This function satisfies the 155 // `IdentityFormatter` interface. 156 func (ip IdentityProcessor) KubernetesIdentityFormatter(xfcc XFCCElement, pu *spiffeutil.ParsedURI) (string, error) { 157 kw, err := spiffeutil.ParseKubernetesWorkloadID(pu.WorkloadID) 158 if err != nil { 159 return "", &XFCCExtractionError{ 160 Class: ip.errInvalidIdentity(), 161 XFCC: xfcc.String(), 162 } 163 } 164 return fmt.Sprintf("%s.%s", kw.ServiceAccount, kw.Namespace), nil 165 } 166 167 // ProcessAllowedTrustDomains returns an error if an allow list is configured 168 // and the trust domain from the parsed SPIFFE URI does not match any elements 169 // in the list. 170 func (ip IdentityProcessor) ProcessAllowedTrustDomains(xfcc XFCCElement, pu *spiffeutil.ParsedURI) error { 171 if len(ip.AllowedTrustDomains) == 0 { 172 return nil 173 } 174 175 for _, allowed := range ip.AllowedTrustDomains { 176 if strings.EqualFold(pu.TrustDomain, allowed) { 177 return nil 178 } 179 } 180 return &XFCCValidationError{ 181 Class: ip.errInvalidIdentity(), 182 XFCC: xfcc.String(), 183 Metadata: map[string]string{ 184 "trustDomain": pu.TrustDomain, 185 }, 186 } 187 } 188 189 // ProcessDeniedTrustDomains returns an error if a denied list is configured 190 // and the trust domain from the parsed SPIFFE URI matches any elements in the 191 // list. 192 func (ip IdentityProcessor) ProcessDeniedTrustDomains(xfcc XFCCElement, pu *spiffeutil.ParsedURI) error { 193 for _, denied := range ip.DeniedTrustDomains { 194 if strings.EqualFold(pu.TrustDomain, denied) { 195 return &XFCCValidationError{ 196 Class: ip.errInvalidIdentity(), 197 XFCC: xfcc.String(), 198 Metadata: map[string]string{ 199 "trustDomain": pu.TrustDomain, 200 }, 201 } 202 } 203 } 204 205 return nil 206 } 207 208 // ProcessAllowedIdentities returns an error if an allow list is configured 209 // and the identity does not match any elements in the list. 210 func (ip IdentityProcessor) ProcessAllowedIdentities(xfcc XFCCElement, identity string) error { 211 if ip.AllowedIdentities.Len() == 0 { 212 return nil 213 } 214 215 if ip.AllowedIdentities.Contains(identity) { 216 return nil 217 } 218 219 return &XFCCValidationError{ 220 Class: ip.errDeniedIdentity(), 221 XFCC: xfcc.String(), 222 Metadata: map[string]string{ 223 ip.getIdentityKey(): identity, 224 }, 225 } 226 } 227 228 // ProcessDeniedIdentities returns an error if a denied list is configured 229 // and the identity matches any elements in the list. 230 func (ip IdentityProcessor) ProcessDeniedIdentities(xfcc XFCCElement, identity string) error { 231 if ip.DeniedIdentities.Len() == 0 { 232 return nil 233 } 234 235 if ip.DeniedIdentities.Contains(identity) { 236 return &XFCCValidationError{ 237 Class: ip.errDeniedIdentity(), 238 XFCC: xfcc.String(), 239 Metadata: map[string]string{ 240 ip.getIdentityKey(): identity, 241 }, 242 } 243 } 244 245 return nil 246 } 247 248 // formatIdentity invokes the `FormatIdentity` on the current processor 249 // or falls back to `KubernetesIdentityFormatter()` if it is not set. 250 func (ip IdentityProcessor) formatIdentity(xfcc XFCCElement, pu *spiffeutil.ParsedURI) (string, error) { 251 if ip.FormatIdentity != nil { 252 return ip.FormatIdentity(xfcc, pu) 253 } 254 return ip.KubernetesIdentityFormatter(xfcc, pu) 255 } 256 257 // getURIForIdentity returns either the `URI` field if this processor has `Type` 258 // "client identity" or the `By` field for the server identity. 259 func (ip IdentityProcessor) getURIForIdentity(xfcc XFCCElement) string { 260 if ip.Type == ClientIdentity { 261 return xfcc.URI 262 } 263 264 return xfcc.By 265 } 266 267 // getIdentityKey returns a key to be used in error metadata indicating if a 268 // value is client identity or server identity. 269 func (ip IdentityProcessor) getIdentityKey() string { 270 if ip.Type == ClientIdentity { 271 return "clientIdentity" 272 } 273 274 return "serverIdentity" 275 } 276 277 // errInvalidIdentity maps the `Type` to a specific error class indicating 278 // an invalid identity. 279 func (ip IdentityProcessor) errInvalidIdentity() ex.Class { 280 if ip.Type == ClientIdentity { 281 return ErrInvalidClientIdentity 282 } 283 284 return ErrInvalidServerIdentity 285 } 286 287 // errDeniedIdentity maps the `Type` to a specific error class indicating 288 // a denied identity. 289 func (ip IdentityProcessor) errDeniedIdentity() ex.Class { 290 if ip.Type == ClientIdentity { 291 return ErrDeniedClientIdentity 292 } 293 294 return ErrDeniedServerIdentity 295 }