storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/jwt.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2016-2020 MinIO, Inc.
     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 cmd
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"net/http"
    23  	"time"
    24  
    25  	jwtgo "github.com/dgrijalva/jwt-go"
    26  	jwtreq "github.com/dgrijalva/jwt-go/request"
    27  
    28  	xjwt "storj.io/minio/cmd/jwt"
    29  	"storj.io/minio/cmd/logger"
    30  	"storj.io/minio/pkg/auth"
    31  )
    32  
    33  const (
    34  	jwtAlgorithm = "Bearer"
    35  
    36  	// Default JWT token for web handlers is one day.
    37  	defaultJWTExpiry = 24 * time.Hour
    38  
    39  	// Inter-node JWT token expiry is 15 minutes.
    40  	defaultInterNodeJWTExpiry = 15 * time.Minute
    41  
    42  	// URL JWT token expiry is one minute (might be exposed).
    43  	defaultURLJWTExpiry = time.Minute
    44  )
    45  
    46  var (
    47  	errInvalidAccessKeyID   = errors.New("The access key ID you provided does not exist in our records")
    48  	errChangeCredNotAllowed = errors.New("Changing access key and secret key not allowed")
    49  	errAuthentication       = errors.New("Authentication failed, check your access credentials")
    50  	errNoAuthToken          = errors.New("JWT token missing")
    51  	errIncorrectCreds       = errors.New("Current access key or secret key is incorrect")
    52  	errPresignedNotAllowed  = errors.New("Unable to generate shareable URL due to lack of read permissions")
    53  )
    54  
    55  func authenticateJWTUsers(accessKey, secretKey string, expiry time.Duration) (string, error) {
    56  	passedCredential, err := auth.CreateCredentials(accessKey, secretKey)
    57  	if err != nil {
    58  		return "", err
    59  	}
    60  	expiresAt := UTCNow().Add(expiry)
    61  	return authenticateJWTUsersWithCredentials(passedCredential, expiresAt)
    62  }
    63  
    64  func authenticateJWTUsersWithCredentials(credentials auth.Credentials, expiresAt time.Time) (string, error) {
    65  	serverCred := globalActiveCred
    66  	if serverCred.AccessKey != credentials.AccessKey {
    67  		var ok bool
    68  		serverCred, ok = GlobalIAMSys.GetUser(context.Background(), credentials.AccessKey)
    69  		if !ok {
    70  			return "", errInvalidAccessKeyID
    71  		}
    72  	}
    73  
    74  	if !serverCred.Equal(credentials) {
    75  		return "", errAuthentication
    76  	}
    77  
    78  	claims := xjwt.NewMapClaims()
    79  	claims.SetExpiry(expiresAt)
    80  	claims.SetAccessKey(credentials.AccessKey)
    81  
    82  	jwt := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, claims)
    83  	return jwt.SignedString([]byte(serverCred.SecretKey))
    84  }
    85  
    86  func authenticateNode(accessKey, secretKey, audience string) (string, error) {
    87  	claims := xjwt.NewStandardClaims()
    88  	claims.SetExpiry(UTCNow().Add(defaultInterNodeJWTExpiry))
    89  	claims.SetAccessKey(accessKey)
    90  	claims.SetAudience(audience)
    91  
    92  	jwt := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, claims)
    93  	return jwt.SignedString([]byte(secretKey))
    94  }
    95  
    96  func authenticateWeb(accessKey, secretKey string) (string, error) {
    97  	return authenticateJWTUsers(accessKey, secretKey, defaultJWTExpiry)
    98  }
    99  
   100  func authenticateURL(accessKey, secretKey string) (string, error) {
   101  	return authenticateJWTUsers(accessKey, secretKey, defaultURLJWTExpiry)
   102  }
   103  
   104  // Callback function used for parsing
   105  func webTokenCallback(claims *xjwt.MapClaims) ([]byte, error) {
   106  	if claims.AccessKey == globalActiveCred.AccessKey {
   107  		return []byte(globalActiveCred.SecretKey), nil
   108  	}
   109  	ok, _, err := GlobalIAMSys.IsTempUser(claims.AccessKey)
   110  	if err != nil {
   111  		if err == errNoSuchUser {
   112  			return nil, errInvalidAccessKeyID
   113  		}
   114  		return nil, err
   115  	}
   116  	if ok {
   117  		return []byte(globalActiveCred.SecretKey), nil
   118  	}
   119  	cred, ok := GlobalIAMSys.GetUser(context.Background(), claims.AccessKey)
   120  	if !ok {
   121  		return nil, errInvalidAccessKeyID
   122  	}
   123  	return []byte(cred.SecretKey), nil
   124  
   125  }
   126  
   127  func isAuthTokenValid(token string) bool {
   128  	_, _, err := webTokenAuthenticate(token)
   129  	return err == nil
   130  }
   131  
   132  func webTokenAuthenticate(token string) (*xjwt.MapClaims, bool, error) {
   133  	if token == "" {
   134  		return nil, false, errNoAuthToken
   135  	}
   136  	claims := xjwt.NewMapClaims()
   137  	if err := xjwt.ParseWithClaims(token, claims, webTokenCallback); err != nil {
   138  		return claims, false, errAuthentication
   139  	}
   140  	owner := claims.AccessKey == globalActiveCred.AccessKey
   141  	return claims, owner, nil
   142  }
   143  
   144  // Check if the request is authenticated.
   145  // Returns nil if the request is authenticated. errNoAuthToken if token missing.
   146  // Returns errAuthentication for all other errors.
   147  func webRequestAuthenticate(req *http.Request) (*xjwt.MapClaims, bool, error) {
   148  	token, err := jwtreq.AuthorizationHeaderExtractor.ExtractToken(req)
   149  	if err != nil {
   150  		if err == jwtreq.ErrNoTokenInRequest {
   151  			return nil, false, errNoAuthToken
   152  		}
   153  		return nil, false, err
   154  	}
   155  	claims := xjwt.NewMapClaims()
   156  	if err := xjwt.ParseWithClaims(token, claims, webTokenCallback); err != nil {
   157  		return claims, false, errAuthentication
   158  	}
   159  	owner := claims.AccessKey == globalActiveCred.AccessKey
   160  	return claims, owner, nil
   161  }
   162  
   163  func newAuthToken(audience string) string {
   164  	cred := globalActiveCred
   165  	token, err := authenticateNode(cred.AccessKey, cred.SecretKey, audience)
   166  	logger.CriticalIf(GlobalContext, err)
   167  	return token
   168  }