istio.io/istio@v0.0.0-20240520182934-d79c90f27776/security/pkg/server/ca/authenticate/cert_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 "context" 19 "fmt" 20 "net/http" 21 22 "google.golang.org/grpc/credentials" 23 "google.golang.org/grpc/peer" 24 25 "istio.io/istio/pkg/security" 26 "istio.io/istio/security/pkg/pki/util" 27 ) 28 29 const ( 30 ClientCertAuthenticatorType = "ClientCertAuthenticator" 31 ) 32 33 // ClientCertAuthenticator extracts identities from client certificate. 34 type ClientCertAuthenticator struct{} 35 36 var _ security.Authenticator = &ClientCertAuthenticator{} 37 38 func (cca *ClientCertAuthenticator) AuthenticatorType() string { 39 return ClientCertAuthenticatorType 40 } 41 42 // Authenticate extracts identities from presented client certificates. This 43 // method assumes that certificate chain has been properly validated before 44 // this method is called. In other words, this method does not do certificate 45 // chain validation itself. 46 func (cca *ClientCertAuthenticator) Authenticate(authCtx security.AuthContext) (*security.Caller, error) { 47 if authCtx.GrpcContext != nil { 48 return cca.authenticateGrpc(authCtx.GrpcContext) 49 } 50 if authCtx.Request != nil { 51 return cca.authenticateHTTP(authCtx.Request) 52 } 53 return nil, nil 54 } 55 56 func (cca *ClientCertAuthenticator) authenticateGrpc(ctx context.Context) (*security.Caller, error) { 57 peer, ok := peer.FromContext(ctx) 58 if !ok || peer.AuthInfo == nil { 59 return nil, fmt.Errorf("no client certificate is presented") 60 } 61 62 if authType := peer.AuthInfo.AuthType(); authType != "tls" { 63 return nil, fmt.Errorf("unsupported auth type: %q", authType) 64 } 65 66 tlsInfo := peer.AuthInfo.(credentials.TLSInfo) 67 chains := tlsInfo.State.VerifiedChains 68 if len(chains) == 0 || len(chains[0]) == 0 { 69 return nil, fmt.Errorf("no verified chain is found") 70 } 71 72 ids, err := util.ExtractIDs(chains[0][0].Extensions) 73 if err != nil { 74 return nil, err 75 } 76 77 return &security.Caller{ 78 AuthSource: security.AuthSourceClientCertificate, 79 Identities: ids, 80 }, nil 81 } 82 83 // authenticateHTTP performs mTLS authentication for http requests. Requires having the endpoints on a listener 84 // with proper TLS configuration. 85 func (cca *ClientCertAuthenticator) authenticateHTTP(req *http.Request) (*security.Caller, error) { 86 if req.TLS == nil || req.TLS.VerifiedChains == nil { 87 return nil, fmt.Errorf("no client certificate is presented") 88 } 89 90 chains := req.TLS.VerifiedChains 91 if len(chains) == 0 || len(chains[0]) == 0 { 92 return nil, fmt.Errorf("no verified chain is found") 93 } 94 95 ids, err := util.ExtractIDs(chains[0][0].Extensions) 96 if err != nil { 97 return nil, err 98 } 99 100 return &security.Caller{ 101 AuthSource: security.AuthSourceClientCertificate, 102 Identities: ids, 103 }, nil 104 }