Skip to content

Commit cd6dd9b

Browse files
authored
Merge pull request #87 from mechanical-orchard/eks-provider
Add a provider to support EKS Pod Identity
2 parents 6b7dc27 + 19ff997 commit cd6dd9b

File tree

5 files changed

+85
-2
lines changed

5 files changed

+85
-2
lines changed

src/aws_credentials_eks.erl

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
%% @doc This provider looks up credential information from EKS Pod Identity
2+
%% @end
3+
-module(aws_credentials_eks).
4+
-behaviour(aws_credentials_provider).
5+
6+
-define(AUTHORIZATION_HEADER, "authorization").
7+
8+
-export([fetch/1]).
9+
10+
-spec fetch(aws_credentials_provider:options()) ->
11+
{error, _}
12+
| {ok, aws_credentials:credentials(), aws_credentials_provider:expiration()}.
13+
fetch(_Options) ->
14+
FullUri = os:getenv("AWS_CONTAINER_CREDENTIALS_FULL_URI"),
15+
TokenFile = os:getenv("AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE"),
16+
AuthToken = read_token(TokenFile),
17+
Response = fetch_session_token(FullUri, AuthToken),
18+
make_map(Response).
19+
20+
-spec read_token(false | string()) -> {error, _} | {ok, binary()}.
21+
read_token(false) -> {error, no_credentials};
22+
read_token(Path) -> file:read_file(Path).
23+
24+
-spec fetch_session_token(false | string(), {error, _} | {ok, string()}) ->
25+
{error, _}
26+
| {ok, aws_credentials_httpc:status_code(),
27+
aws_credentials_httpc:body(),
28+
aws_credentials_httpc:headers()}.
29+
fetch_session_token(false, _AuthToken) -> {error, no_credentials};
30+
fetch_session_token(_FullUri, {error, _Error} = Error) -> Error;
31+
fetch_session_token(FullUri, {ok, AuthToken}) ->
32+
aws_credentials_httpc:request(get, FullUri, [{?AUTHORIZATION_HEADER, AuthToken}]).
33+
34+
35+
-spec make_map({error, _}
36+
| {ok, aws_credentials_httpc:status_code(),
37+
aws_credentials_httpc:body(),
38+
aws_credentials_httpc:headers()}) ->
39+
{error, _}
40+
| {ok, aws_credentials:credentials(), aws_credentials_provider:expiration()}.
41+
make_map({error, _Error} = Error) -> Error;
42+
make_map({ok, _Status, Body, _Headers}) ->
43+
#{ <<"AccessKeyId">> := AccessKeyId
44+
, <<"SecretAccessKey">> := SecretAccessKey
45+
, <<"Token">> := Token
46+
, <<"Expiration">> := Expiration
47+
} = jsx:decode(Body),
48+
Creds = aws_credentials:make_map(?MODULE, AccessKeyId, SecretAccessKey, Token),
49+
{ok, Creds, Expiration}.

src/aws_credentials_httpc.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
-type httpc_result() :: {status_line(), [header()], body()}.
2323
-type status_line() :: {http_version(), status_code(), reason_phrase()}.
24-
-type header() :: {string(), string()}.
24+
-type header() :: {string(), binary() | string()}.
2525
-type body() :: binary().
2626
-type http_version() :: string().
2727
-type status_code() :: non_neg_integer().

src/aws_credentials_provider.erl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
| aws_credentials_file
3737
| aws_credentials_ecs
3838
| aws_credentials_ec2
39+
| aws_credentials_eks
3940
| module().
4041
-type error_log() :: [{provider(), term()}].
4142
-export_type([ options/0, expiration/0, provider/0, error_log/0 ]).
@@ -48,7 +49,8 @@
4849
-define(DEFAULT_PROVIDERS, [aws_credentials_env,
4950
aws_credentials_file,
5051
aws_credentials_ecs,
51-
aws_credentials_ec2]).
52+
aws_credentials_ec2,
53+
aws_credentials_eks]).
5254

5355
-spec fetch() ->
5456
{ok, aws_credentials:credentials(), expiration()} |

test/aws_credentials_providers_SUITE.erl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ all() ->
3535
, {group, env}
3636
, {group, application_env}
3737
, {group, ecs}
38+
, {group, eks}
3839
, {group, credential_process}
3940
].
4041

@@ -48,6 +49,7 @@ groups() ->
4849
, {env, [], all_testcases()}
4950
, {application_env, [], all_testcases()}
5051
, {ecs, [], all_testcases()}
52+
, {eks, [], all_testcases()}
5153
, {credential_process, [], all_testcases()}
5254
].
5355

@@ -118,6 +120,9 @@ assert_test(profile_env) ->
118120
assert_test(credential_process) ->
119121
Provider = provider(file),
120122
assert_values(?DUMMY_ACCESS_KEY2, ?DUMMY_SECRET_ACCESS_KEY2, Provider);
123+
assert_test(eks) ->
124+
Provider = provider(eks),
125+
assert_values(?DUMMY_ACCESS_KEY, ?DUMMY_SECRET_ACCESS_KEY, Provider);
121126
assert_test(GroupName) ->
122127
Provider = provider(GroupName),
123128
assert_values(?DUMMY_ACCESS_KEY, ?DUMMY_SECRET_ACCESS_KEY, Provider).
@@ -196,6 +201,18 @@ setup_provider(ecs, _Config) ->
196201
#{ mocks => [httpc]
197202
, env => [{"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", OldUri}]
198203
};
204+
setup_provider(eks, Config) ->
205+
OldFullUri = os:getenv("AWS_CONTAINER_CREDENTIALS_FULL_URI"),
206+
OldTokenFile = os:getenv("AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE"),
207+
os:putenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "https://eks.amazonaws.com/dummy-uri"),
208+
os:putenv("AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE", ?config(data_dir, Config) ++ "eks/token"),
209+
meck:new(httpc, [no_link, passthrough]),
210+
meck:expect(httpc, request, fun mock_httpc_request_eks/5),
211+
#{ mocks => [httpc]
212+
, env => [ {"AWS_CONTAINER_CREDENTIALS_FULL_URI", OldFullUri}
213+
, {"AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE", OldTokenFile}
214+
]
215+
};
199216
setup_provider(config_env, Config) ->
200217
Old = os:getenv("AWS_CONFIG_FILE"),
201218
os:putenv("AWS_CONFIG_FILE", ?config(data_dir, Config) ++ "env/config"),
@@ -258,6 +275,14 @@ mock_httpc_request_ecs(Method, Request, HTTPOptions, Options, Profile) ->
258275
meck:passthrough([Method, Request, HTTPOptions, Options, Profile])
259276
end.
260277

278+
mock_httpc_request_eks(Method, Request, HTTPOptions, Options, Profile) ->
279+
case Request of
280+
{"https://eks.amazonaws.com/dummy-uri", [{"authorization", <<"dummy-authorization-token">>}]} ->
281+
{ok, response('eks-credentials')};
282+
_ ->
283+
meck:passthrough([Method, Request, HTTPOptions, Options, Profile])
284+
end.
285+
261286
response(BodyTag) ->
262287
StatusLine = {unused, 200, unused},
263288
Headers = [],
@@ -277,6 +302,12 @@ body('dummy-role') ->
277302
body('document') ->
278303
jsx:encode(#{ 'region' => unused });
279304
body('dummy-uri') ->
305+
jsx:encode(#{ 'AccessKeyId' => ?DUMMY_ACCESS_KEY
306+
, 'SecretAccessKey' => ?DUMMY_SECRET_ACCESS_KEY
307+
, 'Expiration' => <<"2025-09-25T23:43:56Z">>
308+
, 'Token' => unused
309+
});
310+
body('eks-credentials') ->
280311
jsx:encode(#{ 'AccessKeyId' => ?DUMMY_ACCESS_KEY
281312
, 'SecretAccessKey' => ?DUMMY_SECRET_ACCESS_KEY
282313
, 'Expiration' => <<"2025-09-25T23:43:56Z">>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dummy-authorization-token

0 commit comments

Comments
 (0)