mirror of
https://github.com/minio/minio.git
synced 2025-11-10 14:09:48 -05:00
Add rotating token support for admin API (#9244)
Use the *credentials.Credentials implementation method *Get*
```
func (c *Credentials) Get() (Value, error) {
```
which also handles auto-refresh, this allows for chaining
of various implementations together if necessary or simply
initialize with credentials.NewStaticV4(access, secret, token)
Co-authored-by: Klaus Post <klauspost@gmail.com>
This commit is contained in:
@@ -26,6 +26,7 @@ import (
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -34,18 +35,21 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio-go/v6/pkg/credentials"
|
||||
"github.com/minio/minio-go/v6/pkg/s3signer"
|
||||
"github.com/minio/minio-go/v6/pkg/s3utils"
|
||||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
// AdminClient implements Amazon S3 compatible methods.
|
||||
type AdminClient struct {
|
||||
/// Standard options.
|
||||
|
||||
// AccessKeyID required for authorized requests.
|
||||
accessKeyID string
|
||||
// SecretAccessKey required for authorized requests.
|
||||
secretAccessKey string
|
||||
// Parsed endpoint url provided by the user.
|
||||
endpointURL *url.URL
|
||||
|
||||
// Holds various credential providers.
|
||||
credsProvider *credentials.Credentials
|
||||
|
||||
// User supplied.
|
||||
appInfo struct {
|
||||
@@ -53,8 +57,6 @@ type AdminClient struct {
|
||||
appVersion string
|
||||
}
|
||||
|
||||
endpointURL url.URL
|
||||
|
||||
// Indicate whether we are using https or not
|
||||
secure bool
|
||||
|
||||
@@ -85,38 +87,67 @@ const (
|
||||
libraryUserAgent = libraryUserAgentPrefix + libraryName + "/" + libraryVersion
|
||||
)
|
||||
|
||||
// New - instantiate minio client Client, adds automatic verification of signature.
|
||||
// Options for New method
|
||||
type Options struct {
|
||||
Creds *credentials.Credentials
|
||||
Secure bool
|
||||
// Add future fields here
|
||||
}
|
||||
|
||||
// New - instantiate minio admin client
|
||||
func New(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*AdminClient, error) {
|
||||
clnt, err := privateNew(endpoint, accessKeyID, secretAccessKey, secure)
|
||||
creds := credentials.NewStaticV4(accessKeyID, secretAccessKey, "")
|
||||
|
||||
clnt, err := privateNew(endpoint, creds, secure)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clnt, nil
|
||||
}
|
||||
|
||||
func privateNew(endpoint, accessKeyID, secretAccessKey string, secure bool) (*AdminClient, error) {
|
||||
// NewWithOptions - instantiate minio admin client with options.
|
||||
func NewWithOptions(endpoint string, opts *Options) (*AdminClient, error) {
|
||||
clnt, err := privateNew(endpoint, opts.Creds, opts.Secure)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clnt, nil
|
||||
}
|
||||
|
||||
func privateNew(endpoint string, creds *credentials.Credentials, secure bool) (*AdminClient, error) {
|
||||
// Initialize cookies to preserve server sent cookies if any and replay
|
||||
// them upon each request.
|
||||
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// construct endpoint.
|
||||
endpointURL, err := getEndpointURL(endpoint, secure)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// instantiate new Client.
|
||||
clnt := &AdminClient{
|
||||
accessKeyID: accessKeyID,
|
||||
secretAccessKey: secretAccessKey,
|
||||
// Remember whether we are using https or not
|
||||
secure: secure,
|
||||
// Save endpoint URL, user agent for future uses.
|
||||
endpointURL: *endpointURL,
|
||||
// Instantiate http client and bucket location cache.
|
||||
httpClient: &http.Client{
|
||||
Transport: DefaultTransport(secure),
|
||||
},
|
||||
// Introduce a new locked random seed.
|
||||
random: rand.New(&lockedRandSource{src: rand.NewSource(time.Now().UTC().UnixNano())}),
|
||||
clnt := new(AdminClient)
|
||||
|
||||
// Save the credentials.
|
||||
clnt.credsProvider = creds
|
||||
|
||||
// Remember whether we are using https or not
|
||||
clnt.secure = secure
|
||||
|
||||
// Save endpoint URL, user agent for future uses.
|
||||
clnt.endpointURL = endpointURL
|
||||
|
||||
// Instantiate http client and bucket location cache.
|
||||
clnt.httpClient = &http.Client{
|
||||
Jar: jar,
|
||||
Transport: DefaultTransport(secure),
|
||||
}
|
||||
|
||||
// Add locked pseudo-random number generator.
|
||||
clnt.random = rand.New(&lockedRandSource{src: rand.NewSource(time.Now().UTC().UnixNano())})
|
||||
|
||||
// Return.
|
||||
return clnt, nil
|
||||
}
|
||||
@@ -393,6 +424,16 @@ func (adm AdminClient) setUserAgent(req *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (adm AdminClient) getSecretKey() string {
|
||||
value, err := adm.credsProvider.Get()
|
||||
if err != nil {
|
||||
// Return empty, call will fail.
|
||||
return ""
|
||||
}
|
||||
|
||||
return value.SecretAccessKey
|
||||
}
|
||||
|
||||
// newRequest - instantiate a new HTTP request for a given method.
|
||||
func (adm AdminClient) newRequest(method string, reqData requestData) (req *http.Request, err error) {
|
||||
// If no method is supplied default to 'POST'.
|
||||
@@ -415,6 +456,17 @@ func (adm AdminClient) newRequest(method string, reqData requestData) (req *http
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value, err := adm.credsProvider.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
accessKeyID = value.AccessKeyID
|
||||
secretAccessKey = value.SecretAccessKey
|
||||
sessionToken = value.SessionToken
|
||||
)
|
||||
|
||||
adm.setUserAgent(req)
|
||||
for k, v := range reqData.customHeaders {
|
||||
req.Header.Set(k, v[0])
|
||||
@@ -425,7 +477,7 @@ func (adm AdminClient) newRequest(method string, reqData requestData) (req *http
|
||||
req.Header.Set("X-Amz-Content-Sha256", hex.EncodeToString(sum256(reqData.content)))
|
||||
req.Body = ioutil.NopCloser(bytes.NewReader(reqData.content))
|
||||
|
||||
req = s3signer.SignV4(*req, adm.accessKeyID, adm.secretAccessKey, "", location)
|
||||
req = s3signer.SignV4(*req, accessKeyID, secretAccessKey, sessionToken, location)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ func (adm *AdminClient) GetConfig(ctx context.Context) ([]byte, error) {
|
||||
return nil, httpRespToErrorResponse(resp)
|
||||
}
|
||||
|
||||
return DecryptData(adm.secretAccessKey, resp.Body)
|
||||
return DecryptData(adm.getSecretKey(), resp.Body)
|
||||
}
|
||||
|
||||
// SetConfig - set config supplied as config.json for the setup.
|
||||
@@ -56,7 +56,7 @@ func (adm *AdminClient) SetConfig(ctx context.Context, config io.Reader) (err er
|
||||
return err
|
||||
}
|
||||
configBytes := configBuf[:n]
|
||||
econfigBytes, err := EncryptData(adm.secretAccessKey, configBytes)
|
||||
econfigBytes, err := EncryptData(adm.getSecretKey(), configBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func (adm *AdminClient) ListConfigHistoryKV(ctx context.Context, count int) ([]C
|
||||
return nil, httpRespToErrorResponse(resp)
|
||||
}
|
||||
|
||||
data, err := DecryptData(adm.secretAccessKey, resp.Body)
|
||||
data, err := DecryptData(adm.getSecretKey(), resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
|
||||
// DelConfigKV - delete key from server config.
|
||||
func (adm *AdminClient) DelConfigKV(ctx context.Context, k string) (err error) {
|
||||
econfigBytes, err := EncryptData(adm.secretAccessKey, []byte(k))
|
||||
econfigBytes, err := EncryptData(adm.getSecretKey(), []byte(k))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -52,7 +52,7 @@ func (adm *AdminClient) DelConfigKV(ctx context.Context, k string) (err error) {
|
||||
|
||||
// SetConfigKV - set key value config to server.
|
||||
func (adm *AdminClient) SetConfigKV(ctx context.Context, kv string) (err error) {
|
||||
econfigBytes, err := EncryptData(adm.secretAccessKey, []byte(kv))
|
||||
econfigBytes, err := EncryptData(adm.getSecretKey(), []byte(kv))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func (adm *AdminClient) GetConfigKV(ctx context.Context, key string) (Targets, e
|
||||
return nil, httpRespToErrorResponse(resp)
|
||||
}
|
||||
|
||||
data, err := DecryptData(adm.secretAccessKey, resp.Body)
|
||||
data, err := DecryptData(adm.getSecretKey(), resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ func (adm *AdminClient) ListUsers(ctx context.Context) (map[string]UserInfo, err
|
||||
return nil, httpRespToErrorResponse(resp)
|
||||
}
|
||||
|
||||
data, err := DecryptData(adm.secretAccessKey, resp.Body)
|
||||
data, err := DecryptData(adm.getSecretKey(), resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -152,7 +152,7 @@ func (adm *AdminClient) SetUser(ctx context.Context, accessKey, secretKey string
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
econfigBytes, err := EncryptData(adm.secretAccessKey, data)
|
||||
econfigBytes, err := EncryptData(adm.getSecretKey(), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -239,7 +239,7 @@ func (adm *AdminClient) AddServiceAccount(ctx context.Context, parentUser string
|
||||
return auth.Credentials{}, err
|
||||
}
|
||||
|
||||
econfigBytes, err := EncryptData(adm.secretAccessKey, data)
|
||||
econfigBytes, err := EncryptData(adm.getSecretKey(), data)
|
||||
if err != nil {
|
||||
return auth.Credentials{}, err
|
||||
}
|
||||
@@ -260,7 +260,7 @@ func (adm *AdminClient) AddServiceAccount(ctx context.Context, parentUser string
|
||||
return auth.Credentials{}, httpRespToErrorResponse(resp)
|
||||
}
|
||||
|
||||
data, err = DecryptData(adm.secretAccessKey, resp.Body)
|
||||
data, err = DecryptData(adm.getSecretKey(), resp.Body)
|
||||
if err != nil {
|
||||
return auth.Credentials{}, err
|
||||
}
|
||||
@@ -298,7 +298,7 @@ func (adm *AdminClient) GetServiceAccount(ctx context.Context, serviceAccountAcc
|
||||
return auth.Credentials{}, httpRespToErrorResponse(resp)
|
||||
}
|
||||
|
||||
data, err := DecryptData(adm.secretAccessKey, resp.Body)
|
||||
data, err := DecryptData(adm.getSecretKey(), resp.Body)
|
||||
if err != nil {
|
||||
return auth.Credentials{}, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user