mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
[security] rpc: Do not transfer access/secret key. (#4857)
This is an improvement upon existing implementation by avoiding transfer of access and secret keys over the network. This change only exchanges JWT tokens generated by an rpc client. Even if the JWT can be traced over the network on a non-TLS connection, this change makes sure that we never really expose the secret key over the network.
This commit is contained in:
parent
f680b8482f
commit
f8024cadbb
@ -33,16 +33,19 @@ func testAdminCmd(cmd cmdType, t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(rootPath)
|
defer os.RemoveAll(rootPath)
|
||||||
|
|
||||||
adminServer := adminCmd{}
|
|
||||||
creds := serverConfig.GetCredential()
|
creds := serverConfig.GetCredential()
|
||||||
|
token, err := authenticateNode(creds.AccessKey, creds.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
adminServer := adminCmd{}
|
||||||
args := LoginRPCArgs{
|
args := LoginRPCArgs{
|
||||||
Username: creds.AccessKey,
|
AuthToken: token,
|
||||||
Password: creds.SecretKey,
|
|
||||||
Version: Version,
|
Version: Version,
|
||||||
RequestTime: UTCNow(),
|
RequestTime: UTCNow(),
|
||||||
}
|
}
|
||||||
reply := LoginRPCReply{}
|
err = adminServer.Login(&args, &LoginRPCReply{})
|
||||||
err = adminServer.Login(&args, &reply)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to login to admin server - %v", err)
|
t.Fatalf("Failed to login to admin server - %v", err)
|
||||||
}
|
}
|
||||||
@ -52,7 +55,7 @@ func testAdminCmd(cmd cmdType, t *testing.T) {
|
|||||||
<-globalServiceSignalCh
|
<-globalServiceSignalCh
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ga := AuthRPCArgs{AuthToken: reply.AuthToken}
|
ga := AuthRPCArgs{AuthToken: token}
|
||||||
genReply := AuthRPCReply{}
|
genReply := AuthRPCReply{}
|
||||||
switch cmd {
|
switch cmd {
|
||||||
case restartCmd:
|
case restartCmd:
|
||||||
@ -91,21 +94,25 @@ func TestReInitDisks(t *testing.T) {
|
|||||||
// Setup admin rpc server for an XL backend.
|
// Setup admin rpc server for an XL backend.
|
||||||
globalIsXL = true
|
globalIsXL = true
|
||||||
adminServer := adminCmd{}
|
adminServer := adminCmd{}
|
||||||
|
|
||||||
creds := serverConfig.GetCredential()
|
creds := serverConfig.GetCredential()
|
||||||
|
token, err := authenticateNode(creds.AccessKey, creds.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
args := LoginRPCArgs{
|
args := LoginRPCArgs{
|
||||||
Username: creds.AccessKey,
|
AuthToken: token,
|
||||||
Password: creds.SecretKey,
|
|
||||||
Version: Version,
|
Version: Version,
|
||||||
RequestTime: UTCNow(),
|
RequestTime: UTCNow(),
|
||||||
}
|
}
|
||||||
reply := LoginRPCReply{}
|
err = adminServer.Login(&args, &LoginRPCReply{})
|
||||||
err = adminServer.Login(&args, &reply)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to login to admin server - %v", err)
|
t.Fatalf("Failed to login to admin server - %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
authArgs := AuthRPCArgs{
|
authArgs := AuthRPCArgs{
|
||||||
AuthToken: reply.AuthToken,
|
AuthToken: token,
|
||||||
}
|
}
|
||||||
authReply := AuthRPCReply{}
|
authReply := AuthRPCReply{}
|
||||||
|
|
||||||
@ -114,12 +121,15 @@ func TestReInitDisks(t *testing.T) {
|
|||||||
t.Errorf("Expected to pass, but failed with %v", err)
|
t.Errorf("Expected to pass, but failed with %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token, err = authenticateNode(creds.AccessKey, creds.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
// Negative test case with admin rpc server setup for FS.
|
// Negative test case with admin rpc server setup for FS.
|
||||||
globalIsXL = false
|
globalIsXL = false
|
||||||
fsAdminServer := adminCmd{}
|
fsAdminServer := adminCmd{}
|
||||||
fsArgs := LoginRPCArgs{
|
fsArgs := LoginRPCArgs{
|
||||||
Username: creds.AccessKey,
|
AuthToken: token,
|
||||||
Password: creds.SecretKey,
|
|
||||||
Version: Version,
|
Version: Version,
|
||||||
RequestTime: UTCNow(),
|
RequestTime: UTCNow(),
|
||||||
}
|
}
|
||||||
@ -130,7 +140,7 @@ func TestReInitDisks(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
authArgs = AuthRPCArgs{
|
authArgs = AuthRPCArgs{
|
||||||
AuthToken: fsReply.AuthToken,
|
AuthToken: token,
|
||||||
}
|
}
|
||||||
authReply = AuthRPCReply{}
|
authReply = AuthRPCReply{}
|
||||||
// Attempt ReInitDisks service on a FS backend.
|
// Attempt ReInitDisks service on a FS backend.
|
||||||
@ -154,9 +164,14 @@ func TestGetConfig(t *testing.T) {
|
|||||||
|
|
||||||
adminServer := adminCmd{}
|
adminServer := adminCmd{}
|
||||||
creds := serverConfig.GetCredential()
|
creds := serverConfig.GetCredential()
|
||||||
|
|
||||||
|
token, err := authenticateNode(creds.AccessKey, creds.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
args := LoginRPCArgs{
|
args := LoginRPCArgs{
|
||||||
Username: creds.AccessKey,
|
AuthToken: token,
|
||||||
Password: creds.SecretKey,
|
|
||||||
Version: Version,
|
Version: Version,
|
||||||
RequestTime: UTCNow(),
|
RequestTime: UTCNow(),
|
||||||
}
|
}
|
||||||
@ -167,7 +182,7 @@ func TestGetConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
authArgs := AuthRPCArgs{
|
authArgs := AuthRPCArgs{
|
||||||
AuthToken: reply.AuthToken,
|
AuthToken: token,
|
||||||
}
|
}
|
||||||
|
|
||||||
configReply := ConfigReply{}
|
configReply := ConfigReply{}
|
||||||
@ -198,9 +213,12 @@ func TestWriteAndCommitConfig(t *testing.T) {
|
|||||||
|
|
||||||
adminServer := adminCmd{}
|
adminServer := adminCmd{}
|
||||||
creds := serverConfig.GetCredential()
|
creds := serverConfig.GetCredential()
|
||||||
|
token, err := authenticateNode(creds.AccessKey, creds.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
args := LoginRPCArgs{
|
args := LoginRPCArgs{
|
||||||
Username: creds.AccessKey,
|
AuthToken: token,
|
||||||
Password: creds.SecretKey,
|
|
||||||
Version: Version,
|
Version: Version,
|
||||||
RequestTime: UTCNow(),
|
RequestTime: UTCNow(),
|
||||||
}
|
}
|
||||||
@ -215,7 +233,7 @@ func TestWriteAndCommitConfig(t *testing.T) {
|
|||||||
tmpFileName := mustGetUUID()
|
tmpFileName := mustGetUUID()
|
||||||
wArgs := WriteConfigArgs{
|
wArgs := WriteConfigArgs{
|
||||||
AuthRPCArgs: AuthRPCArgs{
|
AuthRPCArgs: AuthRPCArgs{
|
||||||
AuthToken: reply.AuthToken,
|
AuthToken: token,
|
||||||
},
|
},
|
||||||
TmpFileName: tmpFileName,
|
TmpFileName: tmpFileName,
|
||||||
Buf: buf,
|
Buf: buf,
|
||||||
@ -232,7 +250,7 @@ func TestWriteAndCommitConfig(t *testing.T) {
|
|||||||
|
|
||||||
cArgs := CommitConfigArgs{
|
cArgs := CommitConfigArgs{
|
||||||
AuthRPCArgs: AuthRPCArgs{
|
AuthRPCArgs: AuthRPCArgs{
|
||||||
AuthToken: reply.AuthToken,
|
AuthToken: token,
|
||||||
},
|
},
|
||||||
FileName: tmpFileName,
|
FileName: tmpFileName,
|
||||||
}
|
}
|
||||||
|
@ -99,21 +99,22 @@ func (authClient *AuthRPCClient) Login() (err error) {
|
|||||||
|
|
||||||
// Attempt to login if not logged in already.
|
// Attempt to login if not logged in already.
|
||||||
if authClient.authToken == "" {
|
if authClient.authToken == "" {
|
||||||
// Login to authenticate and acquire a new auth token.
|
authClient.authToken, err = authenticateNode(authClient.config.accessKey, authClient.config.secretKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Login to authenticate your token.
|
||||||
var (
|
var (
|
||||||
loginMethod = authClient.config.serviceName + loginMethodName
|
loginMethod = authClient.config.serviceName + loginMethodName
|
||||||
loginArgs = LoginRPCArgs{
|
loginArgs = LoginRPCArgs{
|
||||||
Username: authClient.config.accessKey,
|
AuthToken: authClient.authToken,
|
||||||
Password: authClient.config.secretKey,
|
|
||||||
Version: Version,
|
Version: Version,
|
||||||
RequestTime: UTCNow(),
|
RequestTime: UTCNow(),
|
||||||
}
|
}
|
||||||
loginReply = LoginRPCReply{}
|
|
||||||
)
|
)
|
||||||
if err = authClient.rpcClient.Call(loginMethod, &loginArgs, &loginReply); err != nil {
|
if err = authClient.rpcClient.Call(loginMethod, &loginArgs, &LoginRPCReply{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
authClient.authToken = loginReply.AuthToken
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,7 @@ package cmd
|
|||||||
const loginMethodName = ".Login"
|
const loginMethodName = ".Login"
|
||||||
|
|
||||||
// AuthRPCServer RPC server authenticates using JWT.
|
// AuthRPCServer RPC server authenticates using JWT.
|
||||||
type AuthRPCServer struct {
|
type AuthRPCServer struct{}
|
||||||
}
|
|
||||||
|
|
||||||
// Login - Handles JWT based RPC login.
|
// Login - Handles JWT based RPC login.
|
||||||
func (b AuthRPCServer) Login(args *LoginRPCArgs, reply *LoginRPCReply) error {
|
func (b AuthRPCServer) Login(args *LoginRPCArgs, reply *LoginRPCReply) error {
|
||||||
@ -30,14 +29,10 @@ func (b AuthRPCServer) Login(args *LoginRPCArgs, reply *LoginRPCReply) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate using JWT.
|
// Return an error if token is not valid.
|
||||||
token, err := authenticateNode(args.Username, args.Password)
|
if !isAuthTokenValid(args.AuthToken) {
|
||||||
if err != nil {
|
return errAuthentication
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the token.
|
|
||||||
reply.AuthToken = token
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,10 @@ func TestLogin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(rootPath)
|
defer os.RemoveAll(rootPath)
|
||||||
creds := serverConfig.GetCredential()
|
creds := serverConfig.GetCredential()
|
||||||
|
token, err := authenticateNode(creds.AccessKey, creds.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
ls := AuthRPCServer{}
|
ls := AuthRPCServer{}
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
args LoginRPCArgs
|
args LoginRPCArgs
|
||||||
@ -38,9 +42,8 @@ func TestLogin(t *testing.T) {
|
|||||||
// Valid case.
|
// Valid case.
|
||||||
{
|
{
|
||||||
args: LoginRPCArgs{
|
args: LoginRPCArgs{
|
||||||
Username: creds.AccessKey,
|
AuthToken: token,
|
||||||
Password: creds.SecretKey,
|
Version: Version,
|
||||||
Version: Version,
|
|
||||||
},
|
},
|
||||||
skewTime: 0,
|
skewTime: 0,
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
@ -48,9 +51,8 @@ func TestLogin(t *testing.T) {
|
|||||||
// Valid username, password and request time, not version.
|
// Valid username, password and request time, not version.
|
||||||
{
|
{
|
||||||
args: LoginRPCArgs{
|
args: LoginRPCArgs{
|
||||||
Username: creds.AccessKey,
|
AuthToken: token,
|
||||||
Password: creds.SecretKey,
|
Version: "INVALID-" + Version,
|
||||||
Version: "INVALID-" + Version,
|
|
||||||
},
|
},
|
||||||
skewTime: 0,
|
skewTime: 0,
|
||||||
expectedErr: errServerVersionMismatch,
|
expectedErr: errServerVersionMismatch,
|
||||||
@ -58,49 +60,17 @@ func TestLogin(t *testing.T) {
|
|||||||
// Valid username, password and version, not request time
|
// Valid username, password and version, not request time
|
||||||
{
|
{
|
||||||
args: LoginRPCArgs{
|
args: LoginRPCArgs{
|
||||||
Username: creds.AccessKey,
|
AuthToken: token,
|
||||||
Password: creds.SecretKey,
|
Version: Version,
|
||||||
Version: Version,
|
|
||||||
},
|
},
|
||||||
skewTime: 20 * time.Minute,
|
skewTime: 20 * time.Minute,
|
||||||
expectedErr: errServerTimeMismatch,
|
expectedErr: errServerTimeMismatch,
|
||||||
},
|
},
|
||||||
// Invalid username length
|
// Invalid token, fails with authentication error
|
||||||
{
|
{
|
||||||
args: LoginRPCArgs{
|
args: LoginRPCArgs{
|
||||||
Username: "aaa",
|
AuthToken: "",
|
||||||
Password: "minio123",
|
Version: Version,
|
||||||
Version: Version,
|
|
||||||
},
|
|
||||||
skewTime: 0,
|
|
||||||
expectedErr: errInvalidAccessKeyLength,
|
|
||||||
},
|
|
||||||
// Invalid password length
|
|
||||||
{
|
|
||||||
args: LoginRPCArgs{
|
|
||||||
Username: "minio",
|
|
||||||
Password: "aaa",
|
|
||||||
Version: Version,
|
|
||||||
},
|
|
||||||
skewTime: 0,
|
|
||||||
expectedErr: errInvalidSecretKeyLength,
|
|
||||||
},
|
|
||||||
// Invalid username
|
|
||||||
{
|
|
||||||
args: LoginRPCArgs{
|
|
||||||
Username: "aaaaa",
|
|
||||||
Password: creds.SecretKey,
|
|
||||||
Version: Version,
|
|
||||||
},
|
|
||||||
skewTime: 0,
|
|
||||||
expectedErr: errInvalidAccessKeyID,
|
|
||||||
},
|
|
||||||
// Invalid password
|
|
||||||
{
|
|
||||||
args: LoginRPCArgs{
|
|
||||||
Username: creds.AccessKey,
|
|
||||||
Password: "aaaaaaaa",
|
|
||||||
Version: Version,
|
|
||||||
},
|
},
|
||||||
skewTime: 0,
|
skewTime: 0,
|
||||||
expectedErr: errAuthentication,
|
expectedErr: errAuthentication,
|
||||||
@ -108,7 +78,7 @@ func TestLogin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for i, test := range testCases {
|
for i, test := range testCases {
|
||||||
reply := LoginRPCReply{}
|
reply := LoginRPCReply{}
|
||||||
test.args.RequestTime = time.Now().Add(test.skewTime).UTC()
|
test.args.RequestTime = UTCNow().Add(test.skewTime)
|
||||||
err := ls.Login(&test.args, &reply)
|
err := ls.Login(&test.args, &reply)
|
||||||
if err != test.expectedErr {
|
if err != test.expectedErr {
|
||||||
t.Errorf("Test %d: Expected error %v but received %v",
|
t.Errorf("Test %d: Expected error %v but received %v",
|
||||||
|
@ -23,26 +23,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Login handler implements JWT login token generator, which upon login request
|
|
||||||
// along with username and password is generated.
|
|
||||||
func (br *browserPeerAPIHandlers) Login(args *LoginRPCArgs, reply *LoginRPCReply) error {
|
|
||||||
// Validate LoginRPCArgs
|
|
||||||
if err := args.IsValid(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authenticate using JWT.
|
|
||||||
token, err := authenticateWeb(args.Username, args.Password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the token.
|
|
||||||
reply.AuthToken = token
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAuthPeerArgs - Arguments collection for SetAuth RPC call
|
// SetAuthPeerArgs - Arguments collection for SetAuth RPC call
|
||||||
type SetAuthPeerArgs struct {
|
type SetAuthPeerArgs struct {
|
||||||
// For Auth
|
// For Auth
|
||||||
|
@ -91,9 +91,12 @@ func (s *TestRPCBrowserPeerSuite) testBrowserPeerRPC(t *testing.T) {
|
|||||||
// Validate for failure in login handler with previous credentials.
|
// Validate for failure in login handler with previous credentials.
|
||||||
rclient = newRPCClient(s.testAuthConf.serverAddr, s.testAuthConf.serviceEndpoint, false)
|
rclient = newRPCClient(s.testAuthConf.serverAddr, s.testAuthConf.serviceEndpoint, false)
|
||||||
defer rclient.Close()
|
defer rclient.Close()
|
||||||
|
token, err := authenticateNode(creds.AccessKey, creds.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
rargs := &LoginRPCArgs{
|
rargs := &LoginRPCArgs{
|
||||||
Username: creds.AccessKey,
|
AuthToken: token,
|
||||||
Password: creds.SecretKey,
|
|
||||||
Version: Version,
|
Version: Version,
|
||||||
RequestTime: UTCNow(),
|
RequestTime: UTCNow(),
|
||||||
}
|
}
|
||||||
@ -105,20 +108,18 @@ func (s *TestRPCBrowserPeerSuite) testBrowserPeerRPC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token, err = authenticateNode(creds.AccessKey, creds.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
// Validate for success in loing handled with valid credetnails.
|
// Validate for success in loing handled with valid credetnails.
|
||||||
rargs = &LoginRPCArgs{
|
rargs = &LoginRPCArgs{
|
||||||
Username: creds.AccessKey,
|
AuthToken: token,
|
||||||
Password: creds.SecretKey,
|
|
||||||
Version: Version,
|
Version: Version,
|
||||||
RequestTime: UTCNow(),
|
RequestTime: UTCNow(),
|
||||||
}
|
}
|
||||||
rreply = &LoginRPCReply{}
|
rreply = &LoginRPCReply{}
|
||||||
err = rclient.Call("BrowserPeer"+loginMethodName, rargs, rreply)
|
if err = rclient.Call("BrowserPeer"+loginMethodName, rargs, rreply); err != nil {
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Validate all the replied fields after successful login.
|
|
||||||
if rreply.AuthToken == "" {
|
|
||||||
t.Fatalf("Generated token cannot be empty %s", errInvalidToken)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ type browserPeerAPIHandlers struct {
|
|||||||
|
|
||||||
// Register RPC router
|
// Register RPC router
|
||||||
func registerBrowserPeerRPCRouter(mux *router.Router) error {
|
func registerBrowserPeerRPCRouter(mux *router.Router) error {
|
||||||
bpHandlers := &browserPeerAPIHandlers{}
|
bpHandlers := &browserPeerAPIHandlers{AuthRPCServer{}}
|
||||||
|
|
||||||
bpRPCServer := newRPCServer()
|
bpRPCServer := newRPCServer()
|
||||||
err := bpRPCServer.RegisterName("BrowserPeer", bpHandlers)
|
err := bpRPCServer.RegisterName("BrowserPeer", bpHandlers)
|
||||||
|
28
cmd/jwt.go
28
cmd/jwt.go
@ -63,10 +63,10 @@ func authenticateJWT(accessKey, secretKey string, expiry time.Duration) (string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
utcNow := UTCNow()
|
utcNow := UTCNow()
|
||||||
token := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, jwtgo.MapClaims{
|
token := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, jwtgo.StandardClaims{
|
||||||
"exp": utcNow.Add(expiry).Unix(),
|
ExpiresAt: utcNow.Add(expiry).Unix(),
|
||||||
"iat": utcNow.Unix(),
|
IssuedAt: utcNow.Unix(),
|
||||||
"sub": accessKey,
|
Subject: accessKey,
|
||||||
})
|
})
|
||||||
|
|
||||||
return token.SignedString([]byte(serverCred.SecretKey))
|
return token.SignedString([]byte(serverCred.SecretKey))
|
||||||
@ -93,13 +93,17 @@ func keyFuncCallback(jwtToken *jwtgo.Token) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isAuthTokenValid(tokenString string) bool {
|
func isAuthTokenValid(tokenString string) bool {
|
||||||
jwtToken, err := jwtgo.Parse(tokenString, keyFuncCallback)
|
var claims jwtgo.StandardClaims
|
||||||
|
jwtToken, err := jwtgo.ParseWithClaims(tokenString, &claims, keyFuncCallback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err, "Unable to parse JWT token string")
|
errorIf(err, "Unable to parse JWT token string")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if err = claims.Valid(); err != nil {
|
||||||
return jwtToken.Valid
|
errorIf(err, "Invalid claims in JWT token string")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return jwtToken.Valid && claims.Subject == serverConfig.GetCredential().AccessKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func isHTTPRequestValid(req *http.Request) bool {
|
func isHTTPRequestValid(req *http.Request) bool {
|
||||||
@ -110,14 +114,20 @@ func isHTTPRequestValid(req *http.Request) bool {
|
|||||||
// Returns nil if the request is authenticated. errNoAuthToken if token missing.
|
// Returns nil if the request is authenticated. errNoAuthToken if token missing.
|
||||||
// Returns errAuthentication for all other errors.
|
// Returns errAuthentication for all other errors.
|
||||||
func webRequestAuthenticate(req *http.Request) error {
|
func webRequestAuthenticate(req *http.Request) error {
|
||||||
jwtToken, err := jwtreq.ParseFromRequest(req, jwtreq.AuthorizationHeaderExtractor, keyFuncCallback)
|
var claims jwtgo.StandardClaims
|
||||||
|
jwtToken, err := jwtreq.ParseFromRequestWithClaims(req, jwtreq.AuthorizationHeaderExtractor, &claims, keyFuncCallback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == jwtreq.ErrNoTokenInRequest {
|
if err == jwtreq.ErrNoTokenInRequest {
|
||||||
return errNoAuthToken
|
return errNoAuthToken
|
||||||
}
|
}
|
||||||
return errAuthentication
|
return errAuthentication
|
||||||
}
|
}
|
||||||
|
if err = claims.Valid(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if claims.Subject != serverConfig.GetCredential().AccessKey {
|
||||||
|
return errInvalidAccessKeyID
|
||||||
|
}
|
||||||
if !jwtToken.Valid {
|
if !jwtToken.Valid {
|
||||||
return errAuthentication
|
return errAuthentication
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -88,6 +89,58 @@ func TestAuthenticateURL(t *testing.T) {
|
|||||||
testAuthenticate("url", t)
|
testAuthenticate("url", t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests web request authenticator.
|
||||||
|
func TestWebRequestAuthenticate(t *testing.T) {
|
||||||
|
testPath, err := newTestConfig(globalMinioDefaultRegion)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable initialize config file, %s", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(testPath)
|
||||||
|
|
||||||
|
creds := serverConfig.GetCredential()
|
||||||
|
token, err := getTokenString(creds.AccessKey, creds.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable get token %s", err)
|
||||||
|
}
|
||||||
|
testCases := []struct {
|
||||||
|
req *http.Request
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
// Set valid authorization header.
|
||||||
|
{
|
||||||
|
req: &http.Request{
|
||||||
|
Header: http.Header{
|
||||||
|
"Authorization": []string{token},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
// No authorization header.
|
||||||
|
{
|
||||||
|
req: &http.Request{
|
||||||
|
Header: http.Header{},
|
||||||
|
},
|
||||||
|
expectedErr: errNoAuthToken,
|
||||||
|
},
|
||||||
|
// Invalid authorization token.
|
||||||
|
{
|
||||||
|
req: &http.Request{
|
||||||
|
Header: http.Header{
|
||||||
|
"Authorization": []string{"invalid-token"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: errAuthentication,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, testCase := range testCases {
|
||||||
|
gotErr := webRequestAuthenticate(testCase.req)
|
||||||
|
if testCase.expectedErr != gotErr {
|
||||||
|
t.Errorf("Test %d, expected err %s, got %s", i+1, testCase.expectedErr, gotErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkAuthenticateNode(b *testing.B) {
|
func BenchmarkAuthenticateNode(b *testing.B) {
|
||||||
testPath, err := newTestConfig(globalMinioDefaultRegion)
|
testPath, err := newTestConfig(globalMinioDefaultRegion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -58,9 +58,12 @@ func createLockTestServer(t *testing.T) (string, *lockServer, string) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
creds := serverConfig.GetCredential()
|
creds := serverConfig.GetCredential()
|
||||||
|
token, err := authenticateNode(creds.AccessKey, creds.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
loginArgs := LoginRPCArgs{
|
loginArgs := LoginRPCArgs{
|
||||||
Username: creds.AccessKey,
|
AuthToken: token,
|
||||||
Password: creds.SecretKey,
|
|
||||||
Version: Version,
|
Version: Version,
|
||||||
RequestTime: UTCNow(),
|
RequestTime: UTCNow(),
|
||||||
}
|
}
|
||||||
@ -69,8 +72,6 @@ func createLockTestServer(t *testing.T) (string, *lockServer, string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to login to lock server - %v", err)
|
t.Fatalf("Failed to login to lock server - %v", err)
|
||||||
}
|
}
|
||||||
token := loginReply.AuthToken
|
|
||||||
|
|
||||||
return testPath, locker, token
|
return testPath, locker, token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,10 +235,7 @@ func (n *notifier) Validate() error {
|
|||||||
if err := n.MySQL.Validate(); err != nil {
|
if err := n.MySQL.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := n.MQTT.Validate(); err != nil {
|
return n.MQTT.Validate()
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *notifier) SetAMQPByID(accountID string, amqpn amqpNotify) {
|
func (n *notifier) SetAMQPByID(accountID string, amqpn amqpNotify) {
|
||||||
|
@ -60,8 +60,7 @@ type AuthRPCReply struct{}
|
|||||||
|
|
||||||
// LoginRPCArgs - login username and password for RPC.
|
// LoginRPCArgs - login username and password for RPC.
|
||||||
type LoginRPCArgs struct {
|
type LoginRPCArgs struct {
|
||||||
Username string
|
AuthToken string
|
||||||
Password string
|
|
||||||
Version string
|
Version string
|
||||||
RequestTime time.Time
|
RequestTime time.Time
|
||||||
}
|
}
|
||||||
@ -80,11 +79,8 @@ func (args LoginRPCArgs) IsValid() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoginRPCReply - login reply provides generated token to be used
|
// LoginRPCReply - login reply is a dummy struct perhaps for future use.
|
||||||
// with subsequent requests.
|
type LoginRPCReply struct{}
|
||||||
type LoginRPCReply struct {
|
|
||||||
AuthToken string
|
|
||||||
}
|
|
||||||
|
|
||||||
// LockArgs represents arguments for any authenticated lock RPC call.
|
// LockArgs represents arguments for any authenticated lock RPC call.
|
||||||
type LockArgs struct {
|
type LockArgs struct {
|
||||||
|
@ -34,6 +34,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
jwtgo "github.com/dgrijalva/jwt-go"
|
||||||
humanize "github.com/dustin/go-humanize"
|
humanize "github.com/dustin/go-humanize"
|
||||||
"github.com/minio/minio-go/pkg/policy"
|
"github.com/minio/minio-go/pkg/policy"
|
||||||
"github.com/minio/minio-go/pkg/set"
|
"github.com/minio/minio-go/pkg/set"
|
||||||
@ -667,6 +668,16 @@ func TestWebCreateURLToken(t *testing.T) {
|
|||||||
ExecObjectLayerTest(t, testCreateURLToken)
|
ExecObjectLayerTest(t, testCreateURLToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTokenString(accessKey, secretKey string) (string, error) {
|
||||||
|
utcNow := UTCNow()
|
||||||
|
token := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, jwtgo.StandardClaims{
|
||||||
|
ExpiresAt: utcNow.Add(defaultJWTExpiry).Unix(),
|
||||||
|
IssuedAt: utcNow.Unix(),
|
||||||
|
Subject: accessKey,
|
||||||
|
})
|
||||||
|
return token.SignedString([]byte(secretKey))
|
||||||
|
}
|
||||||
|
|
||||||
func testCreateURLToken(obj ObjectLayer, instanceType string, t TestErrHandler) {
|
func testCreateURLToken(obj ObjectLayer, instanceType string, t TestErrHandler) {
|
||||||
apiRouter := initTestWebRPCEndPoint(obj)
|
apiRouter := initTestWebRPCEndPoint(obj)
|
||||||
credentials := serverConfig.GetCredential()
|
credentials := serverConfig.GetCredential()
|
||||||
@ -700,6 +711,21 @@ func testCreateURLToken(obj ObjectLayer, instanceType string, t TestErrHandler)
|
|||||||
if !isAuthTokenValid(tokenReply.Token) {
|
if !isAuthTokenValid(tokenReply.Token) {
|
||||||
t.Fatalf("token is not valid")
|
t.Fatalf("token is not valid")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Token is invalid.
|
||||||
|
if isAuthTokenValid("") {
|
||||||
|
t.Fatalf("token shouldn't be valid, but it is")
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := getTokenString("invalid-access", credentials.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token has invalid access key.
|
||||||
|
if isAuthTokenValid(token) {
|
||||||
|
t.Fatalf("token shouldn't be valid, but it is")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper for calling Upload Handler
|
// Wrapper for calling Upload Handler
|
||||||
|
@ -92,12 +92,7 @@ func (d config) Save(filename string) error {
|
|||||||
func (d config) Load(filename string) error {
|
func (d config) Load(filename string) error {
|
||||||
d.lock.Lock()
|
d.lock.Lock()
|
||||||
defer d.lock.Unlock()
|
defer d.lock.Unlock()
|
||||||
|
return loadFileConfig(filename, d.data)
|
||||||
if err := loadFileConfig(filename, d.data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data - grab internal data map for reading
|
// Data - grab internal data map for reading
|
||||||
|
Loading…
Reference in New Issue
Block a user