diff --git a/api/client.go b/api/client.go index 58ac61bc86..2ef4667bd9 100644 --- a/api/client.go +++ b/api/client.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "io" + "net/http" "path" "strings" "time" @@ -288,6 +289,7 @@ type ConsoleCredentialsI interface { type ConsoleCredentials struct { ConsoleCredentials *credentials.Credentials AccountAccessKey string + CredContext *credentials.CredContext } func (c ConsoleCredentials) GetAccountAccessKey() string { @@ -296,7 +298,7 @@ func (c ConsoleCredentials) GetAccountAccessKey() string { // Get implements *Login.Get() func (c ConsoleCredentials) Get() (credentials.Value, error) { - return c.ConsoleCredentials.Get() + return c.ConsoleCredentials.GetWithContext(c.CredContext) } // Expire implements *Login.Expire() @@ -311,6 +313,10 @@ type consoleSTSAssumeRole struct { stsAssumeRole *credentials.STSAssumeRole } +func (s consoleSTSAssumeRole) RetrieveWithCredContext(cc *credentials.CredContext) (credentials.Value, error) { + return s.stsAssumeRole.RetrieveWithCredContext(cc) +} + func (s consoleSTSAssumeRole) Retrieve() (credentials.Value, error) { return s.stsAssumeRole.Retrieve() } @@ -319,7 +325,7 @@ func (s consoleSTSAssumeRole) IsExpired() bool { return s.stsAssumeRole.IsExpired() } -func stsCredentials(minioURL, accessKey, secretKey, location, clientIP string) (*credentials.Credentials, error) { +func stsCredentials(minioURL, accessKey, secretKey, location string, client *http.Client) (*credentials.Credentials, error) { if accessKey == "" || secretKey == "" { return nil, errors.New("credentials endpoint, access and secret key are mandatory for AssumeRoleSTS") } @@ -330,7 +336,7 @@ func stsCredentials(minioURL, accessKey, secretKey, location, clientIP string) ( DurationSeconds: int(xjwt.GetConsoleSTSDuration().Seconds()), } stsAssumeRole := &credentials.STSAssumeRole{ - Client: GetConsoleHTTPClient(clientIP), + Client: client, STSEndpoint: minioURL, Options: opts, } @@ -338,51 +344,48 @@ func stsCredentials(minioURL, accessKey, secretKey, location, clientIP string) ( return credentials.New(consoleSTSWrapper), nil } -func NewConsoleCredentials(accessKey, secretKey, location, clientIP string) (*credentials.Credentials, error) { +func NewConsoleCredentials(accessKey, secretKey, location string, client *http.Client) (*credentials.Credentials, error) { minioURL := getMinIOServer() - // Future authentication methods can be added under this switch statement - switch { // LDAP authentication for Console - case ldap.GetLDAPEnabled(): - { - creds, err := auth.GetCredentialsFromLDAP(GetConsoleHTTPClient(clientIP), minioURL, accessKey, secretKey) - if err != nil { - return nil, err - } + if ldap.GetLDAPEnabled() { + creds, err := auth.GetCredentialsFromLDAP(client, minioURL, accessKey, secretKey) + if err != nil { + return nil, err + } - // We verify if LDAP credentials are correct and no error is returned - _, err = creds.Get() + credContext := &credentials.CredContext{ + Client: client, + } - if err != nil && strings.Contains(strings.ToLower(err.Error()), "not found") { - // We try to use STS Credentials in case LDAP credentials are incorrect. - stsCreds, errSTS := stsCredentials(minioURL, accessKey, secretKey, location, clientIP) + // We verify if LDAP credentials are correct and no error is returned + _, err = creds.GetWithContext(credContext) - // If there is an error with STS too, then we return the original LDAP error - if errSTS != nil { - LogError("error in STS credentials for LDAP case: %v ", errSTS) + if err != nil && strings.Contains(strings.ToLower(err.Error()), "not found") { + // We try to use STS Credentials in case LDAP credentials are incorrect. + stsCreds, errSTS := stsCredentials(minioURL, accessKey, secretKey, location, client) - // We return LDAP result - return creds, nil - } + // If there is an error with STS too, then we return the original LDAP error + if errSTS != nil { + LogError("error in STS credentials for LDAP case: %v ", errSTS) - _, err := stsCreds.Get() - // There is an error with STS credentials, We return the result of LDAP as STS is not a priority in this case. - if err != nil { - return creds, nil - } + // We return LDAP result + return creds, nil + } - return stsCreds, nil + _, err := stsCreds.GetWithContext(credContext) + // There is an error with STS credentials, We return the result of LDAP as STS is not a priority in this case. + if err != nil { + return creds, nil } - return creds, nil - } - // default authentication for Console is via STS (Security Token Service) against MinIO - default: - { - return stsCredentials(minioURL, accessKey, secretKey, location, clientIP) + return stsCreds, nil } + + return creds, nil } + + return stsCredentials(minioURL, accessKey, secretKey, location, client) } // getConsoleCredentialsFromSession returns the *consoleCredentials.Login associated to the diff --git a/api/user_account.go b/api/user_account.go index 00da0c0430..95b034d4b5 100644 --- a/api/user_account.go +++ b/api/user_account.go @@ -58,6 +58,7 @@ func getChangePasswordResponse(session *models.Principal, params accountApi.Acco ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) defer cancel() clientIP := getClientIP(params.HTTPRequest) + client := GetConsoleHTTPClient(clientIP) // changePassword operations requires an AdminClient initialized with parent account credentials not // STS credentials @@ -79,7 +80,7 @@ func getChangePasswordResponse(session *models.Principal, params accountApi.Acco } // user credentials are updated at this point, we need to generate a new admin client and authenticate using // the new credentials - credentials, err := getConsoleCredentials(accessKey, newSecretKey, clientIP) + credentials, err := getConsoleCredentials(accessKey, newSecretKey, client) if err != nil { return nil, ErrorWithContext(ctx, ErrInvalidLogin, nil, err) } diff --git a/api/user_buckets.go b/api/user_buckets.go index 0607fd0eb6..f95cd47af1 100644 --- a/api/user_buckets.go +++ b/api/user_buckets.go @@ -21,6 +21,7 @@ import ( "encoding/json" "errors" "fmt" + "net/http" "strings" "time" @@ -29,6 +30,7 @@ import ( "github.com/minio/madmin-go/v3" "github.com/minio/mc/cmd" "github.com/minio/mc/pkg/probe" + "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/sse" "github.com/minio/minio-go/v7/pkg/tags" @@ -1067,8 +1069,7 @@ func getMaxShareLinkExpirationResponse(session *models.Principal, params bucketA // getMaxShareLinkExpirationSeconds returns the max share link expiration time in seconds which is the sts token expiration time func getMaxShareLinkExpirationSeconds(session *models.Principal) (int64, error) { creds := getConsoleCredentialsFromSession(session) - - val, err := creds.Get() + val, err := creds.GetWithContext(&credentials.CredContext{Client: http.DefaultClient}) if err != nil { return 0, err } diff --git a/api/user_login.go b/api/user_login.go index ec0dd6d98f..9f6b3aae56 100644 --- a/api/user_login.go +++ b/api/user_login.go @@ -20,15 +20,10 @@ import ( "context" "encoding/base64" "encoding/json" - stderrors "errors" "fmt" - "net" "net/http" - "net/url" "strings" - "github.com/go-openapi/errors" - "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/minio/console/api/operations" @@ -39,6 +34,7 @@ import ( "github.com/minio/madmin-go/v3" "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/pkg/v3/env" + xnet "github.com/minio/pkg/v3/net" ) func registerLoginHandlers(api *operations.ConsoleAPI) { @@ -114,14 +110,17 @@ func getAccountInfo(ctx context.Context, client MinioAdmin) (*madmin.AccountInfo } // getConsoleCredentials will return ConsoleCredentials interface -func getConsoleCredentials(accessKey, secretKey, clientIP string) (*ConsoleCredentials, error) { - creds, err := NewConsoleCredentials(accessKey, secretKey, GetMinIORegion(), clientIP) +func getConsoleCredentials(accessKey, secretKey string, client *http.Client) (*ConsoleCredentials, error) { + creds, err := NewConsoleCredentials(accessKey, secretKey, GetMinIORegion(), client) if err != nil { return nil, err } return &ConsoleCredentials{ ConsoleCredentials: creds, AccountAccessKey: accessKey, + CredContext: &credentials.CredContext{ + Client: client, + }, }, nil } @@ -130,25 +129,24 @@ func getLoginResponse(params authApi.LoginParams) (*models.LoginResponse, *Coded ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) defer cancel() lr := params.Body + + clientIP := getClientIP(params.HTTPRequest) + client := GetConsoleHTTPClient(clientIP) + var err error var consoleCreds *ConsoleCredentials // if we receive an STS we use that instead of the credentials if lr.Sts != "" { - creds := credentials.NewStaticV4(lr.AccessKey, lr.SecretKey, lr.Sts) consoleCreds = &ConsoleCredentials{ - ConsoleCredentials: creds, + ConsoleCredentials: credentials.NewStaticV4(lr.AccessKey, lr.SecretKey, lr.Sts), AccountAccessKey: lr.AccessKey, - } - - credsVerificate, _ := creds.Get() - - if credsVerificate.SessionToken == "" || credsVerificate.SecretAccessKey == "" || credsVerificate.AccessKeyID == "" { - return nil, ErrorWithContext(ctx, errors.New(401, "Invalid STS Params")) + CredContext: &credentials.CredContext{ + Client: client, + }, } } else { - clientIP := getClientIP(params.HTTPRequest) // prepare console credentials - consoleCreds, err = getConsoleCredentials(lr.AccessKey, lr.SecretKey, clientIP) + consoleCreds, err = getConsoleCredentials(lr.AccessKey, lr.SecretKey, client) if err != nil { return nil, ErrorWithContext(ctx, err, ErrInvalidLogin) } @@ -160,11 +158,8 @@ func getLoginResponse(params authApi.LoginParams) (*models.LoginResponse, *Coded } sessionID, err := login(consoleCreds, sf) if err != nil { - var urlErr *url.Error - if stderrors.As(err, &urlErr) { - if _, isNetErr := urlErr.Err.(net.Error); isNetErr { - return nil, ErrorWithContext(ctx, ErrNetworkError) - } + if xnet.IsNetworkOrHostDown(err, true) { + return nil, ErrorWithContext(ctx, ErrNetworkError) } return nil, ErrorWithContext(ctx, err, ErrInvalidLogin) } @@ -265,6 +260,7 @@ func getLoginOauth2AuthResponse(params authApi.LoginOauth2AuthParams, openIDProv r := params.HTTPRequest lr := params.Body + client := GetConsoleHTTPClient(getClientIP(params.HTTPRequest)) if len(openIDProviders) > 0 { // we read state rState := *lr.State @@ -288,8 +284,7 @@ func getLoginOauth2AuthResponse(params authApi.LoginOauth2AuthParams, openIDProv } // Initialize new identity provider with new oauth2Client per IDPName - oauth2Client, err := providerCfg.GetOauth2Provider(IDPName, nil, r, - GetConsoleHTTPClient(getClientIP(params.HTTPRequest))) + oauth2Client, err := providerCfg.GetOauth2Provider(IDPName, nil, r, client) if err != nil { return nil, ErrorWithContext(ctx, err) } @@ -309,6 +304,7 @@ func getLoginOauth2AuthResponse(params authApi.LoginOauth2AuthParams, openIDProv token, err := login(&ConsoleCredentials{ ConsoleCredentials: userCredentials, AccountAccessKey: "", + CredContext: &credentials.CredContext{Client: client}, }, nil) if err != nil { return nil, ErrorWithContext(ctx, err) diff --git a/go.mod b/go.mod index 63fa42b459..55e8b11688 100644 --- a/go.mod +++ b/go.mod @@ -21,9 +21,9 @@ require ( github.com/minio/cli v1.24.2 github.com/minio/highwayhash v1.0.3 github.com/minio/kes v0.23.0 - github.com/minio/madmin-go/v3 v3.0.81 + github.com/minio/madmin-go/v3 v3.0.85 github.com/minio/mc v0.0.0-20241215225040-f4dd5e4a07ff - github.com/minio/minio-go/v7 v7.0.82 + github.com/minio/minio-go/v7 v7.0.83-0.20241230094935-5757f2c8544a github.com/minio/selfupdate v0.6.0 github.com/minio/websocket v1.6.0 github.com/mitchellh/go-homedir v1.1.0 @@ -33,7 +33,7 @@ require ( github.com/tidwall/gjson v1.17.3 // indirect github.com/unrolled/secure v1.15.0 golang.org/x/crypto v0.31.0 - golang.org/x/net v0.32.0 + golang.org/x/net v0.33.0 golang.org/x/oauth2 v0.24.0 // Added to include security fix for // https://github.com/golang/go/issues/56152 @@ -41,7 +41,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect ) -require github.com/minio/pkg/v3 v3.0.24 +require github.com/minio/pkg/v3 v3.0.25 require ( aead.dev/mem v0.2.0 // indirect diff --git a/go.sum b/go.sum index 7a57e3f779..31c9221409 100644 --- a/go.sum +++ b/go.sum @@ -179,18 +179,18 @@ github.com/minio/kes v0.23.0 h1:T0zHtyDoI3JdKrVvzdM4xwVryYYyh5pKwNUVBoqxsNs= github.com/minio/kes v0.23.0/go.mod h1:vvXVGcgu9mYLkbVWlEvFFl6bYR196RQlOU2Q+rHApl8= github.com/minio/kes-go v0.2.1 h1:KnqS+p6xoSFJZbQhmJaz/PbxeA6nQyRqT/ywrn5lU2o= github.com/minio/kes-go v0.2.1/go.mod h1:76xf7l41Wrh+IifisABXK2S8uZWYgWV1IGBKC3GdOJk= -github.com/minio/madmin-go/v3 v3.0.81 h1:sEGhX3gEHciUT6H5O2qyOJ4Nr31vssQUikDcygMcPms= -github.com/minio/madmin-go/v3 v3.0.81/go.mod h1:QAZPX3xx4gdZbZ8t85SieFSwXMOQhFx7bVjldhyc6Bk= +github.com/minio/madmin-go/v3 v3.0.85 h1:bP63oKd5YclvjuUw58BtE8cME0VAoZwvwUV50lEvES4= +github.com/minio/madmin-go/v3 v3.0.85/go.mod h1:pMLdj9OtN0CANNs5tdm6opvOlDFfj0WhbztboZAjRWE= github.com/minio/mc v0.0.0-20241215225040-f4dd5e4a07ff h1:KOiKIGERKan7dcg8T9hSFj1/DFSw3X1r7p+NFGFsGBo= github.com/minio/mc v0.0.0-20241215225040-f4dd5e4a07ff/go.mod h1:kKjtUlsNcehsP5f2ji9SicURHyTdlZ9kY2/sCwHKOVk= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.82 h1:tWfICLhmp2aFPXL8Tli0XDTHj2VB/fNf0PC1f/i1gRo= -github.com/minio/minio-go/v7 v7.0.82/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= +github.com/minio/minio-go/v7 v7.0.83-0.20241230094935-5757f2c8544a h1:nPw29aor4WGYpmBZy5jQT/cW5wtFrG8tEOCNeltMcq8= +github.com/minio/minio-go/v7 v7.0.83-0.20241230094935-5757f2c8544a/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY= github.com/minio/mux v1.9.0 h1:dWafQFyEfGhJvK6AwLOt83bIG5bxKxKJnKMCi0XAaoA= github.com/minio/mux v1.9.0/go.mod h1:1pAare17ZRL5GpmNL+9YmqHoWnLmMZF9C/ioUCfy0BQ= -github.com/minio/pkg/v3 v3.0.24 h1:DyaUMvPYueuEn3Tx0kDlU3qFHx/Ygfw9q/2bEp3erR8= -github.com/minio/pkg/v3 v3.0.24/go.mod h1:mIaN552nu0D2jiSk5BQC8LB25f44ytbOBJCuLtksX7Q= +github.com/minio/pkg/v3 v3.0.25 h1:bfxBcxN77uLNiI+qY4/0fxXF4lVdJulwkcJNZcvc1xg= +github.com/minio/pkg/v3 v3.0.25/go.mod h1:mIaN552nu0D2jiSk5BQC8LB25f44ytbOBJCuLtksX7Q= github.com/minio/selfupdate v0.6.0 h1:i76PgT0K5xO9+hjzKcacQtO7+MjJ4JKA8Ak8XQ9DDwU= github.com/minio/selfupdate v0.6.0/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM= github.com/minio/websocket v1.6.0 h1:CPvnQvNvlVaQmvw5gtJNyYQhg4+xRmrPNhBbv8BdpAE= @@ -326,8 +326,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go index 4208a84320..978aff518e 100644 --- a/pkg/logger/logger_test.go +++ b/pkg/logger/logger_test.go @@ -20,24 +20,18 @@ import ( "context" "fmt" "net/http" + "net/http/httptest" "os" "testing" ) -func testServer(_ http.ResponseWriter, _ *http.Request) { +type testServer struct{} + +func (t *testServer) ServeHTTP(_ http.ResponseWriter, _ *http.Request) { } func TestInitializeLogger(t *testing.T) { - testServerWillStart := make(chan interface{}) - http.HandleFunc("/", testServer) - go func() { - close(testServerWillStart) - err := http.ListenAndServe("127.0.0.1:1337", nil) - if err != nil { - return - } - }() - <-testServerWillStart + srv := httptest.NewServer(&testServer{}) // use a random port loggerWebhookEnable := fmt.Sprintf("%s_TEST", EnvLoggerWebhookEnable) loggerWebhookEndpoint := fmt.Sprintf("%s_TEST", EnvLoggerWebhookEndpoint) @@ -85,7 +79,7 @@ func TestInitializeLogger(t *testing.T) { wantErr: false, setEnvVars: func() { os.Setenv(loggerWebhookEnable, "on") - os.Setenv(loggerWebhookEndpoint, "http://127.0.0.1:1337/logger") + os.Setenv(loggerWebhookEndpoint, srv.URL+"/logger") os.Setenv(loggerWebhookAuthToken, "test") os.Setenv(loggerWebhookClientCert, "") os.Setenv(loggerWebhookClientKey, "") @@ -133,7 +127,7 @@ func TestInitializeLogger(t *testing.T) { wantErr: false, setEnvVars: func() { os.Setenv(auditWebhookEnable, "on") - os.Setenv(auditWebhookEndpoint, "http://127.0.0.1:1337/audit") + os.Setenv(auditWebhookEndpoint, srv.URL+"/audit") os.Setenv(auditWebhookAuthToken, "test") os.Setenv(auditWebhookClientCert, "") os.Setenv(auditWebhookClientKey, "")