-
-
Save joshainglis/d7b5e14bf5b0c33e474fce40c4d465d2 to your computer and use it in GitHub Desktop.
A function that lets you sign S3 urls for viewing from within your database queries
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- This function is based on this description: | |
| -- https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html | |
| CREATE OR REPLACE FUNCTION | |
| sign_s3_url( | |
| m_host text, | |
| m_resource text, | |
| m_region text, | |
| m_key text, | |
| m_secret text, | |
| m_time timestamptz) | |
| RETURNS text AS $$ | |
| DECLARE | |
| m_verb text := 'GET'; | |
| m_time_utz timestamptz := m_time at time zone 'UTC'; | |
| m_service text := 's3'; | |
| m_date_str text := to_char(m_time_utz, 'yyyyMMdd'); | |
| m_time_str text := to_char(m_time_utz, 'yyyyMMddThh24mmssZ'); | |
| m_credential text := concat( | |
| m_key, | |
| '/', m_date_str, | |
| '/', m_region, | |
| '/', m_service, | |
| '/aws4_request' | |
| ); | |
| -- Credential won't contain any other things we need to encode | |
| -- replace any / with %2F | |
| m_credential_url_encoded text := replace(m_credential, '/', '%2F'); | |
| m_signed_headers text := 'host'; | |
| -- The line break is not in error, required by spec for each header line | |
| m_headers text := concat('host:', m_host, E'\n'); | |
| -- If you need query parameters, they should go here, sorted alphabetically (and URI encoded) | |
| m_query_str text := concat( | |
| 'X-Amz-Algorithm=AWS4-HMAC-SHA256', | |
| '&X-Amz-Credential=', m_credential_url_encoded, | |
| '&X-Amz-Date=', m_time_str, | |
| '&X-Amz-Expires=', '3600', -- 1800 is desired, use this for testing | |
| '&X-Amz-SignedHeaders=', m_signed_headers | |
| ); | |
| -- Build the canonical request | |
| m_canonical_request text := concat( | |
| m_verb, E'\n', | |
| m_resource, E'\n', | |
| m_query_str, E'\n', | |
| m_headers, E'\n', | |
| m_signed_headers, E'\n', | |
| 'UNSIGNED-PAYLOAD' | |
| ); | |
| m_hex_encoded_hash text := encode(digest(m_canonical_request, 'sha256'), 'hex'); | |
| m_string_to_sign bytea := concat( | |
| 'AWS4-HMAC-SHA256', E'\n', | |
| m_time_str, E'\n', | |
| concat(m_date_str, '/', m_region, '/', m_service, '/aws4_request'), E'\n', | |
| m_hex_encoded_hash | |
| ); | |
| -- In the AWS documentation, format is HMAC-SHA256(key, data), but postgres is hmac(data, key, type) | |
| -- So, these look backwards, but they are not. | |
| m_date_key bytea := hmac(bytea(m_date_str), bytea(concat('AWS4', m_secret)), 'sha256'); | |
| m_date_region_key bytea := hmac(bytea(m_region), m_date_key, 'sha256'); | |
| m_date_region_service_key bytea := hmac(bytea(m_service), m_date_region_key, 'sha256'); | |
| m_signing_key bytea := hmac(bytea('aws4_request'), m_date_region_service_key, 'sha256'); | |
| m_signature text := encode(hmac(m_string_to_sign, m_signing_key, 'sha256'), 'hex'); | |
| m_result_url text := concat( | |
| 'https://', | |
| m_host, | |
| m_resource, | |
| '?', | |
| m_query_str, | |
| '&X-Amz-Signature=', | |
| m_signature | |
| ); | |
| BEGIN | |
| RETURN m_result_url; | |
| END; | |
| $$ LANGUAGE plpgsql; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment