Skip to content

Commit a645591

Browse files
authored
Merge pull request #7 from abhay-krishna/hybrid-nodes-setup-scripts
Add Hybrid nodes setup scripts to bootstrap container
2 parents b75ac51 + aa56e08 commit a645591

File tree

6 files changed

+306
-15
lines changed

6 files changed

+306
-15
lines changed

Dockerfile

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,47 @@
1+
FROM public.ecr.aws/amazonlinux/amazonlinux:2023 AS builder
2+
3+
ARG SSM_AGENT_VERSION
4+
ENV SSM_AGENT_VERSION=$SSM_AGENT_VERSION
5+
6+
# Validation
7+
RUN : "${SSM_AGENT_VERSION:?SSM Agent version required to build}"
8+
9+
# SSM Agent is downloaded from eu-north-1 as this region gets new releases of SSM Agent first.
10+
COPY ./hashes/ssm ./hashes
11+
COPY ./gpg-keys/amazon-ssm-agent.gpg ./amazon-ssm-agent.gpg
12+
RUN \
13+
ARCH=$(uname -m | sed 's/aarch64/arm64/' | sed 's/x86_64/amd64/') && \
14+
curl -L "https://s3.eu-north-1.amazonaws.com/amazon-ssm-eu-north-1/${SSM_AGENT_VERSION}/linux_${ARCH}/amazon-ssm-agent.rpm" \
15+
-o "amazon-ssm-agent-${SSM_AGENT_VERSION}.${ARCH}.rpm" && \
16+
grep "amazon-ssm-agent-${SSM_AGENT_VERSION}.${ARCH}.rpm" hashes \
17+
| sha512sum --check - && \
18+
rpm --import amazon-ssm-agent.gpg && \
19+
rpm --checksig "amazon-ssm-agent-${SSM_AGENT_VERSION}.${ARCH}.rpm" && \
20+
dnf install -y "amazon-ssm-agent-${SSM_AGENT_VERSION}.${ARCH}.rpm"
21+
122
FROM public.ecr.aws/amazonlinux/amazonlinux:2023
223

24+
# IMAGE_VERSION is the assigned version of inputs for this image.
25+
ARG IMAGE_VERSION
26+
ENV IMAGE_VERSION=$IMAGE_VERSION
27+
28+
# Validation
29+
RUN : "${IMAGE_VERSION:?IMAGE_VERSION is required to build}"
30+
31+
LABEL "org.opencontainers.image.version"="$IMAGE_VERSION"
32+
333
# Install necessary packages
4-
RUN yum update -y
5-
6-
RUN yum install -y \
7-
aws-cli \
8-
jq \
9-
util-linux \
10-
e2fsprogs \
11-
xfsprogs \
12-
lvm2 \
13-
mdadm && \
14-
yum clean all
34+
RUN dnf update -y && \
35+
dnf install -y \
36+
aws-cli \
37+
jq \
38+
util-linux \
39+
e2fsprogs \
40+
xfsprogs \
41+
lvm2 \
42+
mdadm \
43+
rsync && \
44+
dnf clean all
1545

1646
# Verify that all packages are installed
1747
RUN \
@@ -21,10 +51,16 @@ RUN \
2151
command -v mkfs.ext4 && \
2252
command -v mkfs.xfs && \
2353
command -v lvm && \
24-
command -v mdadm
54+
command -v mdadm && \
55+
command -v rsync
2556

26-
# Copy the wrapper script into the container
57+
# Copy the wrapper script and EKS Hybrid setup scripts into the container
2758
COPY bootstrap-script.sh /usr/local/bin/bootstrap-script.sh
59+
COPY eks-hybrid-ssm-setup.sh /usr/local/bin/eks-hybrid-ssm-setup
60+
COPY eks-hybrid-iam-ra-setup.sh /usr/local/bin/eks-hybrid-iam-ra-setup
61+
62+
# Copy the SSM agent from the builder stage
63+
COPY --from=builder /usr/bin/amazon-ssm-agent /usr/local/bin/amazon-ssm-agent
2864

2965
# Set the wrapper script as the entry point
3066
ENTRYPOINT ["/usr/local/bin/bootstrap-script.sh"]

Makefile

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,18 @@ DISTFILE ?= $(subst /,,$(DESTDIR))/$(subst /,_,$(IMAGE_NAME)).tar.gz
1616
UNAME_ARCH = $(shell uname -m)
1717
ARCH ?= $(lastword $(subst :, ,$(filter $(UNAME_ARCH):%,x86_64:amd64 aarch64:arm64)))
1818

19-
.PHONY: all build dist clean
19+
# SSM_AGENT_VERSION is the SSM Agent's distributed RPM Version to install.
20+
SSM_AGENT_VERSION ?= 3.3.1957.0
21+
22+
TEST_ACTIVATION_ID=abcdef12-3456-7890-abcd-ef1234567890
23+
TEST_ACTIVATION_CODE=abcdef1234567890abcdef
24+
TEST_NODE_CERT=test-certificate
25+
TEST_NODE_KEY=test-key
26+
27+
.PHONY: all build dist check check-ssm-agent check-ssm-setup check-iam-ra-setup clean download-ssm-agent update-ssm-agent
2028

2129
# Run all build tasks for this container image.
22-
all: build
30+
all: build check
2331

2432
# Create a distribution container image tarball for release.
2533
dist: all
@@ -31,7 +39,65 @@ build:
3139
DOCKER_BUILDKIT=1 docker build $(DOCKER_BUILD_FLAGS) \
3240
--tag $(IMAGE_NAME) \
3341
--build-arg IMAGE_VERSION="$(IMAGE_VERSION)" \
42+
--build-arg SSM_AGENT_VERSION="$(SSM_AGENT_VERSION)" \
3443
-f Dockerfile . >&2
3544

45+
# Run checks against the bootstrap container image
46+
check: check-ssm-agent check-ssm-setup check-iam-ra-setup
47+
48+
# Check that the SSM Agent is the expected version.
49+
check-ssm-agent:
50+
@echo "Running SSM version check"
51+
@docker run --rm --entrypoint /usr/bin/bash \
52+
$(IMAGE_NAME) \
53+
-c 'amazon-ssm-agent -version | grep -Fw "$(SSM_AGENT_VERSION)"' >&2
54+
55+
# Check that the SSM setup script doesn't print sensitive input
56+
check-ssm-setup:
57+
@echo "Running SSM setup check"
58+
@OUTPUT=$$(docker run --rm --entrypoint /usr/bin/bash \
59+
$(IMAGE_NAME) \
60+
-c "eks-hybrid-ssm-setup --region=us-west-2 --activation-id=${TEST_ACTIVATION_ID} --activation-code=${TEST_ACTIVATION_CODE} --enable-credentials-file=true 2>&1 || true"); \
61+
if echo "$$OUTPUT" | grep -q "${TEST_ACTIVATION_ID}"; then \
62+
echo "Test failed: hybrid activation ID found in output"; \
63+
exit 1; \
64+
elif echo "$$OUTPUT" | grep -q "${TEST_ACTIVATION_CODE}"; then \
65+
echo "Test failed: hybrid activation code found in output"; \
66+
exit 1; \
67+
else \
68+
echo "Test passed: No sensitive content in output"; \
69+
fi
70+
71+
# Check that the IAM-RA setup script doesn't print sensitive input
72+
check-iam-ra-setup:
73+
@echo "Running IAM-RA setup check"
74+
@OUTPUT=$$(docker run --rm --entrypoint /usr/bin/bash \
75+
$(IMAGE_NAME) \
76+
-c "cp /usr/bin/true /usr/bin/apiclient; eks-hybrid-iam-ra-setup --certificate=${TEST_NODE_CERT} --key=${TEST_NODE_KEY} --dry-run=true 2>&1 || true"); \
77+
if echo "$$OUTPUT" | grep -q "${TEST_NODE_CERT}"; then \
78+
echo "Test failed: certificate content found in output"; \
79+
exit 1; \
80+
elif echo "$$OUTPUT" | grep -q "${TEST_NODE_KEY}"; then \
81+
echo "Test failed: private key content found in output"; \
82+
exit 1; \
83+
else \
84+
echo "Test passed: No sensitive content in output"; \
85+
fi
86+
87+
# Download SSM Agent version SSM_AGENT_VERSION for all architectures.
88+
download-ssm-agent: amazon-ssm-agent-${SSM_AGENT_VERSION}.amd64.rpm amazon-ssm-agent-${SSM_AGENT_VERSION}.arm64.rpm
89+
90+
amazon-ssm-agent-${SSM_AGENT_VERSION}.amd64.rpm:
91+
curl -L "https://s3.eu-north-1.amazonaws.com/amazon-ssm-eu-north-1/${SSM_AGENT_VERSION}/linux_amd64/amazon-ssm-agent.rpm" \
92+
-o "amazon-ssm-agent-${SSM_AGENT_VERSION}.amd64.rpm"
93+
94+
amazon-ssm-agent-${SSM_AGENT_VERSION}.arm64.rpm:
95+
curl -L "https://s3.eu-north-1.amazonaws.com/amazon-ssm-eu-north-1/${SSM_AGENT_VERSION}/linux_arm64/amazon-ssm-agent.rpm" \
96+
-o "amazon-ssm-agent-${SSM_AGENT_VERSION}.arm64.rpm"
97+
98+
# Update the expected hashes of SSM Agent to those for SSM_AGENT_VERSION.
99+
update-ssm-agent: download-ssm-agent
100+
sha512sum amazon-ssm-agent-${SSM_AGENT_VERSION}.*.rpm >hashes/ssm
101+
36102
clean:
37103
rm -f $(DISTFILE)

eks-hybrid-iam-ra-setup.sh

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env bash
2+
3+
exec >&2
4+
set -eu -o pipefail
5+
6+
declare -r SECRETS_DIR="/.bottlerocket/rootfs/root/.aws"
7+
8+
DRY_RUN="false"
9+
for opt in "$@"; do
10+
optarg="$(expr "${opt}" : '[^ =]*[= ]\(.*\)')"
11+
case "${opt}" in
12+
--certificate=*) NODE_CERT_DATA="${optarg}" ;;
13+
--key=*) NODE_KEY_DATA="${optarg}" ;;
14+
--dry-run=*) DRY_RUN="${optarg}" ;;
15+
esac
16+
done
17+
18+
if [ "${DRY_RUN}" = "true" ]; then
19+
mkdir -p "${SECRETS_DIR}"
20+
fi
21+
22+
if [ -z "${NODE_CERT_DATA}" ]; then
23+
echo "Unable to retrieve certificate data for IAM-RA"
24+
echo "Please provide certificate contents as --certificate=<Certificate>"
25+
exit 1
26+
fi
27+
28+
if [ -z "${NODE_KEY_DATA}" ]; then
29+
echo "Unable to retrieve private key data for IAM-RA"
30+
echo "Please provide private key contents as --key=<Key>"
31+
exit 1
32+
fi
33+
34+
if ! [ -d "${SECRETS_DIR}" ]; then
35+
echo "Error: Directory ${SECRETS_DIR} is missing"
36+
exit 1
37+
fi
38+
39+
if ! [ "${DRY_RUN}" = "true" ]; then
40+
context=$(stat -c "%C" "${SECRETS_DIR}" 2>/dev/null || echo "")
41+
if [[ ! "$context" == *":secret_t:"* ]]; then
42+
echo "Error: Directory ${SECRETS_DIR} is not labeled with secret_t"
43+
fi
44+
fi
45+
46+
cat << EOF > "${SECRETS_DIR}/node.crt"
47+
${NODE_CERT_DATA}
48+
EOF
49+
50+
cat << EOF > "${SECRETS_DIR}/node.key"
51+
${NODE_KEY_DATA}
52+
EOF
53+
54+
variant_id="$(apiclient get os.variant_id | jq -r '.os.variant_id')"
55+
version_id="$(apiclient get os.version_id | jq -r '.os.version_id')"
56+
apiclient set \
57+
"settings.kubernetes.node-labels.\"os.bottlerocket.aws/variant\""="${variant_id}" \
58+
"settings.kubernetes.node-labels.\"os.bottlerocket.aws/version\""="${version_id}"

eks-hybrid-ssm-setup.sh

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env bash
2+
3+
exec >&2
4+
set -eu -o pipefail
5+
6+
HOST_ROOTFS="/.bottlerocket/rootfs"
7+
SSM_AGENT_PERSISTENT_STATE_DIR="${HOST_ROOTFS}/local/host-containers/control/ssm"
8+
SSM_AGENT_REGISTRATION="${SSM_AGENT_PERSISTENT_STATE_DIR}/registration"
9+
mkdir -p "${SSM_AGENT_PERSISTENT_STATE_DIR}"
10+
ENABLE_CREDENTIALS_FILE="false"
11+
SSM_ACTIVATION_ID_REGEX="^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
12+
13+
for opt in "$@"; do
14+
optarg="$(expr "${opt}" : '[^ =]*[= ]\(.*\)')"
15+
case "${opt}" in
16+
--region=*) AWS_REGION="${optarg}" ;;
17+
--activation-code=*) SSM_ACTIVATION_CODE="${optarg}" ;;
18+
--activation-id=*) SSM_ACTIVATION_ID="${optarg}" ;;
19+
--enable-credentials-file=*) ENABLE_CREDENTIALS_FILE="${optarg}" ;;
20+
esac
21+
done
22+
23+
validate_activation_id() {
24+
if ! [[ "${SSM_ACTIVATION_ID}" =~ ${SSM_ACTIVATION_ID_REGEX} ]]; then
25+
echo "Error: Invalid activation ID format" >&2
26+
exit 1
27+
fi
28+
}
29+
30+
register_hybrid_node() {
31+
amazon-ssm-agent \
32+
-register \
33+
-region "${AWS_REGION}" \
34+
-code "${SSM_ACTIVATION_CODE}" \
35+
-id "${SSM_ACTIVATION_ID}" \
36+
-disableSimilarityCheck
37+
}
38+
39+
persist_ssm_state() {
40+
rsync -aq \
41+
/var/lib/amazon/ssm/ \
42+
"${SSM_AGENT_PERSISTENT_STATE_DIR}"
43+
}
44+
45+
set_hostname_settings() {
46+
local ssm_node_id
47+
local cluster_name
48+
local variant_id
49+
local version_id
50+
ssm_node_id="$(jq -r '.ManagedInstanceID' "${SSM_AGENT_PERSISTENT_STATE_DIR}/registration")"
51+
cluster_name="$(apiclient get settings.kubernetes.cluster-name | jq -r ".settings.kubernetes.\"cluster-name\"")"
52+
variant_id="$(apiclient get os.variant_id | jq -r '.os.variant_id')"
53+
version_id="$(apiclient get os.version_id | jq -r '.os.version_id')"
54+
55+
apiclient set \
56+
network.hostname="${ssm_node_id}" \
57+
kubernetes.hostname-override="${ssm_node_id}" \
58+
kubernetes.provider-id="eks-hybrid:///${AWS_REGION}/${cluster_name}/${ssm_node_id}" \
59+
"settings.kubernetes.node-labels.\"os.bottlerocket.aws/variant\""="${variant_id}" \
60+
"settings.kubernetes.node-labels.\"os.bottlerocket.aws/version\""="${version_id}"
61+
}
62+
63+
symlink_aws_creds() {
64+
local control_aws_dir
65+
local control_aws_dir_relative_to_host
66+
local control_creds
67+
local host_creds
68+
local hybrid_nodes_pod_identity_aws_dir
69+
control_aws_dir="${HOST_ROOTFS}/run/host-containerd/io.containerd.runtime.v2.task/default/control/rootfs/root/.aws"
70+
control_aws_dir_relative_to_host="/run/host-containerd/io.containerd.runtime.v2.task/default/control/rootfs/root/.aws"
71+
control_creds="${control_aws_dir}/credentials"
72+
host_creds="${HOST_ROOTFS}/root/.aws/credentials"
73+
ln -srnf "${control_creds}" "${host_creds}"
74+
if [ "${ENABLE_CREDENTIALS_FILE}" = "true" ]; then
75+
hybrid_nodes_pod_identity_aws_dir="${HOST_ROOTFS}/var/eks-hybrid/.aws"
76+
mkdir -p "$(dirname "${hybrid_nodes_pod_identity_aws_dir}")"
77+
ln -sf "${control_aws_dir_relative_to_host}" "${hybrid_nodes_pod_identity_aws_dir}"
78+
fi
79+
}
80+
81+
if [ ! -s "${SSM_AGENT_REGISTRATION}" ]; then
82+
validate_activation_id
83+
register_hybrid_node
84+
persist_ssm_state
85+
set_hostname_settings
86+
fi
87+
88+
symlink_aws_creds

gpg-keys/amazon-ssm-agent.gpg

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
-----BEGIN PGP PUBLIC KEY BLOCK-----
2+
Version: GnuPG v2.0.22 (GNU/Linux)
3+
4+
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
39+
E6+O8RI2451Dcg==
40+
=aDkv
41+
-----END PGP PUBLIC KEY BLOCK-----

hashes/ssm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
514cf1e5e0c38a0d2f43ac9f5e9ef18aa77044fdc4807a49f6451aeef906efbedd88ccfa4c82aafcec88a86f4de92c8ff85c5cba67d88b6ed8844703af732715 amazon-ssm-agent-3.3.1957.0.amd64.rpm
2+
37af64673c097e7f83ee85ba49ef1f23bf319cb9480ea91657651829e1e07fdcf4f207059332b05ae598ecb35ba01c1fe5e5cd7a7d52f7aa58570382b1260bec amazon-ssm-agent-3.3.1957.0.arm64.rpm

0 commit comments

Comments
 (0)