vitess.io/vitess@v0.16.2/go/vt/servenv/grpc_server_auth_static.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package servenv 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "os" 24 25 "github.com/spf13/pflag" 26 "google.golang.org/grpc/codes" 27 "google.golang.org/grpc/metadata" 28 "google.golang.org/grpc/status" 29 30 "vitess.io/vitess/go/vt/log" 31 ) 32 33 var ( 34 credsFile string 35 // StaticAuthPlugin implements AuthPlugin interface 36 _ Authenticator = (*StaticAuthPlugin)(nil) 37 ) 38 39 // The datatype for static auth Context keys 40 type staticAuthKey int 41 42 const ( 43 // Internal Context key for the authenticated username 44 staticAuthUsername staticAuthKey = 0 45 ) 46 47 func registerGRPCServerAuthStaticFlags(fs *pflag.FlagSet) { 48 fs.StringVar(&credsFile, "grpc_auth_static_password_file", credsFile, "JSON File to read the users/passwords from.") 49 } 50 51 // StaticAuthConfigEntry is the container for server side credentials. Current implementation matches the 52 // the one from the client but this will change in the future as we hooked this pluging into ACL 53 // features. 54 type StaticAuthConfigEntry struct { 55 Username string 56 Password string 57 // TODO (@rafael) Add authorization parameters 58 } 59 60 // StaticAuthPlugin implements static username/password authentication for grpc. It contains an array of username/passwords 61 // that will be authorized to connect to the grpc server. 62 type StaticAuthPlugin struct { 63 entries []StaticAuthConfigEntry 64 } 65 66 // Authenticate implements AuthPlugin interface. This method will be used inside a middleware in grpc_server to authenticate 67 // incoming requests. 68 func (sa *StaticAuthPlugin) Authenticate(ctx context.Context, fullMethod string) (context.Context, error) { 69 if md, ok := metadata.FromIncomingContext(ctx); ok { 70 if len(md["username"]) == 0 || len(md["password"]) == 0 { 71 return nil, status.Errorf(codes.Unauthenticated, "username and password must be provided") 72 } 73 username := md["username"][0] 74 password := md["password"][0] 75 for _, authEntry := range sa.entries { 76 if username == authEntry.Username && password == authEntry.Password { 77 return newStaticAuthContext(ctx, username), nil 78 } 79 } 80 return nil, status.Errorf(codes.PermissionDenied, "auth failure: caller %q provided invalid credentials", username) 81 } 82 return nil, status.Errorf(codes.Unauthenticated, "username and password must be provided") 83 } 84 85 // StaticAuthUsernameFromContext returns the username authenticated by the static auth plugin and stored in the Context, if any 86 func StaticAuthUsernameFromContext(ctx context.Context) string { 87 username, ok := ctx.Value(staticAuthUsername).(string) 88 if ok { 89 return username 90 } 91 return "" 92 } 93 94 func newStaticAuthContext(ctx context.Context, username string) context.Context { 95 return context.WithValue(ctx, staticAuthUsername, username) 96 } 97 98 func staticAuthPluginInitializer() (Authenticator, error) { 99 entries := make([]StaticAuthConfigEntry, 0) 100 if credsFile == "" { 101 err := fmt.Errorf("failed to load static auth plugin. Plugin configured but grpc_auth_static_password_file not provided") 102 return nil, err 103 } 104 105 data, err := os.ReadFile(credsFile) 106 if err != nil { 107 err := fmt.Errorf("failed to load static auth plugin %v", err) 108 return nil, err 109 } 110 111 err = json.Unmarshal(data, &entries) 112 if err != nil { 113 err := fmt.Errorf("fail to load static auth plugin: %v", err) 114 return nil, err 115 } 116 staticAuthPlugin := &StaticAuthPlugin{ 117 entries: entries, 118 } 119 log.Info("static auth plugin have initialized successfully with config from grpc_auth_static_password_file") 120 return staticAuthPlugin, nil 121 } 122 123 func init() { 124 RegisterAuthPlugin("static", staticAuthPluginInitializer) 125 grpcAuthServerFlagHooks = append(grpcAuthServerFlagHooks, registerGRPCServerAuthStaticFlags) 126 }