diff --git a/doc/admin-guide/plugins/s3_auth.en.rst b/doc/admin-guide/plugins/s3_auth.en.rst index 6445e13feb4..82625ebb036 100644 --- a/doc/admin-guide/plugins/s3_auth.en.rst +++ b/doc/admin-guide/plugins/s3_auth.en.rst @@ -36,6 +36,7 @@ Using the plugin in a remap rule would be e.g.:: ... @plugin=s3_auth.so @pparam=--access_key @pparam=my-key \ @pparam=--secret_key @pparam=my-secret \ + @pparam=--session_token @pparam=my-token \ @pparam=--virtual_host @@ -71,6 +72,7 @@ Configuration options:: --version=4 # Optional + --session_token= --v4-include-headers= --v4-exclude-headers= --v4-region-map=region_map.config @@ -87,6 +89,7 @@ The ``s3_auth_v4.config`` config file could look like this:: access_key= secret_key= + session_token= version=4 v4-include-headers= v4-exclude-headers= @@ -141,6 +144,7 @@ This is a pretty barebone start for the S3 services, it is missing a number of f - It does not deal with canonicalization of AMZ headers. - It does not handle POST requests (but do we need to ?) - It does not incorporate query parameters. +- It does not support session tokens. Contributions to any of these would be appreciated. diff --git a/plugins/s3_auth/aws_auth_v4.h b/plugins/s3_auth/aws_auth_v4.h index f1b65158e86..d0eac44a1f6 100644 --- a/plugins/s3_auth/aws_auth_v4.h +++ b/plugins/s3_auth/aws_auth_v4.h @@ -61,6 +61,7 @@ class TsInterface static const String X_AMZ_CONTENT_SHA256 = "x-amz-content-sha256"; static const String X_AMX_DATE = "x-amz-date"; +static const String X_AMZ_SECURITY_TOKEN = "x-amz-security-token"; static const String X_AMZ = "x-amz-"; static const String CONTENT_TYPE = "content-type"; static const String HOST = "host"; diff --git a/plugins/s3_auth/s3_auth.cc b/plugins/s3_auth/s3_auth.cc index 1d954057834..7c7ed88580e 100644 --- a/plugins/s3_auth/s3_auth.cc +++ b/plugins/s3_auth/s3_auth.cc @@ -172,9 +172,10 @@ class S3Config ~S3Config() { - _secret_len = _keyid_len = 0; + _secret_len = _keyid_len = _token_len = 0; TSfree(_secret); TSfree(_keyid); + TSfree(_token); if (_cont) { TSContDestroy(_cont); } @@ -200,6 +201,9 @@ class S3Config if (_region_map_modified && !_region_map.empty()) { TSError("[%s] region map is not used with AWS auth v2, parameter ignored", PLUGIN_NAME); } + if (nullptr != _token || _token_len > 0) { + TSError("[%s] session token support with AWS auth v2 is not implemented, parameter ignored", PLUGIN_NAME); + } } else { /* 4 == _version */ if (_virt_host_modified) { @@ -240,6 +244,11 @@ class S3Config _keyid_len = src->_keyid_len; } + if (src->_token) { + _token = TSstrdup(src->_token); + _token_len = src->_token_len; + } + if (src->_version_modified) { _version = src->_version; _version_modified = true; @@ -285,6 +294,12 @@ class S3Config return _keyid; } + const char * + token() const + { + return _token; + } + int secret_len() const { @@ -297,6 +312,12 @@ class S3Config return _keyid_len; } + int + token_len() const + { + return _token_len; + } + int version() const { @@ -337,6 +358,13 @@ class S3Config _keyid_len = strlen(s); } void + set_token(const char *s) + { + TSfree(_token); + _token = TSstrdup(s); + _token_len = strlen(s); + } + void set_virt_host(bool f = true) { _virt_host = f; @@ -392,6 +420,8 @@ class S3Config size_t _secret_len = 0; char *_keyid = nullptr; size_t _keyid_len = 0; + char *_token = nullptr; + size_t _token_len = 0; bool _virt_host = false; int _version = 2; bool _version_modified = false; @@ -448,6 +478,8 @@ S3Config::parse_config(const std::string &config_fname) set_secret(pos2 + 11); } else if (0 == strncasecmp(pos2, "access_key=", 11)) { set_keyid(pos2 + 11); + } else if (0 == strncasecmp(pos2, "session_token=", 14)) { + set_token(pos2 + 14); } else if (0 == strncasecmp(pos2, "version=", 8)) { set_version(pos2 + 8); } else if (0 == strncasecmp(pos2, "virtual_host", 12)) { @@ -666,6 +698,12 @@ S3Request::authorizeV4(S3Config *s3) return TS_HTTP_STATUS_INTERNAL_SERVER_ERROR; } + /* set X-Amz-Security-Token if we have a token */ + if (nullptr != s3->token() && '\0' != *(s3->token()) && + !set_header(X_AMZ_SECURITY_TOKEN.data(), X_AMZ_SECURITY_TOKEN.size(), s3->token(), s3->token_len())) { + return TS_HTTP_STATUS_INTERNAL_SERVER_ERROR; + } + String auth = util.getAuthorizationHeader(); if (auth.empty()) { return TS_HTTP_STATUS_INTERNAL_SERVER_ERROR; @@ -927,6 +965,7 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf ATS_UNUSE {const_cast("v4-include-headers"), required_argument, nullptr, 'i'}, {const_cast("v4-exclude-headers"), required_argument, nullptr, 'e'}, {const_cast("v4-region-map"), required_argument, nullptr, 'm'}, + {const_cast("session_token"), required_argument, nullptr, 't'}, {nullptr, no_argument, nullptr, '\0'}, }; @@ -957,6 +996,9 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf ATS_UNUSE case 's': s3->set_secret(optarg); break; + case 't': + s3->set_token(optarg); + break; case 'h': s3->set_virt_host(); break;