istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/security/mock.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 security 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "net/http" 22 "sync" 23 24 "go.uber.org/atomic" 25 "google.golang.org/grpc/credentials" 26 "google.golang.org/grpc/peer" 27 28 "istio.io/istio/pkg/log" 29 "istio.io/istio/pkg/spiffe" 30 "istio.io/istio/pkg/util/sets" 31 "istio.io/istio/security/pkg/pki/util" 32 ) 33 34 type DirectSecretManager struct { 35 items map[string]*SecretItem 36 mu sync.RWMutex 37 } 38 39 var _ SecretManager = &DirectSecretManager{} 40 41 func NewDirectSecretManager() *DirectSecretManager { 42 return &DirectSecretManager{ 43 items: map[string]*SecretItem{}, 44 } 45 } 46 47 func (d *DirectSecretManager) GenerateSecret(resourceName string) (*SecretItem, error) { 48 d.mu.RLock() 49 defer d.mu.RUnlock() 50 si, f := d.items[resourceName] 51 if !f { 52 return nil, fmt.Errorf("resource %v not found", resourceName) 53 } 54 return si, nil 55 } 56 57 func (d *DirectSecretManager) Set(resourceName string, secret *SecretItem) { 58 d.mu.Lock() 59 defer d.mu.Unlock() 60 if secret == nil { 61 delete(d.items, resourceName) 62 } else { 63 d.items[resourceName] = secret 64 } 65 } 66 67 type FakeAuthenticator struct { 68 AllowedToken string 69 AllowedCert string 70 Name string 71 72 Successes *atomic.Int32 73 Failures *atomic.Int32 74 75 mu sync.Mutex 76 } 77 78 func NewFakeAuthenticator(name string) *FakeAuthenticator { 79 return &FakeAuthenticator{ 80 Name: name, 81 Successes: atomic.NewInt32(0), 82 Failures: atomic.NewInt32(0), 83 } 84 } 85 86 func (f *FakeAuthenticator) Authenticate(authCtx AuthContext) (*Caller, error) { 87 if authCtx.GrpcContext != nil { 88 return f.authenticateGrpc(authCtx.GrpcContext) 89 } 90 if authCtx.Request != nil { 91 return f.authenticateHTTP(authCtx.Request) 92 } 93 return nil, nil 94 } 95 96 func (f *FakeAuthenticator) authenticateHTTP(req *http.Request) (*Caller, error) { 97 return nil, errors.New("not implemented") 98 } 99 100 func (f *FakeAuthenticator) authenticateGrpc(ctx context.Context) (*Caller, error) { 101 f.mu.Lock() 102 at := f.AllowedToken 103 ac := f.AllowedCert 104 f.mu.Unlock() 105 token := checkToken(ctx, at) 106 cert := checkCert(ctx, ac) 107 id := []string{spiffe.Identity{ 108 TrustDomain: "cluster.local", 109 Namespace: "fake-namespace", 110 ServiceAccount: "fake-sa", 111 }.String()} 112 log.WithLabels("name", f.Name, "cert", cert, "token", token).Infof("authentication complete") 113 if cert == nil { 114 f.Successes.Inc() 115 return &Caller{ 116 AuthSource: AuthSourceClientCertificate, 117 Identities: id, 118 }, nil 119 } 120 if token == nil { 121 f.Successes.Inc() 122 return &Caller{ 123 AuthSource: AuthSourceIDToken, 124 Identities: id, 125 }, nil 126 } 127 f.Failures.Inc() 128 return nil, fmt.Errorf("neither token (%v) nor cert (%v) succeeded", token, cert) 129 } 130 131 func (f *FakeAuthenticator) AuthenticatorType() string { 132 return "fake" 133 } 134 135 func (f *FakeAuthenticator) Set(token string, identity string) *FakeAuthenticator { 136 f.mu.Lock() 137 defer f.mu.Unlock() 138 f.AllowedToken = token 139 f.AllowedCert = identity 140 return f 141 } 142 143 var _ Authenticator = &FakeAuthenticator{} 144 145 func checkToken(ctx context.Context, expected string) error { 146 if expected == "" { 147 return fmt.Errorf("jwt authentication not allowed") 148 } 149 targetJWT, err := ExtractBearerToken(ctx) 150 if err != nil { 151 return fmt.Errorf("target JWT extraction error: %v", err) 152 } 153 if targetJWT != expected { 154 return fmt.Errorf("expected token %q got %q", expected, targetJWT) 155 } 156 return nil 157 } 158 159 func checkCert(ctx context.Context, expected string) error { 160 if expected == "" { 161 return fmt.Errorf("cert authentication not allowed") 162 } 163 164 p, ok := peer.FromContext(ctx) 165 if !ok || p.AuthInfo == nil { 166 return fmt.Errorf("no client certificate is presented") 167 } 168 169 if authType := p.AuthInfo.AuthType(); authType != "tls" { 170 return fmt.Errorf("unsupported auth type: %q", authType) 171 } 172 173 tlsInfo := p.AuthInfo.(credentials.TLSInfo) 174 chains := tlsInfo.State.VerifiedChains 175 if len(chains) == 0 || len(chains[0]) == 0 { 176 return fmt.Errorf("no verified chain is found") 177 } 178 179 ids, err := util.ExtractIDs(chains[0][0].Extensions) 180 if err != nil { 181 return fmt.Errorf("failed to extract IDs") 182 } 183 if !sets.New(ids...).Contains(expected) { 184 return fmt.Errorf("expected identity %q, got %v", expected, ids) 185 } 186 187 return nil 188 }