Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d342776
Add functions to create pre-signed URLs for S3
tommysrn Mar 16, 2019
0d52b00
Add capability to generate presigned urls with a' la carte headers
lbakerchef Sep 11, 2020
73d34a5
Add capability to presign URLs with passed-in host header and date
lbakerchef Sep 10, 2020
1721f82
Add capability to generate a signature using a passed-in date
lbakerchef May 6, 2020
f59486d
Use date header in sign_v4 if available
lbakerchef May 12, 2020
8a7c0bf
More consistent treatment of host header
lbakerchef May 28, 2020
12c172d
Add iso_8601_basic_time/1 function
lbakerchef Jun 27, 2020
267647a
Fix fips issue on rhel boxes
lbakerchef Jul 20, 2020
55f9937
Add eunit tests for presigned URLs
lbakerchef Apr 3, 2020
151d0c1
add eunit tests for signature/8
lbakerchef Nov 12, 2020
c65bef6
replace rebar.lock
lbakerchef Apr 23, 2020
f59231c
Upgrade to Erlang 24x. (#4)
lbakerchef Jul 11, 2022
20d5c99
fix undefined type error.
lbakerchef Aug 9, 2022
abe2a21
fix random:uniform deprecation.
lbakerchef Aug 12, 2022
a598d3f
fix unused variable warning
lbakerchef Aug 12, 2022
eccdeec
fix warning - variable exported from case
lbakerchef Aug 12, 2022
9f8fba3
fix tests
lbakerchef Aug 12, 2022
52ff8e7
remove platform restrictions
lbakerchef Aug 25, 2022
27724cc
Normalize host header 'value' to lowercase
lbakerchef Jan 23, 2023
9b2bc15
typo in s3 mfa_delete attribute
KingBrewer Mar 30, 2023
0f5f8e2
Merge pull request #747 from KingBrewer/patch-1
motobob Mar 30, 2023
199b464
added create_secret_ functions
KingBrewer Apr 3, 2023
cdb1fb3
Added delete_secret functions
KingBrewer Apr 3, 2023
23fab25
delete_resource_policy, describe_secret, get_resource_policy, put_res…
KingBrewer Apr 4, 2023
02a5419
Merge pull request #748 from KingBrewer/sm-update
motobob Apr 4, 2023
0fa91a8
Update README.md (#749)
KingBrewer Apr 4, 2023
3d2a70f
Merge remote-tracking branch 'erlcloud/master' into ft/sigv4-presigne…
keynslug Apr 7, 2023
5ad3179
Merge remote-tracking branch 'chef/lbaker/presigned-headers' into ft/…
keynslug Apr 7, 2023
ac5b6b0
chore: update appup
keynslug Apr 7, 2023
14191cf
fix(s3): ensure host in sigv4 considers scheme + port
keynslug Apr 7, 2023
6d5d3a3
fix(s3): avoid setting text/xml on uploads by default
keynslug Apr 7, 2023
6c49552
fix(s3): uriencode canonical resource in presigned urls
keynslug Apr 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
image: erlang:${{matrix.otp_vsn}}
strategy:
matrix:
otp_vsn: [19.3, 20.3, 21.3, 22.3, 23.3, 24.0]
otp_vsn: [19.3, 20.3, 21.3, 22.3, 23.3, 24.0, 24.3]
os: [ubuntu-latest]
force_rebar2: [true, false]
env:
Expand Down
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@ endif
warnings: deps
ifeq ($(REBAR_VSN),2)
@WARNINGS_AS_ERRORS=true $(REBAR) compile
@WARNINGS_AS_ERRORS=true $(REBAR) compile_only=true eunit
@AWS_DEFAULT_REGION=us-east-1 WARNINGS_AS_ERRORS=true $(REBAR) compile_only=true eunit
else
@$(REBAR) as test compile
endif

eunit: deps
ifeq ($(REBAR_VSN),2)
$(MAKE) compile
@$(REBAR) eunit skip_deps=true
@AWS_DEFAULT_REGION=us-east-1 $(REBAR) eunit skip_deps=true
else
@ERL_FLAGS="-config $(PWD)/eunit" $(REBAR) eunit
@AWS_DEFAULT_REGION=us-east-1 ERL_FLAGS="-config $(PWD)/eunit" $(REBAR) eunit
endif

.dialyzer_plt:
Expand All @@ -75,7 +75,7 @@ endif
check: deps
ifeq ($(REBAR_VSN),2)
$(MAKE) compile
@$(REBAR) compile_only=true eunit
@AWS_DEFAULT_REGION=us-east-1 $(REBAR) compile_only=true eunit
$(MAKE) .dialyzer_plt
dialyzer --no_check_plt --fullpath \
$(CHECK_EUNIT_FILES) \
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Service APIs implemented:
- Simple Notification Service (SNS)
- Web Application Firewall (WAF)
- AWS Cost and Usage Report API
- AWS Secrets Manager
- AWS Systems Manager (SSM)
- and more to come

Expand Down
2 changes: 1 addition & 1 deletion include/erlcloud_aws.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
sts_host="sts.amazonaws.com"::string(),
s3_scheme="https://"::string(),
s3_host="s3.amazonaws.com"::string(),
s3_port=80::non_neg_integer(),
s3_port=443::non_neg_integer(),
s3_follow_redirect=false::boolean(),
s3_follow_redirect_count=2::non_neg_integer(),
%% When set to 'auto' access method is chosen
Expand Down
5 changes: 3 additions & 2 deletions rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

{erl_opts,
[debug_info,
{platform_define, "^19.|^2", 'ERLANG_OTP_VERSION_19'},

strict_validation,
warn_bif_clash,
warn_deprecated_function,
Expand All @@ -18,7 +18,8 @@

{deps, [
{jsx, "2.11.0"},
{lhttpc, "1.6.2"},
{lhttpc, ".*",
{git, "https://github.com/erlcloud/lhttpc", {tag, "1.6.2"}}},
{eini, "1.2.9"},
{base16, "1.0.0"}
]}.
Expand Down
14 changes: 10 additions & 4 deletions rebar.lock
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
{"1.1.0",
{"1.2.0",
[{<<"base16">>,{pkg,<<"base16">>,<<"1.0.0">>},0},
{<<"eini">>,{pkg,<<"eini">>,<<"1.2.9">>},0},
{<<"jsx">>,{pkg,<<"jsx">>,<<"2.11.0">>},0},
{<<"lhttpc">>,{pkg,<<"lhttpc">>,<<"1.6.2">>},0}]}.
{<<"lhttpc">>,
{git,"https://github.com/erlcloud/lhttpc",
{ref,"8e34985a3cd0ac2a7fc2a88a041554c64d33e74b"}},
0}]}.
[
{pkg_hash,[
{<<"base16">>, <<"283644E2B21BD5915ACB7178BED7851FB07C6E5749B8FAD68A53C501092176D9">>},
{<<"eini">>, <<"FCC3CBD49BBDD9A1D9735C7365DAFFCD84481CCE81E6CB80537883AA44AC4895">>},
{<<"jsx">>, <<"08154624050333919B4AC1B789667D5F4DB166DC50E190C4D778D1587F102EE0">>},
{<<"lhttpc">>, <<"044F16F0018C7AA7E945E9E9406C7F6035E0B8BC08BF77B00C78CE260E1071E3">>}]}
{<<"jsx">>, <<"08154624050333919B4AC1B789667D5F4DB166DC50E190C4D778D1587F102EE0">>}]},
{pkg_hash_ext,[
{<<"base16">>, <<"02AFD0827E61A7B07093873E063575CA3A2B07520567C7F8CEC7C5D42F052D76">>},
{<<"eini">>, <<"DA64AE8DB7C2F502E6F20CDF44CD3D9BE364412B87FF49FEBF282540F673DFCB">>},
{<<"jsx">>, <<"EED26A0D04D217F9EECEFFFB89714452556CF90EB38F290A27A4D45B9988F8C0">>}]}
].
4 changes: 3 additions & 1 deletion src/erlcloud.appup.src
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
%%-*-: erlang -*-
{"3.6.7",
{"3.6.8",
[
{"3.6.7",[{restart_application,erlcloud}]},
{"3.5.16",[{restart_application,erlcloud}]},
%% emqx e4.3.9/e4.4.3 relied on version erlcloud 3.2.8, but it was
%% upgraded to 3.5.16 in e4.3.10/e4.4.4.
Expand All @@ -10,6 +11,7 @@
{<<".*">>,[]}
],
[
{"3.6.7",[{restart_application,erlcloud}]},
{"3.5.16",[{restart_application,erlcloud}]},
{"3.2.8",[{restart_application,erlcloud}]},
{<<".*">>,[]}
Expand Down
56 changes: 46 additions & 10 deletions src/erlcloud_aws.erl
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@
http_body/1,
request_to_return/1,
sign_v4_headers/5,
sign_v4/8,
sign_v4/8, sign_v4/9,
canonical_query_string/1,
get_service_status/1,
is_throttling_error_response/1,
get_timeout/1,
profile/0, profile/1, profile/2
profile/0, profile/1, profile/2,
iso_8601_basic_time/0, iso_8601_basic_time/1,
to_sign/3,
signing_key/4,
base16/1,
canonical_request/5,
credential_scope/3,
credential/4
]).

-include("erlcloud.hrl").
Expand All @@ -48,6 +55,7 @@
-type httpc_result_ok() :: {http_client_headers(), binary()}.
-type httpc_result_error() :: {http_error, Status :: pos_integer(), StatusLine :: string(), Body :: binary()}
| {socket_error, Reason :: term()}.
-type headers() :: [{atom() | string(), iodata()}].
-export_type([httpc_result_error/0]).
-type httpc_result() :: {ok, httpc_result_ok()} | {error, httpc_result_error()}.
-export_type([httpc_result/0]).
Expand Down Expand Up @@ -1147,22 +1155,45 @@ sign_v4_headers(Config, Headers, Payload, Region, Service) ->
-spec sign_v4(atom(), list(), aws_config(), erlcloud_httpc:headers(), string() | binary(), string(), string(), list()) -> erlcloud_httpc:headers().
sign_v4(Method, Uri, Config, Headers, Payload, Region, Service, QueryParams) ->
Date = iso_8601_basic_time(),
{PayloadHash, Headers1} =
sign_v4_content_sha256_header( [{"x-amz-date", Date} | Headers], Payload ),
Headers2 = case Config#aws_config.security_token of
undefined -> Headers1;
Token -> [{"x-amz-security-token", Token} | Headers1]
sign_v4(Method, Uri, Config, Headers, Payload, Region, Service, QueryParams, Date).

-spec sign_v4(atom(), list(), aws_config(), headers(), string() | binary(), string(), string(), list(), string()) -> headers().
sign_v4(Method, Uri, Config, Headers0, Payload, Region, Service, QueryParams, Date0) ->
% use passed-in x-amz-date header or create one
{Headers1, Date} =
case proplists:get_value("x-amz-date", Headers0) of
undefined ->
{[{"x-amz-date", Date0} | Headers0], Date0};
ADate ->
{Headers0, ADate}
end,

{PayloadHash, Headers2} =
sign_v4_content_sha256_header(Headers1, Payload),
Headers3 = case Config#aws_config.security_token of
undefined -> Headers2;
Token -> [{"x-amz-security-token", Token} | Headers2]
end,
{Request, SignedHeaders} = canonical_request(Method, Uri, QueryParams, Headers2, PayloadHash),
Headers4 =
case proplists:get_value("host", Headers3) of
undefined -> Headers3;
Host -> [{"host", string:lowercase(Host)} | proplists:delete("host", Headers3)]
end,
{Request, SignedHeaders} = canonical_request(Method, Uri, QueryParams, Headers4, PayloadHash),
CredentialScope = credential_scope(Date, Region, Service),
ToSign = to_sign(Date, CredentialScope, Request),
SigningKey = signing_key(Config, Date, Region, Service),
Signature = base16(erlcloud_util:sha256_mac( SigningKey, ToSign)),
Authorization = authorization(Config, CredentialScope, SignedHeaders, Signature),
[{"Authorization", lists:flatten(Authorization)} | Headers2].
[{"Authorization", lists:flatten(Authorization)} | Headers4].

-spec iso_8601_basic_time() -> string().
iso_8601_basic_time() ->
{{Year,Month,Day},{Hour,Min,Sec}} = calendar:universal_time(),
iso_8601_basic_time(calendar:universal_time()).

-spec iso_8601_basic_time(tuple()) -> string().
iso_8601_basic_time(Datetime) ->
{{Year,Month,Day},{Hour,Min,Sec}} = Datetime,
lists:flatten([
integer_to_list(Year), two_digits(Month), two_digits(Day), $T,
two_digits(Hour), two_digits(Min), two_digits(Sec), $Z
Expand Down Expand Up @@ -1233,10 +1264,15 @@ hash_encode(Data) ->
base16(Data) ->
[binary:bin_to_list(base16:encode(Data))].

-spec credential_scope(string(), string(), string()) -> string().
credential_scope(Date, Region, Service) ->
DateOnly = string:left(Date, 8),
[DateOnly, $/, Region, $/, Service, "/aws4_request"].

-spec credential(aws_config(), string(), string(), string()) -> string().
credential(Config, Date, Region, Service) ->
[Config#aws_config.access_key_id, $/, credential_scope(Date, Region, Service)].

to_sign(Date, CredentialScope, Request) ->
["AWS4-HMAC-SHA256\n",
Date, $\n,
Expand Down
Loading