Skip to content

Commit 0dfe73c

Browse files
committed
STS integration, JWT auth and Stateless MCS
This commit changes the authentication mechanism between mcs and minio to an sts (security token service) schema using the user provided credentials, previously mcs was using master credentials. With that said in order for you to login to MCS as an admin your user must exists first on minio and have enough privileges to do administrative operations. ``` ./mc admin user add myminio alevsk alevsk12345 ``` ``` cat admin.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "admin:*", "s3:*" ], "Resource": [ "arn:aws:s3:::*" ] } ] } ./mc admin policy add myminio admin admin.json ``` ``` ./mc admin policy set myminio admin user=alevsk ```
1 parent 0bcf88e commit 0dfe73c

29 files changed

+814
-302
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ assets:
2222

2323
test:
2424
@(go test -race -v github.com/minio/mcs/restapi/...)
25+
@(go test -race -v github.com/minio/mcs/pkg/auth)
2526

2627
coverage:
2728
@(go test -v -coverprofile=coverage.out github.com/minio/mcs/restapi/... && go tool cover -html=coverage.out && open coverage.html)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ $ mc admin policy set myminio mcsAdmin user=mcs
5454
To run the server:
5555

5656
```
57+
export MCS_HMAC_JWT_SECRET=YOURJWTSIGNINGSECRET
5758
export MCS_ACCESS_KEY=mcs
5859
export MCS_SECRET_KEY=YOURMCSSECRET
5960
export MCS_MINIO_SERVER=http://localhost:9000

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/minio/mcs
33
go 1.14
44

55
require (
6+
github.com/dgrijalva/jwt-go v3.2.0+incompatible
67
github.com/elazarl/go-bindata-assetfs v1.0.0
78
github.com/go-openapi/errors v0.19.4
89
github.com/go-openapi/loads v0.19.5
@@ -12,6 +13,7 @@ require (
1213
github.com/go-openapi/swag v0.19.8
1314
github.com/go-openapi/validate v0.19.7
1415
github.com/jessevdk/go-flags v1.4.0
16+
github.com/json-iterator/go v1.1.9
1517
github.com/minio/cli v1.22.0
1618
github.com/minio/mc v0.0.0-20200415193718-68b638f2f96c
1719
github.com/minio/minio v0.0.0-20200415191640-bde0f444dbab

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ github.com/minio/minio v0.0.0-20200415191640-bde0f444dbab h1:9hlqghJl3e3HorXa6AD
390390
github.com/minio/minio v0.0.0-20200415191640-bde0f444dbab/go.mod h1:v8oQPMMaTkjDwp5cOz1WCElA4Ik+X+0y4On+VMk0fis=
391391
github.com/minio/minio-go/v6 v6.0.53 h1:8jzpwiOzZ5Iz7/goFWqNZRICbyWYShbb5rARjrnSCNI=
392392
github.com/minio/minio-go/v6 v6.0.53/go.mod h1:DIvC/IApeHX8q1BAMVCXSXwpmrmM+I+iBvhvztQorfI=
393+
github.com/minio/parquet-go v0.0.0-20200414234858-838cfa8aae61 h1:pUSI/WKPdd77gcuoJkSzhJ4wdS8OMDOsOu99MtpXEQA=
393394
github.com/minio/parquet-go v0.0.0-20200414234858-838cfa8aae61/go.mod h1:4trzEJ7N1nBTd5Tt7OCZT5SEin+WiAXpdJ/WgPkESA8=
394395
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
395396
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=

pkg/auth/jwt.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2020 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package auth
18+
19+
import (
20+
"errors"
21+
22+
jwtgo "github.com/dgrijalva/jwt-go"
23+
xjwt "github.com/minio/mcs/pkg/auth/jwt"
24+
"github.com/minio/minio-go/v6/pkg/credentials"
25+
"github.com/minio/minio/cmd"
26+
)
27+
28+
var (
29+
errAuthentication = errors.New("Authentication failed, check your access credentials")
30+
errNoAuthToken = errors.New("JWT token missing")
31+
)
32+
33+
// IsJWTValid returns true or false depending if the provided jwt is valid or not
34+
func IsJWTValid(token string) bool {
35+
_, err := JWTAuthenticate(token)
36+
return err == nil
37+
}
38+
39+
// JWTAuthenticate takes a jwt, decode it, extract claims and validate the signature
40+
// returns claims after validation in the following format:
41+
//
42+
// type MapClaims struct {
43+
// AccessKeyID
44+
// SecretAccessKey
45+
// SessionToken
46+
// }
47+
func JWTAuthenticate(token string) (*xjwt.MapClaims, error) {
48+
if token == "" {
49+
return nil, errNoAuthToken
50+
}
51+
claims := xjwt.NewMapClaims()
52+
if err := xjwt.ParseWithClaims(token, claims); err != nil {
53+
return claims, errAuthentication
54+
}
55+
return claims, nil
56+
}
57+
58+
// NewJWTWithClaimsForClient generates a new jwt with claims based on the provided STS credentials
59+
func NewJWTWithClaimsForClient(credentials *credentials.Value, audience string) (string, error) {
60+
if credentials != nil {
61+
claims := xjwt.NewStandardClaims()
62+
claims.SetExpiry(cmd.UTCNow().Add(xjwt.GetMcsSTSAndJWTDurationTime()))
63+
claims.SetAccessKeyID(credentials.AccessKeyID)
64+
claims.SetSecretAccessKey(credentials.SecretAccessKey)
65+
claims.SetSessionToken(credentials.SessionToken)
66+
claims.SetAudience(audience)
67+
68+
jwt := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, claims)
69+
return jwt.SignedString([]byte(xjwt.GetHmacJWTSecret()))
70+
}
71+
return "", errors.New("provided credentials are empty")
72+
}

restapi/sessions/sessions.go renamed to pkg/auth/jwt/config.go

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,58 +14,18 @@
1414
// You should have received a copy of the GNU Affero General Public License
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

17-
package sessions
17+
package jwt
1818

1919
import (
2020
"crypto/rand"
2121
"io"
22+
"strconv"
2223
"strings"
23-
"sync"
24+
"time"
2425

25-
mcCmd "github.com/minio/mc/cmd"
26+
"github.com/minio/minio/pkg/env"
2627
)
2728

28-
type Singleton struct {
29-
sessions map[string]*mcCmd.Config
30-
}
31-
32-
var instance *Singleton
33-
var once sync.Once
34-
35-
// Returns a Singleton instance that keeps the sessions
36-
func GetInstance() *Singleton {
37-
once.Do(func() {
38-
//build sessions hash
39-
sessions := make(map[string]*mcCmd.Config)
40-
41-
instance = &Singleton{
42-
sessions: sessions,
43-
}
44-
})
45-
return instance
46-
}
47-
48-
// The delete built-in function deletes the element with the specified key (m[key]) from the map.
49-
// If m is nil or there is no such element, delete is a no-op. https://golang.org/pkg/builtin/#delete
50-
func (s *Singleton) DeleteSession(sessionID string) {
51-
delete(s.sessions, sessionID)
52-
}
53-
54-
func (s *Singleton) NewSession(cfg *mcCmd.Config) string {
55-
// genereate random session id
56-
sessionID := RandomCharString(64)
57-
// store the cfg under that session id
58-
s.sessions[sessionID] = cfg
59-
return sessionID
60-
}
61-
62-
func (s *Singleton) ValidSession(sessionID string) bool {
63-
if _, ok := s.sessions[sessionID]; ok {
64-
return true
65-
}
66-
return false
67-
}
68-
6929
// Do not use:
7030
// https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go
7131
// It relies on math/rand and therefore not on a cryptographically secure RNG => It must not be used
@@ -93,3 +53,27 @@ func RandomCharString(n int) string {
9353
}
9454
return s.String()
9555
}
56+
57+
// defaultHmacJWTSecret will be used by default if application is not configured with a custom MCS_HMAC_JWT_SECRET secret
58+
var defaultHmacJWTSecret = RandomCharString(64)
59+
60+
// GetHmacJWTSecret returns the 64 bytes secret used for signing the generated JWT for the application
61+
func GetHmacJWTSecret() string {
62+
return env.Get(McsHmacJWTSecret, defaultHmacJWTSecret)
63+
}
64+
65+
// McsSTSAndJWTDurationSeconds returns the default session duration for the STS requested tokens and the generated JWTs.
66+
// Ideally both values should match so jwt and Minio sts sessions expires at the same time.
67+
func GetMcsSTSAndJWTDurationInSeconds() int {
68+
duration, err := strconv.Atoi(env.Get(McsSTSAndJWTDurationSeconds, "3600"))
69+
if err != nil {
70+
duration = 3600
71+
}
72+
return duration
73+
}
74+
75+
// GetMcsSTSAndJWTDurationTime returns GetMcsSTSAndJWTDurationInSeconds in duration format
76+
func GetMcsSTSAndJWTDurationTime() time.Duration {
77+
duration := GetMcsSTSAndJWTDurationInSeconds()
78+
return time.Duration(duration) * time.Second
79+
}

pkg/auth/jwt/const.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2020 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package jwt
18+
19+
const (
20+
McsHmacJWTSecret = "MCS_HMAC_JWT_SECRET"
21+
McsSTSAndJWTDurationSeconds = "MCS_STS_AND_JWT_DURATION_SECONDS"
22+
)

0 commit comments

Comments
 (0)