Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
11 changes: 9 additions & 2 deletions os/debian/Dockerfile.debian
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,7 @@ RUN helm3 plugin install https://github.com/databus23/helm-diff.git --version v$
# AWS_DATA_PATH is a PATH-like variable for configuring the AWS botocore library to
# load additional modules. Do not set it.
ARG GEODESIC_AWS_HOME=${HOME}/.aws
ENV AWS_CONFIG_FILE=${GEODESIC_AWS_HOME}/config
ENV AWS_SHARED_CREDENTIALS_FILE=${GEODESIC_AWS_HOME}/credentials

# Region abbreviation types are "fixed" (always 3 chars), "short" (4-5 chars), or "long" (the full AWS string)
# See https://github.com/cloudposse/terraform-aws-utils#introduction
ENV AWS_REGION_ABBREVIATION_TYPE=short
Expand Down Expand Up @@ -320,6 +319,14 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \
sudo dpkg -i /tmp/session-manager-plugin.deb && \
rm -f /tmp/session-manager-plugin.deb

# This is a workaround for https://github.com/moby/buildkit/issues/5775
# CHAMBER_KMS_KEY_ALIAS is used by the `chamber` CLI, but it is incorrectly
# flagged as a secret by the SecretsUsedInArgOrEnv check. This is a false positive.
# So, as a workaround, we allow you to set `CHAMBER_KMS_ALIAS` instead,
# and at runtime we copy the value to `CHAMBER_KMS_KEY_ALIAS` for you.
ENV CHAMBER_KMS_ALIAS=aws/ssm


# Install documentation
COPY docs/ /usr/share/docs/

Expand Down
1 change: 1 addition & 0 deletions packages.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ kubectl@cloudposse
kubectx@cloudposse
kubens@cloudposse
less
lsb-release
make
man-db
openssh-client
Expand Down
22 changes: 22 additions & 0 deletions rootfs/etc/profile.d/_10-colors.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,28 @@
# The main change is that it uses the terminal's default colors for foreground and background,
# whereas the previous version "reset" the color by setting it to black, which fails in dark mode.

# These utilities use basic ANSI color codes (0-7) to colorize text in the terminal.
# Besides being the most widely supported method, it also has the advantage
# that terminals that support colored text and backgrounds as themes often
# take responsibility for the ANSI colors being legible regardless of the theme.
# For example, on a completely red background, the ANSI red color will be changed
# as part of the theme to a lighter or darker shade of red to ensure it is legible.
# This would not be the case if we tried to set the color directly using RGB values,
# or even using extended ANSI colors.

# To test a terminal manually, the following script displays all 256 ANSI colors.
# Our color functions only use 1 through 6.
# We indirectly support 0 and 7 (black and white), reversing them in dark mode,
# but we do not provide direct functions for them.
# We support a "bold" modifier, which some terminals equate to colors 8 through 15.
#
# for i in {0..255}; do
# printf "\e[38;5;${i}mcolor%-5i\e[0m" $i
# if [ $((($i + 1) % 8)) == 0 ]; then
# echo
# fi
# done

function update-terminal-theme() {
local new_mode="$1"
local quiet=false
Expand Down
131 changes: 100 additions & 31 deletions rootfs/etc/profile.d/aws.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#!/bin/bash
# shellcheck disable=SC2155
# Above directive suppresses ShellCheck SC2155: Declare and assign separately to avoid masking return values.
# In this script, we do not care about return values, as problems are detected by the resulting empty value.

export AWS_REGION_ABBREVIATION_TYPE=${AWS_REGION_ABBREVIATION_TYPE:-fixed}
export AWS_DEFAULT_SHORT_REGION=${AWS_DEFAULT_SHORT_REGION:-$(aws-region --${AWS_REGION_ABBREVIATION_TYPE} ${AWS_DEFAULT_REGION:-us-west-2})}
Expand Down Expand Up @@ -62,7 +65,7 @@ fi

function aws_choose_role() {
_preview="${FZF_PREVIEW:-crudini --format=ini --get "$AWS_CONFIG_FILE" 'profile {}'}"
cat "${AWS_SHARED_CREDENTIALS_FILE:-~/.aws/credentials}" "${AWS_CONFIG_FILE:-~/.aws/config}" 2>/dev/null |
cat "${AWS_SHARED_CREDENTIALS_FILE:-${GEODESIC_AWS_HOME}/credentials}" "${AWS_CONFIG_FILE:-${GEODESIC_AWS_HOME}/config}" 2>/dev/null |
crudini --get - | sed 's/^ *profile *//' |
fzf \
--height 30% \
Expand Down Expand Up @@ -107,74 +110,140 @@ function aws_sdk_assume_role() {
# Asks AWS what the currently active identity is and
# sets environment variables accordingly
function export_current_aws_role() {
local role_name
local role_name role_names
# Could be a primary or assumed role. If we have assumed a role, cut off the session name.
local current_role=$(aws sts get-caller-identity --output text --query 'Arn' 2>/dev/null | cut -d/ -f1-2)
if [[ -z $current_role ]]; then
unset ASSUME_ROLE
return 0
fi

# Quick check, are we who we say we are?
# If AWS_VAULT is not enabled, clear any setting from it.
[[ "${AWS_VAULT_ENABLED:-false}" == "true" ]] || unset AWS_VAULT

# Quick check, are we who we say we are? Does the current role match the profile?
local profile_arn
local profile_target=${AWS_PROFILE:-${AWS_VAULT:-default}}
if [[ -n $profile_target ]]; then
profile_arn=$(aws --profile "${profile_target}" sts get-caller-identity --output text --query 'Arn' 2>/dev/null | cut -d/ -f1-2)
if [[ $profile_arn == $current_role ]]; then
# Extract profile name from config file:
# 1. For default profile, look for a better name
# 2. Skip identity profiles (ending with -identity), as they are too generic
# 3. Use the first non-default, non-identity profile found
if [[ $profile_target == "default" ]] || [[ $profile_target =~ -identity$ ]]; then
# Make some effort to find a better name for the role, but only check the config file, not credentials.
local config_file="${AWS_CONFIG_FILE:-\~/.aws/config}"
if [[ -r $config_file ]]; then
# Assumed roles in AWS config file use the role ARN, not the assumed role ARN, so adjust accordingly.
local role_arn=$(printf "%s" "$current_role" | sed 's/:sts:/:iam:/g' | sed 's,:assumed-role/,:role/,')
role_name=($(crudini --get --format=lines "$config_file" | grep "$role_arn" | cut -d' ' -f 3))
for rn in "${role_name[@]}"; do
if [[ $rn == "default" ]] || [[ $rn =~ -identity$ ]]; then
# Remove the session name from the profile target role, if present
profile_arn=$(aws --profile "${profile_target}" sts get-caller-identity --output text --query 'Arn' 2>/dev/null | cut -d/ -f1-2)
# The main way there would be a mismatch is if AWS_VAULT is set or there are API keys in the environment
if [[ "$profile_arn" == "$current_role" ]]; then
# If we are here, then the current role matches the assigned profile. That is a good thing.
# However, the profile name may not be the best name for the role. If it is too generic, try to find a better name.
# Extract profile name from config file:
# 1. For default profile, look for a better name
# 2. Skip identity profiles (ending with -identity), as they are too generic
# 3. Use the first non-default, non-identity profile found
if [[ $profile_target == "default" ]] || [[ $profile_target =~ -identity$ ]]; then
local backup_name="$profile_target"
# Make some effort to find a better name for the role, but only check the config file, not credentials.
local config_file="${AWS_CONFIG_FILE:-${GEODESIC_AWS_HOME}/config}"
if [[ -r $config_file ]]; then
# Is this a normal IAM role or an Identity Center permissions set role?
if [[ $current_role =~ AWSReservedSSO_[^_]+_[0-9a-f]+$ ]]; then
# This is an Identity Center permissions set role
# current_role is "arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_IdentityAdminRoleAccess_5c90026c17fbd1c2"

# Extract account ID using cut
local account_id=$(echo "$current_role" | cut -d':' -f5)

# Extract the full role part
local role_part=$(echo "$current_role" | cut -d':' -f6) # This gets everything after the 5th colon

# Extract the role name by isolating it from boilerplate
local sso_role_name=$(echo "$role_part" | cut -d'_' -f2) # This selects the second field delimited by '_'

# Find all profiles that have matching role names
local profile_names=($(crudini --get --format=lines "$config_file" | grep "$sso_role_name" | cut -d' ' -f 3))
local profile_name
for profile_name in "${profile_names[@]}"; do
# Skip the generic profiles
if [[ "$profile_name" == "default" ]] || [[ "$profile_name" =~ -identity$ ]]; then
continue
else
export ASSUME_ROLE=$rn
fi
if [[ "$account_id" == "$(crudini --get "$config_file" "profile $profile_name" sso_account_id)" ]]; then
export ASSUME_ROLE="$profile_name"
return
fi
done
export ASSUME_ROLE="$backup_name"
return
fi
else
export ASSUME_ROLE="$profile_target"
return

# Normal IAM role
# Assumed roles in AWS config file use the role ARN, not the assumed role ARN, so adjust accordingly.
local role_arn=$(printf "%s" "$current_role" | sed 's/:sts:/:iam:/g' | sed 's,:assumed-role/,:role/,')
role_names=($(crudini --get --format=lines "$config_file" | grep "$role_arn" | cut -d' ' -f 3))
for rn in "${role_names[@]}"; do
if [[ $rn == "default" ]] || [[ $rn =~ -identity$ ]]; then
continue
else
export ASSUME_ROLE=$rn
return
fi
done
fi
fi
echo "* $(red Profile is set to $profile_target but current role does not match:)"
echo "* $(red $current_role)"
# could not find a better match, so just use the generic profile name
export ASSUME_ROLE="$profile_target"
return
fi

# If we are here, then the current role is not what we would expect from the AWS_PROFILE setting.
# If AWS_PROFILE is unset, then we forgive the current role not being the default role.
# Otherwise, we warn about a mismatch.
if [[ -n $AWS_PROFILE ]]; then
red "* AWS Credentials Mismatch! AWS_PROFILE is set to $AWS_PROFILE"
red "* That profile selects role $profile_arn"
red "* But STS reports current role is $current_role"
export ASSUME_ROLE=$(red-n '!mixed!')
return
elif [[ -n $AWS_VAULT ]]; then
red "* AWS Credentials Mismatch! AWS_VAULT claims to have set role to profile $AWS_VAULT"
red "* That profile selects role $profile_arn"
red "* But STS reports current role is $current_role"
red "* "
export ASSUME_ROLE=$(red-n '!mixed!')
return
fi

# If we are here, then we are not using AWS_VAULT or AWS_PROFILE, and the current role does not match the default profile.
# This is likely because we are using API keys directly in the environment or credentials file.
# Try to figure out a better name for the role.

# saml2aws will store the assumed role from sign-in as x_principal_arn in credentials file
# Default values from https://awscli.amazonaws.com/v2/documentation/api/latest/topic/config-vars.html
local creds_file="${AWS_SHARED_CREDENTIALS_FILE:-\~/.aws/credentials}"
local creds_file="${AWS_SHARED_CREDENTIALS_FILE:-${GEODESIC_AWS_HOME}/credentials}"
if [[ -r $creds_file ]]; then
role_name=$(crudini --get --format=lines "${creds_file}" | grep "$current_role" | head -1 | cut -d' ' -f 2)
fi

# Assumed roles are normally found in AWS config file, but using the role ARN,
# not the assumed role ARN. google2aws also puts login role in this file.
local config_file="${AWS_CONFIG_FILE:-\~/.aws/config}"
local config_file="${AWS_CONFIG_FILE:-${GEODESIC_AWS_HOME}/config}"
if [[ -z $role_name ]] && [[ -r $config_file ]]; then
local role_arn=$(printf "%s" "$current_role" | sed 's/:sts:/:iam:/g' | sed 's,:assumed-role/,:role/,')
role_name=$(crudini --get --format=lines "$config_file" | grep "$role_arn" | head -1 | cut -d' ' -f 3)
fi

# If we still don't have a profile name, make one up.
if [[ -z $role_name ]]; then
if [[ "$role_arn" =~ "role/OrganizationAccountAccessRole" ]]; then
role_name="$(printf "%s" "$role_arn" | cut -d: -f 5):OrgAccess"
echo "* $(red "Could not find profile name for ${role_arn} ; calling it \"${role_name}\"")" >&2
elif [[ $current_role =~ AWSReservedSSO_[^_]+_[0-9a-f]+$ ]]; then
# This is an Identity Center permissions set role
# current_role is "arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_IdentityAdminRoleAccess_5c90026c17fbd1c2"
# Extract account ID using cut
local account_id=$(echo "$current_role" | cut -d':' -f5)
# Extract the full role part
local role_part=$(echo "$current_role" | cut -d':' -f6) # This gets everything after the 5th colon
# Extract the role name by isolating it from boilerplate
local sso_role_name=$(echo "$role_part" | cut -d'_' -f2) # This selects the second field delimited by '_'
role_name="${account_id}:${sso_role_name}"
else
role_name="$(printf "%s" "$role_arn" | cut -d/ -f 2)"
echo "* $(green "Could not find profile name for ${role_arn} ; calling it \"${role_name}\"")" >&2
fi
echo "* $(green "Could not find profile name for ${role_arn} ; calling it \"${role_name}\"")" >&2
fi
export ASSUME_ROLE="$role_name"
}
Expand All @@ -186,7 +255,7 @@ function refresh_current_aws_role_if_needed() {
local is_exported="^declare -[^ x]*x[^ x]* "
local aws_profile=$(declare -p AWS_PROFILE 2>/dev/null)
[[ $aws_profile =~ $is_exported ]] || aws_profile=""
local credentials_mtime=$(stat -c "%Y" ${AWS_SHARED_CREDENTIALS_FILE:-"~/.aws/credentials"} 2>/dev/null)
local credentials_mtime=$(stat -c "%Y" ${AWS_SHARED_CREDENTIALS_FILE:-"${GEODESIC_AWS_HOME}/credentials"} 2>/dev/null)
local role_fingerprint="${aws_profile}/${credentials_mtime}/${AWS_ACCESS_KEY_ID}"
if [[ $role_fingerprint != $GEODESIC_AWS_ROLE_CACHE ]]; then
export_current_aws_role
Expand Down
22 changes: 22 additions & 0 deletions rootfs/etc/profile.d/chamber.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This is a workaround for https://github.com/moby/buildkit/issues/5775
#
# The `chamber` command (https://github.com/segmentio/chamber) is a CLI for managing secrets.
# It is installed in Geodesic and referenced in various documentation.
#
# Chamber works with AWS SSM Parameter store to save encrypted parameters.
# By default, in uses a KMS key with the alias `parameter_store_key`
# to encrypt and decrypt the parameters. Geodesic supports using
# the AWS default key, with alias `ssm`, or a custom key.
#
# However, due to the issue with buildkit mentioned above,
# setting the required environment variable in the Dockerfile
# leads to a warning about it being a secret stored in the image.
#
# So, as a workaround, we allow you to set `CHAMBER_KMS_ALIAS` instead,
# and we will set the `CHAMBER_KMS_KEY_ALIAS` environment variable for you here.
#

if [[ -z "$CHAMBER_KMS_KEY_ALIAS" ]] && [[ -n "$CHAMBER_KMS_ALIAS" ]]; then
export CHAMBER_KMS_KEY_ALIAS="$CHAMBER_KMS_ALIAS"
unset CHAMBER_KMS_ALIAS
fi
Loading