diff --git a/src/args/agnostic.nix b/src/args/agnostic.nix index b3400078..7c7fdc20 100644 --- a/src/args/agnostic.nix +++ b/src/args/agnostic.nix @@ -75,6 +75,8 @@ let makeSearchPaths = import ./make-search-paths/default.nix self; makeSecretForAwsFromEnv = import ./make-secret-for-aws-from-env/default.nix self; + makeSecretForAwsFromGitlab = + import ./make-secret-for-aws-from-gitlab/default.nix self; makeSecretForEnvFromSops = import ./make-secret-for-env-from-sops/default.nix self; makeSecretForTerraformFromEnv = diff --git a/src/args/make-secret-for-aws-from-gitlab/default.nix b/src/args/make-secret-for-aws-from-gitlab/default.nix new file mode 100644 index 00000000..8afe74d5 --- /dev/null +++ b/src/args/make-secret-for-aws-from-gitlab/default.nix @@ -0,0 +1,13 @@ +{ __nixpkgs__, makeTemplate, toDerivationName, ... }: +{ duration, name, retries, roleArn, }: +makeTemplate { + replace = { + __argDuration__ = duration; + __argName__ = toDerivationName name; + __argRetries__ = retries; + __argRoleArn__ = roleArn; + }; + name = "make-secret-for-aws-from-gitlab-for-${name}"; + searchPaths.bin = [ __nixpkgs__.awscli __nixpkgs__.jq ]; + template = ./template.sh; +} diff --git a/src/args/make-secret-for-aws-from-gitlab/template.sh b/src/args/make-secret-for-aws-from-gitlab/template.sh new file mode 100644 index 00000000..686765c0 --- /dev/null +++ b/src/args/make-secret-for-aws-from-gitlab/template.sh @@ -0,0 +1,62 @@ +# shellcheck shell=bash + +function _get_credential { + local credential="${1}" + local session="${2}" + + echo "${session}" | jq -rec ".Credentials.${credential}" +} + +function login { + # AWS STS args + local args=( + --role-arn "${1}" + --role-session-name "gitlab-${CI_PROJECT_ID}-${CI_PIPELINE_ID}-${CI_JOB_ID}" + --web-identity-token "${CI_JOB_JWT_V2}" + --duration-seconds "${2}" + ) + + # Retry logic + local retries="__argRetries__" + local wait="1" + local try="1" + local success="1" + + # Session variables + local session + export AWS_ACCESS_KEY_ID + export AWS_SECRET_ACCESS_KEY + export AWS_SESSION_TOKEN + + : \ + && while [ "${try}" -le "${retries}" ]; do + if session="$(aws sts assume-role-with-web-identity "${args[@]}" 2> /dev/null)"; then + success="0" \ + && break + else + info "Login failed. Attempt ${try} of ${retries}." \ + && sleep "${wait}" \ + && try=$((try + 1)) + fi + done \ + && if [ "${success}" == "0" ]; then + AWS_ACCESS_KEY_ID="$(_get_credential "AccessKeyId" "${session}")" \ + && AWS_SECRET_ACCESS_KEY="$(_get_credential "SecretAccessKey" "${session}")" \ + && AWS_SESSION_TOKEN="$(_get_credential "SessionToken" "${session}")" + else + error "Could not login to AWS." + fi +} + +function main { + : \ + && info "Making secrets for aws from gitlab for __argName__:" \ + && if test -n "${CI_JOB_JWT_V2-}"; then + info "Logging in as '__argName__' using GitLab OIDC." \ + && login "__argRoleArn__" "__argDuration__" + else + warn "It looks like this job is not running on GitLab CI. Skipping." + fi +} + +main "${@}" diff --git a/src/evaluator/modules/default.nix b/src/evaluator/modules/default.nix index 6630160b..1ccf1998 100644 --- a/src/evaluator/modules/default.nix +++ b/src/evaluator/modules/default.nix @@ -24,6 +24,7 @@ (import ./lint-with-ajv/default.nix args) (import ./pipelines/default.nix args) (import ./secrets-for-aws-from-env/default.nix args) + (import ./secrets-for-aws-from-gitlab/default.nix args) (import ./secrets-for-env-from-sops/default.nix args) (import ./secrets-for-terraform-from-env/default.nix args) (import ./test-license/default.nix args) diff --git a/src/evaluator/modules/secrets-for-aws-from-gitlab/default.nix b/src/evaluator/modules/secrets-for-aws-from-gitlab/default.nix new file mode 100644 index 00000000..6f3eac2f --- /dev/null +++ b/src/evaluator/modules/secrets-for-aws-from-gitlab/default.nix @@ -0,0 +1,37 @@ +{ __toModuleOutputs__, makeSecretForAwsFromGitlab, ... }: +{ config, lib, ... }: +let + type = lib.types.submodule (_: { + options = { + duration = lib.mkOption { + default = 3600; + type = lib.types.ints.positive; + }; + retries = lib.mkOption { + default = 15; + type = lib.types.ints.positive; + }; + roleArn = lib.mkOption { type = lib.types.str; }; + }; + }); + output = name: + { duration, retries, roleArn, }: { + name = "/secretsForAwsFromGitlab/${name}"; + value = makeSecretForAwsFromGitlab { + inherit duration; + inherit name; + inherit retries; + inherit roleArn; + }; + }; +in { + options = { + secretsForAwsFromGitlab = lib.mkOption { + default = { }; + type = lib.types.attrsOf type; + }; + }; + config = { + outputs = __toModuleOutputs__ output config.secretsForAwsFromGitlab; + }; +}