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:
Harshavardhana
2020-04-01 13:34:20 -07:00
committed by GitHub
parent 336460f67e
commit 7de29e6e6b
7 changed files with 93 additions and 49 deletions

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}