This commit is contained in:
Taylor Bantle
2023-03-21 13:58:53 -07:00
parent a50029d765
commit 77a413f977
8 changed files with 90 additions and 46 deletions
+11 -5
View File
@@ -102,7 +102,10 @@ func clone(ctx context.Context, apr *argparser.ArgParseResults, dEnv *env.DoltEn
return verr
}
dEnv.UserPassConfig = getUserAndPassConfig(apr)
dEnv.UserPassConfig, verr = getUserAndPassConfig(apr)
if verr != nil {
return verr
}
userDirExists, _ := dEnv.FS.Exists(dir)
@@ -233,13 +236,16 @@ func validateAndParseDolthubUrl(urlStr string) (string, bool) {
return "", false
}
func getUserAndPassConfig(apr *argparser.ArgParseResults) *creds.DoltCredsForPass {
func getUserAndPassConfig(apr *argparser.ArgParseResults) (*creds.DoltCredsForPass, errhand.VerboseError) {
if !apr.Contains(cli.UserParam) {
return nil
return nil, nil
}
pass, found := os.LookupEnv("DOLT_REMOTE_PASSWORD")
if !found {
return nil, errhand.BuildDError("error: must set DOLT_REMOTE_PASSWORD environment variable to use --user param").Build()
}
pass := os.Getenv("DOLT_REMOTE_PASSWORD")
return &creds.DoltCredsForPass{
Username: apr.GetValueOrDefault(cli.UserParam, ""),
Password: pass,
}
}, nil
}
+2 -1
View File
@@ -238,7 +238,8 @@ func Serve(
ReadOnly: true,
HttpListenAddr: listenaddr,
GrpcListenAddr: listenaddr,
}, &remotesrv.UserAuth{User: serverConfig.User(), Password: serverConfig.Password()})
})
args = sqle.WithUserPasswordAuth(args, remotesrv.UserAuth{User: serverConfig.User(), Password: serverConfig.Password()})
args.TLSConfig = serverConf.TLSConfig
remoteSrv, err = remotesrv.NewServer(args)
if err != nil {
+31 -25
View File
@@ -60,11 +60,6 @@ type DoltCreds struct {
KeyID []byte
}
type DoltCredsForPass struct {
Username string
Password string
}
func PubKeyStrToKIDStr(pub string) (string, error) {
data, err := B32CredsEncoding.DecodeString(pub)
@@ -126,20 +121,15 @@ func (dc DoltCreds) Sign(data []byte) []byte {
}
type RPCCreds struct {
PrivKey ed25519.PrivateKey
KeyID string
Audience string
Issuer string
Subject string
RequireTLS bool
UserPassContents string
PrivKey ed25519.PrivateKey
KeyID string
Audience string
Issuer string
Subject string
RequireTLS bool
}
func (c *RPCCreds) toBearerToken() (string, error) {
if len(c.UserPassContents) > 0 {
return "", fmt.Errorf("cannot create bearer token with user/pass credentials")
}
key := jose.SigningKey{Algorithm: jose.EdDSA, Key: c.PrivKey}
opts := &jose.SignerOptions{ExtraHeaders: map[jose.HeaderKey]interface{}{
JWTKIDHeader: c.KeyID,
@@ -163,11 +153,6 @@ func (c *RPCCreds) toBearerToken() (string, error) {
}
func (c *RPCCreds) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
if len(c.UserPassContents) > 0 {
return map[string]string{
"authorization": "Basic " + c.UserPassContents,
}, nil
}
t, err := c.toBearerToken()
if err != nil {
return nil, err
@@ -195,13 +180,34 @@ func (dc DoltCreds) RPCCreds(audience string) *RPCCreds {
}
}
func (dcp DoltCredsForPass) ToBase64Str() string {
return base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", dcp.Username, dcp.Password)))
type DoltCredsForPass struct {
Username string
Password string
}
func (dc DoltCredsForPass) RPCCreds() *RPCCreds {
return &RPCCreds{
type RPCCredsForPass struct {
RequireTLS bool
UserPassContents string
}
func (dcp DoltCredsForPass) ToBase64Str() string {
bStr := []byte(fmt.Sprintf("%s:%s", dcp.Username, dcp.Password))
return base64.StdEncoding.EncodeToString(bStr)
}
func (dc DoltCredsForPass) RPCCreds() *RPCCredsForPass {
return &RPCCredsForPass{
RequireTLS: false,
UserPassContents: dc.ToBase64Str(),
}
}
func (c *RPCCredsForPass) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
"authorization": "Basic " + c.UserPassContents,
}, nil
}
func (c *RPCCredsForPass) RequireTransportSecurity() bool {
return c.RequireTLS
}
@@ -33,12 +33,12 @@ type UserAuth struct {
Password string
}
type serverinterceptor struct {
type ServerInterceptor struct {
Lgr *logrus.Entry
ExpectedUserAuth UserAuth
}
func (si *serverinterceptor) Stream() grpc.StreamServerInterceptor {
func (si *ServerInterceptor) Stream() grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
if err := si.authenticate(ss.Context()); err != nil {
return err
@@ -48,7 +48,7 @@ func (si *serverinterceptor) Stream() grpc.StreamServerInterceptor {
}
}
func (si *serverinterceptor) Unary() grpc.UnaryServerInterceptor {
func (si *ServerInterceptor) Unary() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if err := si.authenticate(ctx); err != nil {
return nil, err
@@ -58,14 +58,14 @@ func (si *serverinterceptor) Unary() grpc.UnaryServerInterceptor {
}
}
func (si *serverinterceptor) Options() []grpc.ServerOption {
func (si *ServerInterceptor) Options() []grpc.ServerOption {
return []grpc.ServerOption{
grpc.ChainUnaryInterceptor(si.Unary()),
grpc.ChainStreamInterceptor(si.Stream()),
}
}
func (si *serverinterceptor) authenticate(ctx context.Context) error {
func (si *ServerInterceptor) authenticate(ctx context.Context) error {
if len(si.ExpectedUserAuth.User) == 0 && len(si.ExpectedUserAuth.Password) == 0 {
return nil
}
@@ -86,7 +86,8 @@ func (si *serverinterceptor) authenticate(ctx context.Context) error {
si.Lgr.Infof("incoming request authorization header failed to decode: %v", err)
return status.Error(codes.Unauthenticated, "unauthenticated")
}
compare := subtle.ConstantTimeCompare(uDec, []byte(fmt.Sprintf("%s:%s", si.ExpectedUserAuth.User, si.ExpectedUserAuth.Password)))
uExp := []byte(fmt.Sprintf("%s:%s", si.ExpectedUserAuth.User, si.ExpectedUserAuth.Password))
compare := subtle.ConstantTimeCompare(uDec, uExp)
if compare == 0 {
si.Lgr.Infof("incoming request authorization header failed to match")
+1 -2
View File
@@ -61,8 +61,7 @@ type ServerArgs struct {
ReadOnly bool
Options []grpc.ServerOption
HttpInterceptor func(http.Handler) http.Handler
ServerInterceptor serverinterceptor
HttpInterceptor func(http.Handler) http.Handler
// If supplied, the listener(s) returned from Listeners() will be TLS
// listeners. The scheme used in the URLs returned from the gRPC server
@@ -465,7 +465,7 @@ func (c *Controller) RemoteSrvServerArgs(ctx *sql.Context, args remotesrv.Server
args.HttpListenAddr = listenaddr
args.GrpcListenAddr = listenaddr
args.Options = c.ServerOptions()
args = sqle.RemoteSrvServerArgs(ctx, args, nil)
args = sqle.RemoteSrvServerArgs(ctx, args)
args.DBCache = remotesrvStoreCache{args.DBCache, c}
keyID := creds.PubKeyToKID(c.pub)
+10 -6
View File
@@ -60,14 +60,18 @@ func (s remotesrvStore) Get(path, nbfVerStr string) (remotesrv.RemoteSrvStore, e
return rss, nil
}
func RemoteSrvServerArgs(ctx *sql.Context, args remotesrv.ServerArgs, userAuth *remotesrv.UserAuth) remotesrv.ServerArgs {
func RemoteSrvServerArgs(ctx *sql.Context, args remotesrv.ServerArgs) remotesrv.ServerArgs {
sess := dsess.DSessFromSess(ctx.Session)
args.FS = sess.Provider().FileSystem()
args.DBCache = remotesrvStore{ctx, args.ReadOnly}
if userAuth != nil {
args.ServerInterceptor.Lgr = args.Logger
args.ServerInterceptor.ExpectedUserAuth = *userAuth
args.Options = append(args.Options, args.ServerInterceptor.Options()...)
}
return args
}
func WithUserPasswordAuth(args remotesrv.ServerArgs, userAuth remotesrv.UserAuth) remotesrv.ServerArgs {
si := remotesrv.ServerInterceptor{
Lgr: args.Logger,
ExpectedUserAuth: userAuth,
}
args.Options = append(args.Options, si.Options()...)
return args
}
@@ -212,3 +212,30 @@ SQL
[[ "$status" != 0 ]] || false
[[ "$output" =~ "Unauthenticated" ]] || false
}
@test "sql-server-remotesrv: dolt clone with incorrect authentication errors" {
mkdir remote
cd remote
dolt init
dolt sql --privilege-file=privs.json -q "CREATE USER user0 IDENTIFIED BY 'pass0'"
dolt sql -q 'create table vals (i int);'
dolt sql -q 'insert into vals (i) values (1), (2), (3), (4), (5);'
dolt add vals
dolt commit -m 'initial vals.'
export DOLT_REMOTE_USER="user0"
export PASSWORD="pass0"
dolt sql-server -u $DOLT_REMOTE_USER -p $PASSWORD --remotesapi-port 50051 &
srv_pid=$!
cd ../
run dolt clone http://localhost:50051/remote repo1 -u $DOLT_REMOTE_USER
[[ "$status" != 0 ]] || false
[[ "$output" =~ "must set DOLT_REMOTE_PASSWORD environment variable" ]] || false
export DOLT_REMOTE_PASSWORD="wrong-password"
run dolt clone http://localhost:50051/remote repo1 -u $DOLT_REMOTE_USER
[[ "$status" != 0 ]] || false
[[ "$output" =~ "Unauthenticated" ]] || false
}