From 2b609a18cf52d620fd56149193d45e0ee6b34171 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Tue, 19 Sep 2023 04:36:40 +0000 Subject: [PATCH] Support for other client authentication modes were added --- gnmi_server/gnoi.go | 15 +++++++------- gnmi_server/pamAuth.go | 41 +++++++++++++++++++------------------- gnoi_client/gnoi_client.go | 7 ++++++- patches/gnmi_cli.all.patch | 27 ++++++++++++++++++++++++- telemetry/telemetry.go | 1 + 5 files changed, 60 insertions(+), 31 deletions(-) diff --git a/gnmi_server/gnoi.go b/gnmi_server/gnoi.go index 8bd96536..760de7a4 100644 --- a/gnmi_server/gnoi.go +++ b/gnmi_server/gnoi.go @@ -15,7 +15,6 @@ import ( "github.com/sonic-net/sonic-gnmi/common_utils" "google.golang.org/grpc/status" "google.golang.org/grpc/codes" - "os/user" "encoding/json" jwt "github.com/dgrijalva/jwt-go" ) @@ -129,19 +128,18 @@ func (srv *Server) Authenticate(ctx context.Context, req *spb_jwt.AuthenticateRe log.V(1).Info("gNOI: Sonic Authenticate") - if !srv.config.UserAuth.Enabled("jwt") { + if srv.config.UserAuth.Enabled("jwt") { + log.V(1).Info("gNOI: Sonic Authenticate - JWT enabled") + } else { + log.V(1).Info("gNOI: Sonic Authenticate - JWT not enabled" return nil, status.Errorf(codes.Unimplemented, "") } auth_success, _ := UserPwAuth(req.Username, req.Password) if auth_success { - usr, err := user.Lookup(req.Username) + roles, err := GetUserRoles(req.Username) if err == nil { - roles, err := GetUserRoles(usr) - if err == nil { - return &spb_jwt.AuthenticateResponse{Token: tokenResp(req.Username, roles)}, nil - } + return &spb_jwt.AuthenticateResponse{Token: tokenResp(req.Username, roles)}, nil } - } return nil, status.Errorf(codes.PermissionDenied, "Invalid Username or Password") @@ -159,6 +157,7 @@ func (srv *Server) Refresh(ctx context.Context, req *spb_jwt.RefreshRequest) (*s token, _, err := JwtAuthenAndAuthor(ctx) if err != nil { + log.Errorf("gNOI: Sonic Refresh - JWTAuthenandAuthor returned error") return nil, err } diff --git a/gnmi_server/pamAuth.go b/gnmi_server/pamAuth.go index 73ce1ed5..96a1b5d7 100644 --- a/gnmi_server/pamAuth.go +++ b/gnmi_server/pamAuth.go @@ -1,12 +1,13 @@ package gnmi import ( - "github.com/sonic-net/sonic-gnmi/common_utils" + "github.com/Azure/sonic-telemetry/common_utils" "errors" + "strings" "github.com/golang/glog" "github.com/msteinert/pam" "golang.org/x/crypto/ssh" - "os/user" + "github.com/Azure/sonic-mgmt-common/translib/transformer" ) type UserCredential struct { @@ -46,32 +47,30 @@ func PAMAuthUser(u string, p string) error { err := cred.PAMAuthenticate() return err } -func GetUserRoles(usr *user.User) ([]string, error) { - // Lookup Roles - gids, err := usr.GroupIds() - if err != nil { - return nil, err - } - roles := make([]string, len(gids)) - for idx, gid := range gids { - group, err := user.LookupGroupId(gid) - if err != nil { - return nil, err +func GetUserRoles(username string) ([]string, error) { + // Lookup Roles using Dbus + var cmd string + cmd = "user_auth_mgmt.retrieve_user_roles" + host_output := transformer.HostQuery(cmd, username) + if host_output.Err != nil { + glog.Errorf("System user roles host query failed") + return nil,errors.New("Failed to retrieve user roles") + } else { + if val, _ := host_output.Body[0].(int32); val == 0 { + glog.Infof("Roles retrieved from host") + roles := strings.Split(host_output.Body[1].(string), ",") + return roles,nil + } else { + glog.Errorf("Invalid User. no roles") + return nil,errors.New(host_output.Body[1].(string)) } - roles[idx] = group.Name } - return roles, nil } func PopulateAuthStruct(username string, auth *common_utils.AuthInfo, r []string) error { if len(r) == 0 { AuthLock.Lock() defer AuthLock.Unlock() - usr, err := user.Lookup(username) - if err != nil { - return err - } - - roles, err := GetUserRoles(usr) + roles,err := GetUserRoles(username) if err != nil { return err } diff --git a/gnoi_client/gnoi_client.go b/gnoi_client/gnoi_client.go index 31ea33da..e33d89b4 100644 --- a/gnoi_client/gnoi_client.go +++ b/gnoi_client/gnoi_client.go @@ -23,6 +23,9 @@ var ( jwtToken = flag.String("jwt_token", "", "JWT Token if required") targetName = flag.String("target_name", "hostname.com", "The target name use to verify the hostname returned by TLS handshake") ) +var username string = "" +var password string = "" + func setUserCreds(ctx context.Context) context.Context { if len(*jwtToken) > 0 { ctx = metadata.AppendToOutgoingContext(ctx, "access_token", *jwtToken) @@ -31,6 +34,8 @@ func setUserCreds(ctx context.Context) context.Context { } func main() { flag.Parse() + username = flag.Lookup("username").Value.String() + password = flag.Lookup("password").Value.String() opts := credentials.ClientCredentials(*targetName) ctx, cancel := context.WithCancel(context.Background()) @@ -254,7 +259,7 @@ func imageDefault(sc spb.SonicServiceClient, ctx context.Context) { func authenticate(sc spb_jwt.SonicJwtServiceClient, ctx context.Context) { fmt.Println("Sonic Authenticate") ctx = setUserCreds(ctx) - req := &spb_jwt.AuthenticateRequest {} + req := &spb_jwt.AuthenticateRequest {Username: username, Password: password} json.Unmarshal([]byte(*args), req) diff --git a/patches/gnmi_cli.all.patch b/patches/gnmi_cli.all.patch index bc5b5b66..2b986810 100644 --- a/patches/gnmi_cli.all.patch +++ b/patches/gnmi_cli.all.patch @@ -11,6 +11,15 @@ } --- ./github.com/openconfig/gnmi/cmd/gnmi_cli/gnmi_cli.go 2019-11-22 14:03:29.839103602 -0800 +++ ./github.com/openconfig/gnmi/cmd/gnmi_cli/gnmi_cli.go 2019-11-21 09:30:52.453893674 -0800 +@@ -61,7 +61,7 @@ + + clientTypes = flags.NewStringList(&cfg.ClientTypes, []string{gclient.Type}) + queryFlag = &flags.StringList{} +- queryType = flag.String("query_type", client.Once.String(), "Type of result, one of: (o, once, p, polling, s, streaming).") ++ queryType = flag.String("query_type", "", "Type of result, one of: (p, polling, s, streaming).") + queryAddr = flags.NewStringList(&q.Addrs, nil) + + reqProto = flag.String("proto", "", "Text proto for gNMI request.") @@ -76,6 +76,11 @@ caCert = flag.String("ca_crt", "", "CA certificate file. Used to verify server TLS certificate.") clientCert = flag.String("client_crt", "", "Client certificate file. Used for client certificate-based authentication.") @@ -23,7 +32,23 @@ ) func init() { -@@ -278,6 +283,22 @@ +@@ -83,7 +88,7 @@ func init() { + flag.Var(queryFlag, "query", "Comma separated list of queries. Each query is a delimited list of OpenConfig path nodes which may also be specified as a glob (*). The delimeter can be specified with the --delimiter flag.") + // Query command-line flags. + flag.Var(queryAddr, "address", "Address of the GNMI target to query.") +- flag.BoolVar(&q.UpdatesOnly, "updates_only", false, "Only stream updates, not the initial sync. Setting this flag for once or polling queries will cause nothing to be returned.") ++ flag.BoolVar(&q.UpdatesOnly, "updates_only", false, "Only stream updates, not the initial sync. Setting this flag for polling queries will cause nothing to be returned.") + // Config command-line flags. + flag.DurationVar(&cfg.PollingInterval, "polling_interval", 30*time.Second, "Interval at which to poll in seconds if polling is specified for query_type.") + flag.UintVar(&cfg.Count, "count", 0, "Number of polling/streaming events (0 is infinite).") +@@ -272,12 +277,28 @@ func executeSubscribe(ctx context.Context) error { + } + + if q.Type = cli.QueryType(*queryType); q.Type == client.Unknown { +- return errors.New("--query_type must be one of: (o, once, p, polling, s, streaming)") ++ return errors.New("--query_type must be one of: (p, polling, s, streaming)") + } + // Parse queryFlag into appropriate format. if len(*queryFlag) == 0 { return errors.New("--query must be set") } diff --git a/telemetry/telemetry.go b/telemetry/telemetry.go index 6cc128fe..4a84f31b 100644 --- a/telemetry/telemetry.go +++ b/telemetry/telemetry.go @@ -80,6 +80,7 @@ func main() { cfg := &gnmi.Config{} cfg.Port = int64(*port) + cfg.UserAuth = userAuth cfg.EnableTranslibWrite = bool(*gnmi_translib_write) cfg.EnableNativeWrite = bool(*gnmi_native_write) cfg.LogLevel = 3