Skip to content

Commit d90644f

Browse files
authored
Merge pull request #19 from jpculp/userdata-keys
Change admin container logic to use public keys from user-data
2 parents eec7430 + 14064c0 commit d90644f

File tree

6 files changed

+115
-50
lines changed

6 files changed

+115
-50
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# 0.6.0
2+
3+
* Use user-data file rather than IMDS directly to set public keys. ([#19])
4+
5+
[#19]: https://github.com/bottlerocket-os/bottlerocket-admin-container/pull/19
6+
17
# 0.5.0
28

39
* Use /proc to find the bash binary in sheltie. ([#8])

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ RUN test -n "$IMAGE_VERSION"
4848
LABEL "org.opencontainers.image.version"="$IMAGE_VERSION"
4949

5050
RUN yum update -y \
51-
&& yum install -y openssh-server sudo util-linux procps-ng \
51+
&& yum install -y openssh-server sudo util-linux procps-ng jq \
5252
&& yum clean all
5353

5454
COPY --from=builder /opt/bash /opt/bin/

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,33 @@ For more information about how the admin container fits into the Bottlerocket op
1111

1212
You'll need Docker 17.06.2 or later, for multi-stage build support.
1313
Then run `make`!
14+
15+
## Authenticating with the Admin Container
16+
17+
Starting from v0.6.0, users have the option to pass in their own ssh keys rather than the admin container relying on the AWS instance metadata service (IMDS).
18+
19+
Users can add their own keys by populating the admin container's user-data with a base64-encoded JSON block.
20+
If user-data is populated then Bottlerocket will not fetch from IMDS at all, but if user-data is not set then Bottlerocket will continue to use the keys from IMDS.
21+
22+
To use custom public keys for `.ssh/authorized_keys` and/or custom CA keys for `/etc/ssh/trusted_user_ca_keys.pub` you will want to generate a JSON-structure like this:
23+
24+
```
25+
{
26+
"ssh":{
27+
"authorized_keys":[
28+
"ssh-rsa EXAMPLEAUTHORIZEDPUBLICKEYHERE my-key-pair"
29+
],
30+
"trusted_user_ca_keys":[
31+
"ssh-rsa EXAMPLETRUSTEDCAPUBLICKEYHERE authority@ssh-ca.example.com"
32+
]
33+
}
34+
}
35+
```
36+
37+
Once you've created your JSON, you'll need to base64-encode it and set it as the value of the admin host container's user-data setting in your [instance user data toml](https://github.com/bottlerocket-os/bottlerocket#using-user-data).
38+
39+
```
40+
[settings.host-containers.admin]
41+
# ex: echo '{"ssh":{"authorized_keys":[]}}' | base64
42+
user-data = "eyJzc2giOnsiYXV0aG9yaXplZF9rZXlzIjpbXX19"
43+
```

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v0.5.2
1+
v0.6.0

sshd_config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
1414

1515
# SFTP is enabled by default in Amazon Linux 2; keeping that behavior here.
1616
Subsystem sftp /usr/libexec/openssh/sftp-server
17+
18+
# Configured by user data
19+
TrustedUserCAKeys /etc/ssh/trusted_user_ca_keys.pub

start_admin_sshd.sh

Lines changed: 74 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,64 +4,90 @@
44
# SPDX-License-Identifier: Apache-2.0 OR MIT
55
set -e
66

7-
mkdir -p /home/ec2-user/.ssh/
8-
chmod 700 /home/ec2-user/.ssh/
9-
ssh_host_key_dir="/.bottlerocket/host-containers/admin/etc/ssh"
10-
ssh_config_dir="/home/ec2-user/.ssh"
7+
log() {
8+
echo "$*" >&2
9+
}
1110

12-
# Populate authorized_keys with all the public keys found in instance meta-data
13-
# The URLs for keys include an index and the keypair name, e.g.
14-
# http://169.254.169.254/latest/meta-data/public-keys/0=mykeypair/openssh-key
15-
ssh_authorized_keys="${ssh_config_dir}/authorized_keys"
16-
touch ${ssh_authorized_keys}
17-
chmod 600 ${ssh_authorized_keys}
18-
public_key_base_url="http://169.254.169.254/latest/meta-data/public-keys/"
19-
imds_session_token=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
20-
imds_request_add_header="X-aws-ec2-metadata-token: ${imds_session_token}"
21-
public_key_indexes=($(curl -H "${imds_request_add_header}" -sf "${public_key_base_url}" \
22-
| cut -d= -f1 \
23-
| xargs))
11+
declare -r local_user="ec2-user"
12+
declare -r ssh_host_key_dir="/.bottlerocket/host-containers/admin/etc/ssh"
13+
declare -r user_data="/.bottlerocket/host-containers/admin/user-data"
14+
declare -r user_ssh_dir="/home/${local_user}/.ssh"
15+
available_auth_methods=0
2416

25-
for public_key_index in "${public_key_indexes[@]}"; do
26-
public_key_data="$(curl -H "${imds_request_add_header}" -sf "${public_key_base_url}/${public_key_index}/openssh-key")"
27-
if [[ ! "${public_key_data}" =~ ^"ssh" ]]; then
28-
echo "Key ${public_key_data} with index ${public_key_index} looks invalid" >&2
29-
continue
30-
fi
31-
echo "${public_key_data}" >> "${ssh_authorized_keys}"
32-
if ! grep -q "${public_key_data}" "${ssh_authorized_keys}"; then
33-
echo "Failed to write key with index ${public_key_index} to authorized_keys" >&2
34-
continue
35-
fi
36-
done
17+
mkdir -p "${user_ssh_dir}"
18+
chmod 700 "${user_ssh_dir}"
19+
20+
get_user_data_keys() {
21+
# Extract the keys from user-data json
22+
local raw_keys
23+
local key_type="${1:?}"
24+
if ! raw_keys=$(jq --arg key_type "${key_type}" -e -r '.["ssh"][$key_type][]' "${user_data}" 2>/dev/null); then
25+
log "Failed to parse ${key_type} from ${user_data}"
26+
return 1
27+
fi
28+
29+
# Map the keys to avoid improper splitting
30+
local mapped_keys
31+
mapfile -t mapped_keys <<< "${raw_keys}"
32+
33+
# Verify the keys are valid
34+
local key
35+
local -a valid_keys
36+
for key in "${mapped_keys[@]}"; do
37+
if ! echo "${key}" | ssh-keygen -lf - &>/dev/null; then
38+
log "Failed to validate ${key}"
39+
continue
40+
fi
41+
valid_keys+=( "${key}" )
42+
done
43+
44+
( IFS=$'\n'; echo "${valid_keys[*]}" )
45+
}
3746

38-
# If we didn't write any keys at all, there's not much point in continuing
39-
if [ ! -s "${ssh_authorized_keys}" ]; then
40-
echo "Failed to write any valid public keys to authorized_keys" >&2
41-
exit 1
47+
# Populate authorized_keys with all the authorized keys found in user-data
48+
if authorized_keys=$(get_user_data_keys "authorized_keys"); then
49+
ssh_authorized_keys="${user_ssh_dir}/authorized_keys"
50+
touch "${ssh_authorized_keys}"
51+
chmod 600 "${ssh_authorized_keys}"
52+
echo "${authorized_keys}" > "${ssh_authorized_keys}"
53+
((++available_auth_methods))
4254
fi
4355

44-
chown ec2-user -R "${ssh_config_dir}"
56+
# Populate trusted_user_ca_keys with all the authorized keys found in user-data
57+
if trusted_user_ca_keys=$(get_user_data_keys "trusted_user_ca_keys"); then
58+
ssh_trusted_user_ca_keys="/etc/ssh/trusted_user_ca_keys.pub"
59+
touch "${ssh_trusted_user_ca_keys}"
60+
chmod 600 "${ssh_trusted_user_ca_keys}"
61+
echo "${trusted_user_ca_keys}" > "${ssh_trusted_user_ca_keys}"
62+
((++available_auth_methods))
63+
fi
64+
65+
chown "${local_user}" -R "${user_ssh_dir}"
66+
67+
# If there were no successful auth methods, then users cannot authenticate
68+
if [[ "${available_auth_methods}" -eq 0 ]]; then
69+
log "Failed to configure ssh authentication"
70+
fi
4571

4672
# Generate the server keys
4773
mkdir -p "${ssh_host_key_dir}"
4874
for key in rsa ecdsa ed25519; do
49-
# If both of the keys exist, don't overwrite them
50-
if [ -s "${ssh_host_key_dir}/ssh_host_${key}_key" ] && [ -s "${ssh_host_key_dir}/ssh_host_${key}_key.pub" ]; then
51-
echo "${key} key already exists, will use existing key." >&2
52-
continue
53-
fi
75+
# If both of the keys exist, don't overwrite them
76+
if [ -s "${ssh_host_key_dir}/ssh_host_${key}_key" ] &&
77+
[ -s "${ssh_host_key_dir}/ssh_host_${key}_key.pub" ]; then
78+
log "${key} key already exists, will use existing key."
79+
continue
80+
fi
5481

55-
rm -rf \
56-
"${ssh_host_key_dir}/ssh_host_${key}_key" \
57-
"${ssh_host_key_dir}/ssh_host_${key}_key.pub"
58-
if ssh-keygen -t "${key}" -f "${ssh_host_key_dir}/ssh_host_${key}_key" -q -N ""; then
59-
chmod 600 "${ssh_host_key_dir}/ssh_host_${key}_key"
60-
chmod 644 "${ssh_host_key_dir}/ssh_host_${key}_key.pub"
61-
else
62-
echo "Failure to generate host ${key} ssh keys" >&2
63-
exit 1
64-
fi
82+
rm -rf \
83+
"${ssh_host_key_dir}/ssh_host_${key}_key" \
84+
"${ssh_host_key_dir}/ssh_host_${key}_key.pub"
85+
if ssh-keygen -t "${key}" -f "${ssh_host_key_dir}/ssh_host_${key}_key" -q -N ""; then
86+
chmod 600 "${ssh_host_key_dir}/ssh_host_${key}_key"
87+
chmod 644 "${ssh_host_key_dir}/ssh_host_${key}_key.pub"
88+
else
89+
log "Failure to generate host ${key} ssh keys"
90+
fi
6591
done
6692

6793
# Start a single sshd process in the foreground

0 commit comments

Comments
 (0)