istio.io/istio@v0.0.0-20240520182934-d79c90f27776/security/pkg/server/ca/authenticate/xfcc_authenticator.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package authenticate 16 17 import ( 18 "fmt" 19 "net" 20 "net/netip" 21 "strings" 22 23 "github.com/alecholmes/xfccparser" 24 25 "istio.io/istio/pilot/pkg/features" 26 "istio.io/istio/pkg/log" 27 "istio.io/istio/pkg/security" 28 ) 29 30 const ( 31 XfccAuthenticatorType = "XfccAuthenticator" 32 ) 33 34 // XfccAuthenticator extracts identities from Xfcc header. 35 type XfccAuthenticator struct{} 36 37 var _ security.Authenticator = &XfccAuthenticator{} 38 39 func (xff XfccAuthenticator) AuthenticatorType() string { 40 return XfccAuthenticatorType 41 } 42 43 // Authenticate extracts identities from Xfcc Header. 44 func (xff XfccAuthenticator) Authenticate(ctx security.AuthContext) (*security.Caller, error) { 45 remoteAddr := ctx.RemoteAddress() 46 xfccHeader := ctx.Header(xfccparser.ForwardedClientCertHeader) 47 if len(remoteAddr) == 0 || len(xfccHeader) == 0 { 48 return nil, fmt.Errorf("caller from %s does not have Xfcc header", remoteAddr) 49 } 50 // First check if client is trusted client so that we can "trust" the Xfcc Header. 51 if !isTrustedAddress(remoteAddr, features.TrustedGatewayCIDR) { 52 return nil, fmt.Errorf("caller from %s is not in the trusted network. XfccAuthenticator can not be used", remoteAddr) 53 } 54 55 return buildSecurityCaller(xfccHeader[0]) 56 } 57 58 func buildSecurityCaller(xfccHeader string) (*security.Caller, error) { 59 clientCerts, err := xfccparser.ParseXFCCHeader(xfccHeader) 60 if err != nil { 61 message := fmt.Sprintf("error in parsing xfcc header: %v", err) 62 return nil, fmt.Errorf(message) 63 } 64 if len(clientCerts) == 0 { 65 message := "xfcc header does not have atleast one client certs" 66 return nil, fmt.Errorf(message) 67 } 68 ids := []string{} 69 for _, cc := range clientCerts { 70 ids = append(ids, cc.URI...) 71 ids = append(ids, cc.DNS...) 72 if cc.Subject != nil { 73 ids = append(ids, cc.Subject.CommonName) 74 } 75 } 76 77 return &security.Caller{ 78 AuthSource: security.AuthSourceClientCertificate, 79 Identities: ids, 80 }, nil 81 } 82 83 func isTrustedAddress(addr string, trustedCidrs []string) bool { 84 ip, _, err := net.SplitHostPort(addr) 85 if err != nil { 86 log.Warnf("peer address %s can not be split in to proper host and port", addr) 87 return false 88 } 89 for _, cidr := range trustedCidrs { 90 if isInRange(ip, cidr) { 91 return true 92 } 93 } 94 // Always trust local host addresses. 95 return netip.MustParseAddr(ip).IsLoopback() 96 } 97 98 func isInRange(addr, cidr string) bool { 99 if strings.Contains(cidr, "/") { 100 ipp, err := netip.ParsePrefix(cidr) 101 if err != nil { 102 return false 103 } 104 105 return ipp.Contains(netip.MustParseAddr(addr)) 106 } 107 return false 108 }