mirror of
https://github.com/hanzos3/go.git
synced 2026-10-30 19:54:46 +00:00
422 lines
11 KiB
Go
422 lines
11 KiB
Go
//go:build !windows
|
|
|
|
/*
|
|
* MinIO Go Library for Amazon S3 Compatible Cloud Storage
|
|
* Copyright 2017 MinIO, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package credentials
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
const credsRespTmpl = `{
|
|
"Code": "Success",
|
|
"Type": "AWS-HMAC",
|
|
"AccessKeyId" : "accessKey",
|
|
"SecretAccessKey" : "secret",
|
|
"Token" : "token",
|
|
"Expiration" : "%s",
|
|
"LastUpdated" : "2009-11-23T0:00:00Z"
|
|
}`
|
|
|
|
const credsFailRespTmpl = `{
|
|
"Code": "ErrorCode",
|
|
"Message": "ErrorMsg",
|
|
"LastUpdated": "2009-11-23T0:00:00Z"
|
|
}`
|
|
|
|
const credsRespEcsTaskTmpl = `{
|
|
"AccessKeyId" : "accessKey",
|
|
"SecretAccessKey" : "secret",
|
|
"Token" : "token",
|
|
"Expiration" : "%s"
|
|
}`
|
|
|
|
const credsRespStsImpl = `<AssumeRoleWithWebIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
|
|
<AssumeRoleWithWebIdentityResult>
|
|
<SubjectFromWebIdentityToken>amzn1.account.AF6RHO7KZU5XRVQJGXK6HB56KR2A</SubjectFromWebIdentityToken>
|
|
<Audience>[email protected]</Audience>
|
|
<AssumedRoleUser>
|
|
<Arn>arn:aws:sts::123456789012:assumed-role/FederatedWebIdentityRole/app1</Arn>
|
|
<AssumedRoleId>AROACLKWSDQRAOEXAMPLE:app1</AssumedRoleId>
|
|
</AssumedRoleUser>
|
|
<Credentials>
|
|
<SessionToken>token</SessionToken>
|
|
<SecretAccessKey>secret</SecretAccessKey>
|
|
<Expiration>%s</Expiration>
|
|
<AccessKeyId>accessKey</AccessKeyId>
|
|
</Credentials>
|
|
<Provider>www.amazon.com</Provider>
|
|
</AssumeRoleWithWebIdentityResult>
|
|
<ResponseMetadata>
|
|
<RequestId>ad4156e9-bce1-11e2-82e6-6b6efEXAMPLE</RequestId>
|
|
</ResponseMetadata>
|
|
</AssumeRoleWithWebIdentityResponse>`
|
|
|
|
func initTestFailServer() *httptest.Server {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
http.Error(w, "Not allowed", http.StatusBadRequest)
|
|
}))
|
|
return server
|
|
}
|
|
|
|
func initTestServerNoRoles() *httptest.Server {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.Write([]byte(""))
|
|
}))
|
|
return server
|
|
}
|
|
|
|
// Instance Metadata Service with V1 disabled.
|
|
func initIMDSv2Server(expireOn string, failAssume bool) *httptest.Server {
|
|
imdsToken := "IMDSTokenabc123=="
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
fmt.Println(r.URL.Path)
|
|
fmt.Println(r.Method)
|
|
if r.URL.Path == "/latest/api/token" && r.Method == "PUT" {
|
|
ttlHeader := r.Header.Get("X-aws-ec2-metadata-token-ttl-seconds")
|
|
ttl, err := strconv.ParseInt(ttlHeader, 10, 32)
|
|
if err != nil || ttl < 0 || ttl > 21600 {
|
|
http.Error(w, "", http.StatusBadRequest)
|
|
return
|
|
}
|
|
w.Header().Set("X-Aws-Ec2-Metadata-Token-Ttl-Seconds", ttlHeader)
|
|
w.Write([]byte(imdsToken))
|
|
return
|
|
}
|
|
token := r.Header.Get("X-aws-ec2-metadata-token")
|
|
if token != imdsToken {
|
|
http.Error(w, r.URL.Path, http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
switch r.URL.Path {
|
|
case "/latest/meta-data/iam/security-credentials/":
|
|
fmt.Fprintln(w, "RoleName")
|
|
case "/latest/meta-data/iam/security-credentials/RoleName":
|
|
if failAssume {
|
|
fmt.Fprint(w, credsFailRespTmpl)
|
|
} else {
|
|
fmt.Fprintf(w, credsRespTmpl, expireOn)
|
|
}
|
|
default:
|
|
http.Error(w, "bad request", http.StatusBadRequest)
|
|
}
|
|
}))
|
|
return server
|
|
}
|
|
|
|
func initEcsTaskTestServer(expireOn string) *httptest.Server {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
fmt.Fprintf(w, credsRespEcsTaskTmpl, expireOn)
|
|
}))
|
|
|
|
return server
|
|
}
|
|
|
|
func initStsTestServer(expireOn string) *httptest.Server {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
required := []string{"RoleArn", "RoleSessionName", "WebIdentityToken", "Version"}
|
|
for _, field := range required {
|
|
if _, ok := r.Form[field]; !ok {
|
|
http.Error(w, fmt.Sprintf("%s missing", field), http.StatusBadRequest)
|
|
return
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(w, credsRespStsImpl, expireOn)
|
|
}))
|
|
|
|
return server
|
|
}
|
|
|
|
func TestIAMMalformedEndpoint(t *testing.T) {
|
|
creds := NewIAM("%%%%")
|
|
_, err := creds.GetWithContext(defaultCredContext)
|
|
if err == nil {
|
|
t.Fatal("Unexpected should fail here")
|
|
}
|
|
}
|
|
|
|
func TestIAMFailServer(t *testing.T) {
|
|
server := initTestFailServer()
|
|
defer server.Close()
|
|
|
|
creds := NewIAM(server.URL)
|
|
|
|
_, err := creds.GetWithContext(defaultCredContext)
|
|
if err == nil {
|
|
t.Fatal("Unexpected should fail here")
|
|
}
|
|
if err.Error() != "400 Bad Request" {
|
|
t.Fatalf("Expected '400 Bad Request', got %s", err)
|
|
}
|
|
}
|
|
|
|
func TestIAMNoRoles(t *testing.T) {
|
|
server := initTestServerNoRoles()
|
|
defer server.Close()
|
|
|
|
creds := NewIAM(server.URL)
|
|
_, err := creds.GetWithContext(defaultCredContext)
|
|
if err == nil {
|
|
t.Fatal("Unexpected should fail here")
|
|
}
|
|
if err.Error() != "No IAM roles attached to this EC2 service" {
|
|
t.Fatalf("Expected 'No IAM roles attached to this EC2 service', got %s", err)
|
|
}
|
|
}
|
|
|
|
func TestIAM(t *testing.T) {
|
|
server := initIMDSv2Server("2014-12-16T01:51:37Z", false)
|
|
defer server.Close()
|
|
|
|
p := &IAM{
|
|
Endpoint: server.URL,
|
|
}
|
|
|
|
creds, err := p.RetrieveWithCredContext(defaultCredContext)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if creds.AccessKeyID != "accessKey" {
|
|
t.Errorf("Expected \"accessKey\", got %s", creds.AccessKeyID)
|
|
}
|
|
|
|
if creds.SecretAccessKey != "secret" {
|
|
t.Errorf("Expected \"secret\", got %s", creds.SecretAccessKey)
|
|
}
|
|
|
|
if creds.SessionToken != "token" {
|
|
t.Errorf("Expected \"token\", got %s", creds.SessionToken)
|
|
}
|
|
|
|
if !p.IsExpired() {
|
|
t.Error("Expected creds to be expired.")
|
|
}
|
|
}
|
|
|
|
func TestIAMFailAssume(t *testing.T) {
|
|
server := initIMDSv2Server("2014-12-16T01:51:37Z", true)
|
|
defer server.Close()
|
|
|
|
p := &IAM{
|
|
Endpoint: server.URL,
|
|
}
|
|
|
|
_, err := p.RetrieveWithCredContext(defaultCredContext)
|
|
if err == nil {
|
|
t.Fatal("Unexpected success, should fail")
|
|
}
|
|
if err.Error() != "ErrorMsg" {
|
|
t.Errorf("Expected \"ErrorMsg\", got %s", err)
|
|
}
|
|
}
|
|
|
|
func TestIAMIsExpired(t *testing.T) {
|
|
server := initIMDSv2Server("2014-12-16T01:51:37Z", false)
|
|
defer server.Close()
|
|
|
|
p := &IAM{
|
|
Endpoint: server.URL,
|
|
}
|
|
p.CurrentTime = func() time.Time {
|
|
return time.Date(2014, 12, 15, 21, 26, 0, 0, time.UTC)
|
|
}
|
|
|
|
if !p.IsExpired() {
|
|
t.Error("Expected creds to be expired before retrieve.")
|
|
}
|
|
|
|
_, err := p.RetrieveWithCredContext(defaultCredContext)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if p.IsExpired() {
|
|
t.Error("Expected creds to not be expired after retrieve.")
|
|
}
|
|
|
|
p.CurrentTime = func() time.Time {
|
|
return time.Date(3014, 12, 15, 21, 26, 0, 0, time.UTC)
|
|
}
|
|
|
|
if !p.IsExpired() {
|
|
t.Error("Expected creds to be expired when curren time has changed")
|
|
}
|
|
}
|
|
|
|
func TestEcsTask(t *testing.T) {
|
|
server := initEcsTaskTestServer("2014-12-16T01:51:37Z")
|
|
defer server.Close()
|
|
p := &IAM{
|
|
Endpoint: server.URL,
|
|
}
|
|
t.Setenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", "/v2/credentials?id=task_credential_id")
|
|
creds, err := p.RetrieveWithCredContext(defaultCredContext)
|
|
os.Unsetenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")
|
|
if err != nil {
|
|
t.Errorf("Unexpected failure %s", err)
|
|
}
|
|
if creds.AccessKeyID != "accessKey" {
|
|
t.Errorf("Expected \"accessKey\", got %s", creds.AccessKeyID)
|
|
}
|
|
|
|
if creds.SecretAccessKey != "secret" {
|
|
t.Errorf("Expected \"secret\", got %s", creds.SecretAccessKey)
|
|
}
|
|
|
|
if creds.SessionToken != "token" {
|
|
t.Errorf("Expected \"token\", got %s", creds.SessionToken)
|
|
}
|
|
|
|
if !p.IsExpired() {
|
|
t.Error("Expected creds to be expired.")
|
|
}
|
|
}
|
|
|
|
func TestEcsTaskFullURI(t *testing.T) {
|
|
server := initEcsTaskTestServer("2014-12-16T01:51:37Z")
|
|
defer server.Close()
|
|
p := &IAM{}
|
|
t.Setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI",
|
|
fmt.Sprintf("%s%s", server.URL, "/v2/credentials?id=task_credential_id"))
|
|
creds, err := p.RetrieveWithCredContext(defaultCredContext)
|
|
os.Unsetenv("AWS_CONTAINER_CREDENTIALS_FULL_URI")
|
|
if err != nil {
|
|
t.Errorf("Unexpected failure %s", err)
|
|
}
|
|
if creds.AccessKeyID != "accessKey" {
|
|
t.Errorf("Expected \"accessKey\", got %s", creds.AccessKeyID)
|
|
}
|
|
|
|
if creds.SecretAccessKey != "secret" {
|
|
t.Errorf("Expected \"secret\", got %s", creds.SecretAccessKey)
|
|
}
|
|
|
|
if creds.SessionToken != "token" {
|
|
t.Errorf("Expected \"token\", got %s", creds.SessionToken)
|
|
}
|
|
|
|
if !p.IsExpired() {
|
|
t.Error("Expected creds to be expired.")
|
|
}
|
|
}
|
|
|
|
func TestSts(t *testing.T) {
|
|
server := initStsTestServer("2014-12-16T01:51:37Z")
|
|
defer server.Close()
|
|
p := &IAM{
|
|
Endpoint: server.URL,
|
|
}
|
|
|
|
f, err := os.CreateTemp(t.TempDir(), "minio-go")
|
|
if err != nil {
|
|
t.Errorf("Unexpected failure %s", err)
|
|
}
|
|
defer os.Remove(f.Name())
|
|
f.Write([]byte("token"))
|
|
f.Close()
|
|
|
|
t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", f.Name())
|
|
t.Setenv("AWS_ROLE_ARN", "arn:aws:sts::123456789012:assumed-role/FederatedWebIdentityRole/app1")
|
|
creds, err := p.RetrieveWithCredContext(defaultCredContext)
|
|
os.Unsetenv("AWS_WEB_IDENTITY_TOKEN_FILE")
|
|
os.Unsetenv("AWS_ROLE_ARN")
|
|
if err != nil {
|
|
t.Errorf("Unexpected failure %s", err)
|
|
}
|
|
if creds.AccessKeyID != "accessKey" {
|
|
t.Errorf("Expected \"accessKey\", got %s", creds.AccessKeyID)
|
|
}
|
|
|
|
if creds.SecretAccessKey != "secret" {
|
|
t.Errorf("Expected \"secret\", got %s", creds.SecretAccessKey)
|
|
}
|
|
|
|
if creds.SessionToken != "token" {
|
|
t.Errorf("Expected \"token\", got %s", creds.SessionToken)
|
|
}
|
|
|
|
if !p.IsExpired() {
|
|
t.Error("Expected creds to be expired.")
|
|
}
|
|
}
|
|
|
|
func TestStsCn(t *testing.T) {
|
|
server := initStsTestServer("2014-12-16T01:51:37Z")
|
|
defer server.Close()
|
|
p := &IAM{
|
|
Endpoint: server.URL,
|
|
}
|
|
|
|
f, err := os.CreateTemp(t.TempDir(), "minio-go")
|
|
if err != nil {
|
|
t.Errorf("Unexpected failure %s", err)
|
|
}
|
|
defer os.Remove(f.Name())
|
|
f.Write([]byte("token"))
|
|
f.Close()
|
|
|
|
t.Setenv("AWS_REGION", "cn-northwest-1")
|
|
t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", f.Name())
|
|
t.Setenv("AWS_ROLE_ARN", "arn:aws:sts::123456789012:assumed-role/FederatedWebIdentityRole/app1")
|
|
creds, err := p.RetrieveWithCredContext(defaultCredContext)
|
|
os.Unsetenv("AWS_WEB_IDENTITY_TOKEN_FILE")
|
|
os.Unsetenv("AWS_ROLE_ARN")
|
|
if err != nil {
|
|
t.Errorf("Unexpected failure %s", err)
|
|
}
|
|
if creds.AccessKeyID != "accessKey" {
|
|
t.Errorf("Expected \"accessKey\", got %s", creds.AccessKeyID)
|
|
}
|
|
|
|
if creds.SecretAccessKey != "secret" {
|
|
t.Errorf("Expected \"secret\", got %s", creds.SecretAccessKey)
|
|
}
|
|
|
|
if creds.SessionToken != "token" {
|
|
t.Errorf("Expected \"token\", got %s", creds.SessionToken)
|
|
}
|
|
|
|
if !p.IsExpired() {
|
|
t.Error("Expected creds to be expired.")
|
|
}
|
|
}
|
|
|
|
func TestIMDSv1Blocked(t *testing.T) {
|
|
server := initIMDSv2Server("2014-12-16T01:51:37Z", false)
|
|
p := &IAM{
|
|
Endpoint: server.URL,
|
|
}
|
|
_, err := p.RetrieveWithCredContext(defaultCredContext)
|
|
if err != nil {
|
|
t.Errorf("Unexpected IMDSv2 failure %s", err)
|
|
}
|
|
}
|