Skip to content

Commit 4f2eac5

Browse files
authored
Fix Role Prompt, bypass buggy secrets check, add lsb_release (#971)
1 parent cdf5e15 commit 4f2eac5

File tree

5 files changed

+154
-33
lines changed

5 files changed

+154
-33
lines changed

os/debian/Dockerfile.debian

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,7 @@ RUN helm3 plugin install https://github.com/databus23/helm-diff.git --version v$
236236
# AWS_DATA_PATH is a PATH-like variable for configuring the AWS botocore library to
237237
# load additional modules. Do not set it.
238238
ARG GEODESIC_AWS_HOME=${HOME}/.aws
239-
ENV AWS_CONFIG_FILE=${GEODESIC_AWS_HOME}/config
240-
ENV AWS_SHARED_CREDENTIALS_FILE=${GEODESIC_AWS_HOME}/credentials
239+
241240
# Region abbreviation types are "fixed" (always 3 chars), "short" (4-5 chars), or "long" (the full AWS string)
242241
# See https://github.com/cloudposse/terraform-aws-utils#introduction
243242
ENV AWS_REGION_ABBREVIATION_TYPE=short
@@ -320,6 +319,14 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \
320319
sudo dpkg -i /tmp/session-manager-plugin.deb && \
321320
rm -f /tmp/session-manager-plugin.deb
322321

322+
# This is a workaround for https://github.com/moby/buildkit/issues/5775
323+
# CHAMBER_KMS_KEY_ALIAS is used by the `chamber` CLI, but it is incorrectly
324+
# flagged as a secret by the SecretsUsedInArgOrEnv check. This is a false positive.
325+
# So, as a workaround, we allow you to set `CHAMBER_KMS_ALIAS` instead,
326+
# and at runtime we copy the value to `CHAMBER_KMS_KEY_ALIAS` for you.
327+
ENV CHAMBER_KMS_ALIAS=aws/ssm
328+
329+
323330
# Install documentation
324331
COPY docs/ /usr/share/docs/
325332

packages.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ kubectl@cloudposse
4040
kubectx@cloudposse
4141
kubens@cloudposse
4242
less
43+
lsb-release
4344
make
4445
man-db
4546
openssh-client

rootfs/etc/profile.d/_10-colors.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,28 @@
1010
# The main change is that it uses the terminal's default colors for foreground and background,
1111
# whereas the previous version "reset" the color by setting it to black, which fails in dark mode.
1212

13+
# These utilities use basic ANSI color codes (0-7) to colorize text in the terminal.
14+
# Besides being the most widely supported method, it also has the advantage
15+
# that terminals that support colored text and backgrounds as themes often
16+
# take responsibility for the ANSI colors being legible regardless of the theme.
17+
# For example, on a completely red background, the ANSI red color will be changed
18+
# as part of the theme to a lighter or darker shade of red to ensure it is legible.
19+
# This would not be the case if we tried to set the color directly using RGB values,
20+
# or even using extended ANSI colors.
21+
22+
# To test a terminal manually, the following script displays all 256 ANSI colors.
23+
# Our color functions only use 1 through 6.
24+
# We indirectly support 0 and 7 (black and white), reversing them in dark mode,
25+
# but we do not provide direct functions for them.
26+
# We support a "bold" modifier, which some terminals equate to colors 8 through 15.
27+
#
28+
# for i in {0..255}; do
29+
# printf "\e[38;5;${i}mcolor%-5i\e[0m" $i
30+
# if [ $((($i + 1) % 8)) == 0 ]; then
31+
# echo
32+
# fi
33+
# done
34+
1335
function update-terminal-theme() {
1436
local new_mode="$1"
1537
local quiet=false

rootfs/etc/profile.d/aws.sh

Lines changed: 100 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#!/bin/bash
2+
# shellcheck disable=SC2155
3+
# Above directive suppresses ShellCheck SC2155: Declare and assign separately to avoid masking return values.
4+
# In this script, we do not care about return values, as problems are detected by the resulting empty value.
25

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

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

118-
# Quick check, are we who we say we are?
121+
# If AWS_VAULT is not enabled, clear any setting from it.
122+
[[ "${AWS_VAULT_ENABLED:-false}" == "true" ]] || unset AWS_VAULT
123+
124+
# Quick check, are we who we say we are? Does the current role match the profile?
119125
local profile_arn
120126
local profile_target=${AWS_PROFILE:-${AWS_VAULT:-default}}
121-
if [[ -n $profile_target ]]; then
122-
profile_arn=$(aws --profile "${profile_target}" sts get-caller-identity --output text --query 'Arn' 2>/dev/null | cut -d/ -f1-2)
123-
if [[ $profile_arn == $current_role ]]; then
124-
# Extract profile name from config file:
125-
# 1. For default profile, look for a better name
126-
# 2. Skip identity profiles (ending with -identity), as they are too generic
127-
# 3. Use the first non-default, non-identity profile found
128-
if [[ $profile_target == "default" ]] || [[ $profile_target =~ -identity$ ]]; then
129-
# Make some effort to find a better name for the role, but only check the config file, not credentials.
130-
local config_file="${AWS_CONFIG_FILE:-\~/.aws/config}"
131-
if [[ -r $config_file ]]; then
132-
# Assumed roles in AWS config file use the role ARN, not the assumed role ARN, so adjust accordingly.
133-
local role_arn=$(printf "%s" "$current_role" | sed 's/:sts:/:iam:/g' | sed 's,:assumed-role/,:role/,')
134-
role_name=($(crudini --get --format=lines "$config_file" | grep "$role_arn" | cut -d' ' -f 3))
135-
for rn in "${role_name[@]}"; do
136-
if [[ $rn == "default" ]] || [[ $rn =~ -identity$ ]]; then
127+
# Remove the session name from the profile target role, if present
128+
profile_arn=$(aws --profile "${profile_target}" sts get-caller-identity --output text --query 'Arn' 2>/dev/null | cut -d/ -f1-2)
129+
# The main way there would be a mismatch is if AWS_VAULT is set or there are API keys in the environment
130+
if [[ "$profile_arn" == "$current_role" ]]; then
131+
# If we are here, then the current role matches the assigned profile. That is a good thing.
132+
# However, the profile name may not be the best name for the role. If it is too generic, try to find a better name.
133+
# Extract profile name from config file:
134+
# 1. For default profile, look for a better name
135+
# 2. Skip identity profiles (ending with -identity), as they are too generic
136+
# 3. Use the first non-default, non-identity profile found
137+
if [[ $profile_target == "default" ]] || [[ $profile_target =~ -identity$ ]]; then
138+
local backup_name="$profile_target"
139+
# Make some effort to find a better name for the role, but only check the config file, not credentials.
140+
local config_file="${AWS_CONFIG_FILE:-${GEODESIC_AWS_HOME}/config}"
141+
if [[ -r $config_file ]]; then
142+
# Is this a normal IAM role or an Identity Center permissions set role?
143+
if [[ $current_role =~ AWSReservedSSO_[^_]+_[0-9a-f]+$ ]]; then
144+
# This is an Identity Center permissions set role
145+
# current_role is "arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_IdentityAdminRoleAccess_5c90026c17fbd1c2"
146+
147+
# Extract account ID using cut
148+
local account_id=$(echo "$current_role" | cut -d':' -f5)
149+
150+
# Extract the full role part
151+
local role_part=$(echo "$current_role" | cut -d':' -f6) # This gets everything after the 5th colon
152+
153+
# Extract the role name by isolating it from boilerplate
154+
local sso_role_name=$(echo "$role_part" | cut -d'_' -f2) # This selects the second field delimited by '_'
155+
156+
# Find all profiles that have matching role names
157+
local profile_names=($(crudini --get --format=lines "$config_file" | grep "$sso_role_name" | cut -d' ' -f 3))
158+
local profile_name
159+
for profile_name in "${profile_names[@]}"; do
160+
# Skip the generic profiles
161+
if [[ "$profile_name" == "default" ]] || [[ "$profile_name" =~ -identity$ ]]; then
137162
continue
138-
else
139-
export ASSUME_ROLE=$rn
163+
fi
164+
if [[ "$account_id" == "$(crudini --get "$config_file" "profile $profile_name" sso_account_id)" ]]; then
165+
export ASSUME_ROLE="$profile_name"
140166
return
141167
fi
142168
done
169+
export ASSUME_ROLE="$backup_name"
170+
return
143171
fi
144-
else
145-
export ASSUME_ROLE="$profile_target"
146-
return
172+
173+
# Normal IAM role
174+
# Assumed roles in AWS config file use the role ARN, not the assumed role ARN, so adjust accordingly.
175+
local role_arn=$(printf "%s" "$current_role" | sed 's/:sts:/:iam:/g' | sed 's,:assumed-role/,:role/,')
176+
role_names=($(crudini --get --format=lines "$config_file" | grep "$role_arn" | cut -d' ' -f 3))
177+
for rn in "${role_names[@]}"; do
178+
if [[ $rn == "default" ]] || [[ $rn =~ -identity$ ]]; then
179+
continue
180+
else
181+
export ASSUME_ROLE=$rn
182+
return
183+
fi
184+
done
147185
fi
148186
fi
149-
echo "* $(red Profile is set to $profile_target but current role does not match:)"
150-
echo "* $(red $current_role)"
187+
# could not find a better match, so just use the generic profile name
188+
export ASSUME_ROLE="$profile_target"
189+
return
190+
fi
191+
192+
# If we are here, then the current role is not what we would expect from the AWS_PROFILE setting.
193+
# If AWS_PROFILE is unset, then we forgive the current role not being the default role.
194+
# Otherwise, we warn about a mismatch.
195+
if [[ -n $AWS_PROFILE ]]; then
196+
red "* AWS Credentials Mismatch! AWS_PROFILE is set to $AWS_PROFILE"
197+
red "* That profile selects role $profile_arn"
198+
red "* But STS reports current role is $current_role"
199+
export ASSUME_ROLE=$(red-n '!mixed!')
200+
return
201+
elif [[ -n $AWS_VAULT ]]; then
202+
red "* AWS Credentials Mismatch! AWS_VAULT claims to have set role to profile $AWS_VAULT"
203+
red "* That profile selects role $profile_arn"
204+
red "* But STS reports current role is $current_role"
205+
red "* "
151206
export ASSUME_ROLE=$(red-n '!mixed!')
152207
return
153208
fi
154209

210+
# If we are here, then we are not using AWS_VAULT or AWS_PROFILE, and the current role does not match the default profile.
211+
# This is likely because we are using API keys directly in the environment or credentials file.
212+
# Try to figure out a better name for the role.
213+
155214
# saml2aws will store the assumed role from sign-in as x_principal_arn in credentials file
156215
# Default values from https://awscli.amazonaws.com/v2/documentation/api/latest/topic/config-vars.html
157-
local creds_file="${AWS_SHARED_CREDENTIALS_FILE:-\~/.aws/credentials}"
216+
local creds_file="${AWS_SHARED_CREDENTIALS_FILE:-${GEODESIC_AWS_HOME}/credentials}"
158217
if [[ -r $creds_file ]]; then
159218
role_name=$(crudini --get --format=lines "${creds_file}" | grep "$current_role" | head -1 | cut -d' ' -f 2)
160219
fi
161220

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

229+
# If we still don't have a profile name, make one up.
170230
if [[ -z $role_name ]]; then
171231
if [[ "$role_arn" =~ "role/OrganizationAccountAccessRole" ]]; then
172232
role_name="$(printf "%s" "$role_arn" | cut -d: -f 5):OrgAccess"
173-
echo "* $(red "Could not find profile name for ${role_arn} ; calling it \"${role_name}\"")" >&2
233+
elif [[ $current_role =~ AWSReservedSSO_[^_]+_[0-9a-f]+$ ]]; then
234+
# This is an Identity Center permissions set role
235+
# current_role is "arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_IdentityAdminRoleAccess_5c90026c17fbd1c2"
236+
# Extract account ID using cut
237+
local account_id=$(echo "$current_role" | cut -d':' -f5)
238+
# Extract the full role part
239+
local role_part=$(echo "$current_role" | cut -d':' -f6) # This gets everything after the 5th colon
240+
# Extract the role name by isolating it from boilerplate
241+
local sso_role_name=$(echo "$role_part" | cut -d'_' -f2) # This selects the second field delimited by '_'
242+
role_name="${account_id}:${sso_role_name}"
174243
else
175244
role_name="$(printf "%s" "$role_arn" | cut -d/ -f 2)"
176-
echo "* $(green "Could not find profile name for ${role_arn} ; calling it \"${role_name}\"")" >&2
177245
fi
246+
echo "* $(green "Could not find profile name for ${role_arn} ; calling it \"${role_name}\"")" >&2
178247
fi
179248
export ASSUME_ROLE="$role_name"
180249
}
@@ -186,7 +255,7 @@ function refresh_current_aws_role_if_needed() {
186255
local is_exported="^declare -[^ x]*x[^ x]* "
187256
local aws_profile=$(declare -p AWS_PROFILE 2>/dev/null)
188257
[[ $aws_profile =~ $is_exported ]] || aws_profile=""
189-
local credentials_mtime=$(stat -c "%Y" ${AWS_SHARED_CREDENTIALS_FILE:-"~/.aws/credentials"} 2>/dev/null)
258+
local credentials_mtime=$(stat -c "%Y" "${AWS_SHARED_CREDENTIALS_FILE:-${GEODESIC_AWS_HOME}/credentials}" 2>/dev/null)
190259
local role_fingerprint="${aws_profile}/${credentials_mtime}/${AWS_ACCESS_KEY_ID}"
191260
if [[ $role_fingerprint != $GEODESIC_AWS_ROLE_CACHE ]]; then
192261
export_current_aws_role

rootfs/etc/profile.d/chamber.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# This is a workaround for https://github.com/moby/buildkit/issues/5775
2+
#
3+
# The `chamber` command (https://github.com/segmentio/chamber) is a CLI for managing secrets.
4+
# It is installed in Geodesic and referenced in various documentation.
5+
#
6+
# Chamber works with AWS SSM Parameter store to save encrypted parameters.
7+
# By default, in uses a KMS key with the alias `parameter_store_key`
8+
# to encrypt and decrypt the parameters. Geodesic supports using
9+
# the AWS default key, with alias `ssm`, or a custom key.
10+
#
11+
# However, due to the issue with buildkit mentioned above,
12+
# setting the required environment variable in the Dockerfile
13+
# leads to a warning about it being a secret stored in the image.
14+
#
15+
# So, as a workaround, we allow you to set `CHAMBER_KMS_ALIAS` instead,
16+
# and we will set the `CHAMBER_KMS_KEY_ALIAS` environment variable for you here.
17+
#
18+
19+
if [[ -z "$CHAMBER_KMS_KEY_ALIAS" ]] && [[ -n "$CHAMBER_KMS_ALIAS" ]]; then
20+
export CHAMBER_KMS_KEY_ALIAS="$CHAMBER_KMS_ALIAS"
21+
unset CHAMBER_KMS_ALIAS
22+
fi

0 commit comments

Comments
 (0)