Skip to content

Commit

Permalink
Add support for STS properly in metrics client (#271)
Browse files Browse the repository at this point in the history
  • Loading branch information
harshavardhana authored Feb 17, 2024
1 parent dd2b187 commit ef97810
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/vulncheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ 1.21.5 ]
go-version: [ 1.21.7 ]
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
Expand Down
26 changes: 17 additions & 9 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,18 @@ const (

// Options for New method
type Options struct {
Creds *credentials.Credentials
Secure bool
Creds *credentials.Credentials
Secure bool
Transport http.RoundTripper
// Add future fields here
}

// New - instantiate minio admin client
// Deprecated: please use NewWithOptions
func New(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*AdminClient, error) {
creds := credentials.NewStaticV4(accessKeyID, secretAccessKey, "")

clnt, err := privateNew(endpoint, creds, secure)
clnt, err := privateNew(endpoint, &Options{Creds: creds, Secure: secure})
if err != nil {
return nil, err
}
Expand All @@ -113,14 +115,14 @@ func New(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*Ad

// NewWithOptions - instantiate minio admin client with options.
func NewWithOptions(endpoint string, opts *Options) (*AdminClient, error) {
clnt, err := privateNew(endpoint, opts.Creds, opts.Secure)
clnt, err := privateNew(endpoint, opts)
if err != nil {
return nil, err
}
return clnt, nil
}

func privateNew(endpoint string, creds *credentials.Credentials, secure bool) (*AdminClient, error) {
func privateNew(endpoint string, opts *Options) (*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})
Expand All @@ -129,26 +131,31 @@ func privateNew(endpoint string, creds *credentials.Credentials, secure bool) (*
}

// construct endpoint.
endpointURL, err := getEndpointURL(endpoint, secure)
endpointURL, err := getEndpointURL(endpoint, opts.Secure)
if err != nil {
return nil, err
}

clnt := new(AdminClient)

// Save the credentials.
clnt.credsProvider = creds
clnt.credsProvider = opts.Creds

// Remember whether we are using https or not
clnt.secure = secure
clnt.secure = opts.Secure

// Save endpoint URL, user agent for future uses.
clnt.endpointURL = endpointURL

tr := opts.Transport
if tr == nil {
tr = DefaultTransport(opts.Secure)
}

// Instantiate http client and bucket location cache.
clnt.httpClient = &http.Client{
Jar: jar,
Transport: DefaultTransport(secure),
Transport: tr,
}

// Add locked pseudo-random number generator.
Expand All @@ -169,6 +176,7 @@ func (adm *AdminClient) SetAppInfo(appName string, appVersion string) {
}

// SetCustomTransport - set new custom transport.
// Deprecated: please use Options{Transport: tr} to provide custom transport.
func (adm *AdminClient) SetCustomTransport(customHTTPTransport http.RoundTripper) {
// Set this to override default transport
// ``http.DefaultTransport``.
Expand Down
60 changes: 46 additions & 14 deletions metrics_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"time"

jwtgo "github.com/golang-jwt/jwt/v4"
"github.com/minio/minio-go/v7/pkg/credentials"
)

const (
Expand All @@ -37,8 +38,8 @@ const (

// MetricsClient implements MinIO metrics operations
type MetricsClient struct {
/// JWT token for authentication
jwtToken string
/// Credentials for authentication
creds *credentials.Credentials
// Indicate whether we are using https or not
secure bool
// Parsed endpoint url provided by the user.
Expand All @@ -53,25 +54,33 @@ type metricsRequestData struct {
relativePath string // URL path relative to admin API base endpoint
}

// NewMetricsClient - instantiate minio metrics client honoring Prometheus format
func NewMetricsClient(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*MetricsClient, error) {
jwtToken, err := getPrometheusToken(accessKeyID, secretAccessKey)
if err != nil {
return nil, err
// NewMetricsClientWithOptions - instantiate minio metrics client honoring Prometheus format
func NewMetricsClientWithOptions(endpoint string, opts *Options) (*MetricsClient, error) {
if opts == nil {
return nil, ErrInvalidArgument("empty options not allowed")
}

endpointURL, err := getEndpointURL(endpoint, secure)
endpointURL, err := getEndpointURL(endpoint, opts.Secure)
if err != nil {
return nil, err
}

clnt, err := privateNewMetricsClient(endpointURL, jwtToken, secure)
clnt, err := privateNewMetricsClient(endpointURL, opts)
if err != nil {
return nil, err
}
return clnt, nil
}

// NewMetricsClient - instantiate minio metrics client honoring Prometheus format
// Deprecated: please use NewMetricsClientWithOptions
func NewMetricsClient(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*MetricsClient, error) {
return NewMetricsClientWithOptions(endpoint, &Options{
Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
Secure: secure,
})
}

// getPrometheusToken creates a JWT from MinIO access and secret keys
func getPrometheusToken(accessKey, secretKey string) (string, error) {
jwt := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, jwtgo.RegisteredClaims{
Expand All @@ -87,13 +96,19 @@ func getPrometheusToken(accessKey, secretKey string) (string, error) {
return token, nil
}

func privateNewMetricsClient(endpointURL *url.URL, jwtToken string, secure bool) (*MetricsClient, error) {
func privateNewMetricsClient(endpointURL *url.URL, opts *Options) (*MetricsClient, error) {
clnt := new(MetricsClient)
clnt.jwtToken = jwtToken
clnt.secure = secure
clnt.creds = opts.Creds
clnt.secure = opts.Secure
clnt.endpointURL = endpointURL

tr := opts.Transport
if tr == nil {
tr = DefaultTransport(opts.Secure)
}

clnt.httpClient = &http.Client{
Transport: DefaultTransport(secure),
Transport: tr,
}
return clnt, nil
}
Expand All @@ -104,7 +119,23 @@ func (client *MetricsClient) executeGetRequest(ctx context.Context, reqData metr
if err != nil {
return nil, err
}
req.Header.Add("Authorization", "Bearer "+client.jwtToken)

v, err := client.creds.Get()
if err != nil {
return nil, err
}

accessKeyID := v.AccessKeyID
secretAccessKey := v.SecretAccessKey

jwtToken, err := getPrometheusToken(accessKeyID, secretAccessKey)
if err != nil {
return nil, err
}

req.Header.Add("Authorization", "Bearer "+jwtToken)
req.Header.Set("X-Amz-Security-Token", v.SessionToken)

return client.httpClient.Do(req)
}

Expand Down Expand Up @@ -133,6 +164,7 @@ func (client *MetricsClient) makeTargetURL(r metricsRequestData) (*url.URL, erro
}

// SetCustomTransport - set new custom transport.
// Deprecated: please use Options{Transport: tr} to provide custom transport.
func (client *MetricsClient) SetCustomTransport(customHTTPTransport http.RoundTripper) {
// Set this to override default transport
// ``http.DefaultTransport``.
Expand Down
23 changes: 0 additions & 23 deletions metrics_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,29 +75,6 @@ func TestMakeTargetUrlReturnsErrorOnURLParse(t *testing.T) {
}
}

func TestPrivteNewMetricsClientInstantiatesMetricsClientWithRequiredFields(t *testing.T) {
endpointURL := &url.URL{}
jwtToken := "someToken"
secure := true

clnt, err := privateNewMetricsClient(endpointURL, jwtToken, secure)
if err != nil {
t.Errorf("error not expected, got: %v", err)
}
if clnt.endpointURL != endpointURL {
t.Errorf("clnt.endpointURL: %s not equal to endpointURL: %s", clnt.endpointURL, endpointURL)
}
if clnt.jwtToken != jwtToken {
t.Errorf("clnt.jwtToken: %s not equal to jwtToken: %s", clnt.jwtToken, jwtToken)
}
if clnt.secure != secure {
t.Errorf("clnt.secure: %v not equal to secure: %v", clnt.secure, secure)
}
if clnt.httpClient.Transport == nil {
t.Errorf("clnt.Transport expecting not nil")
}
}

func TestGetPrometheusTokenReturnsValidJwtTokenFromAccessAndSecretKey(t *testing.T) {
accessKey := "myaccessKey"
secretKey := "mysecretKey"
Expand Down

0 comments on commit ef97810

Please sign in to comment.