Skip to content

Commit fd7dc1e

Browse files
google/internal/external: Adding metadata verification
1 parent 68a41d6 commit fd7dc1e

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

google/internal/externalaccount/aws.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,29 @@ type awsRequest struct {
267267
Headers []awsRequestHeader `json:"headers"`
268268
}
269269

270+
func (cs awsCredentialSource) validateMetadataServers() error {
271+
if err := cs.validateMetadataServer(cs.RegionURL, "region_url"); err != nil {
272+
return err
273+
}
274+
if err := cs.validateMetadataServer(cs.CredVerificationURL, "url"); err != nil {
275+
return err
276+
}
277+
return cs.validateMetadataServer(cs.IMDSv2SessionTokenURL, "imdsv2_session_token_url")
278+
}
279+
280+
func (cs awsCredentialSource) validateMetadataServer(metadataUrl, urlName string) error {
281+
if metadataUrl == "" {
282+
return nil
283+
}
284+
285+
u, err := url.Parse(metadataUrl)
286+
if err == nil && (u.Hostname() == "169.254.169.254" || u.Hostname() == "fd00:ec2::254") {
287+
return nil
288+
}
289+
290+
return errors.New("oauth2/google: invalid hostname " + metadataUrl + " for " + urlName)
291+
}
292+
270293
func (cs awsCredentialSource) doRequest(req *http.Request) (*http.Response, error) {
271294
if cs.client == nil {
272295
cs.client = oauth2.NewClient(cs.ctx, nil)

google/internal/externalaccount/aws_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ func (server *testAwsServer) getCredentialSource(url string) CredentialSource {
501501
RegionURL: url + server.regionURL,
502502
RegionalCredVerificationURL: server.regionalCredVerificationURL,
503503
IMDSv2SessionTokenURL: url + server.imdsv2SessionTokenUrl,
504+
skipValidation: true, // We need to use non-amazon URLs for testing, which will fail validation.
504505
}
505506
}
506507

@@ -1025,3 +1026,88 @@ func TestAWSCredential_RequestWithBadFinalCredentialURL(t *testing.T) {
10251026
t.Errorf("subjectToken = %q, want %q", got, want)
10261027
}
10271028
}
1029+
1030+
func TestAWSCredential_Validations(t *testing.T) {
1031+
var metadataServerValidityTests = []struct {
1032+
name string
1033+
credSource CredentialSource
1034+
errText string
1035+
}{
1036+
{
1037+
name: "No Metadata Server URLs",
1038+
credSource: CredentialSource{
1039+
EnvironmentID: "aws1",
1040+
RegionURL: "",
1041+
URL: "",
1042+
IMDSv2SessionTokenURL: "",
1043+
},
1044+
}, {
1045+
name: "IPv4 Metadata Server URLs",
1046+
credSource: CredentialSource{
1047+
EnvironmentID: "aws1",
1048+
RegionURL: "http://169.254.169.254/latest/meta-data/placement/availability-zone",
1049+
URL: "http://169.254.169.254/latest/meta-data/iam/security-credentials",
1050+
IMDSv2SessionTokenURL: "http://169.254.169.254/latest/api/token",
1051+
},
1052+
}, {
1053+
name: "IPv6 Metadata Server URLs",
1054+
credSource: CredentialSource{
1055+
EnvironmentID: "aws1",
1056+
RegionURL: "http://[fd00:ec2::254]/latest/meta-data/placement/availability-zone",
1057+
URL: "http://[fd00:ec2::254]/latest/meta-data/iam/security-credentials",
1058+
IMDSv2SessionTokenURL: "http://[fd00:ec2::254]/latest/api/token",
1059+
},
1060+
}, {
1061+
name: "Faulty RegionURL",
1062+
credSource: CredentialSource{
1063+
EnvironmentID: "aws1",
1064+
RegionURL: "http://abc.com/latest/meta-data/placement/availability-zone",
1065+
URL: "http://169.254.169.254/latest/meta-data/iam/security-credentials",
1066+
IMDSv2SessionTokenURL: "http://169.254.169.254/latest/api/token",
1067+
},
1068+
errText: "oauth2/google: invalid hostname http://abc.com/latest/meta-data/placement/availability-zone for region_url",
1069+
}, {
1070+
name: "Faulty CredVerificationURL",
1071+
credSource: CredentialSource{
1072+
EnvironmentID: "aws1",
1073+
RegionURL: "http://169.254.169.254/latest/meta-data/placement/availability-zone",
1074+
URL: "http://abc.com/latest/meta-data/iam/security-credentials",
1075+
IMDSv2SessionTokenURL: "http://169.254.169.254/latest/api/token",
1076+
},
1077+
errText: "oauth2/google: invalid hostname http://abc.com/latest/meta-data/iam/security-credentials for url",
1078+
}, {
1079+
name: "Faulty IMDSv2SessionTokenURL",
1080+
credSource: CredentialSource{
1081+
EnvironmentID: "aws1",
1082+
RegionURL: "http://169.254.169.254/latest/meta-data/placement/availability-zone",
1083+
URL: "http://169.254.169.254/latest/meta-data/iam/security-credentials",
1084+
IMDSv2SessionTokenURL: "http://abc.com/latest/api/token",
1085+
},
1086+
errText: "oauth2/google: invalid hostname http://abc.com/latest/api/token for imdsv2_session_token_url",
1087+
},
1088+
}
1089+
1090+
for _, tt := range metadataServerValidityTests {
1091+
t.Run(tt.name, func(t *testing.T) {
1092+
tfc := testFileConfig
1093+
tfc.CredentialSource = tt.credSource
1094+
1095+
oldGetenv := getenv
1096+
defer func() { getenv = oldGetenv }()
1097+
getenv = setEnvironment(map[string]string{})
1098+
1099+
_, err := tfc.parse(context.Background())
1100+
if err != nil {
1101+
if tt.errText == "" {
1102+
t.Errorf("Didn't expect an error, but got %v", err)
1103+
} else if tt.errText != err.Error() {
1104+
t.Errorf("Expected %v, but got %v", tt.errText, err)
1105+
}
1106+
} else {
1107+
if tt.errText != "" {
1108+
t.Errorf("Expected error %v, but got none", tt.errText)
1109+
}
1110+
}
1111+
})
1112+
}
1113+
}

google/internal/externalaccount/basecredentials.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ type CredentialSource struct {
185185
CredVerificationURL string `json:"cred_verification_url"`
186186
IMDSv2SessionTokenURL string `json:"imdsv2_session_token_url"`
187187
Format format `json:"format"`
188+
189+
skipValidation bool
188190
}
189191

190192
type ExecutableConfig struct {
@@ -213,6 +215,12 @@ func (c *Config) parse(ctx context.Context) (baseCredentialSource, error) {
213215
awsCredSource.IMDSv2SessionTokenURL = c.CredentialSource.IMDSv2SessionTokenURL
214216
}
215217

218+
if !c.CredentialSource.skipValidation {
219+
if err := awsCredSource.validateMetadataServers(); err != nil {
220+
return nil, err
221+
}
222+
}
223+
216224
return awsCredSource, nil
217225
}
218226
} else if c.CredentialSource.File != "" {

0 commit comments

Comments
 (0)