diff --git a/pkg/provider/aws/action/openshift-snc/cloud-config b/pkg/provider/aws/action/openshift-snc/cloud-config index 51f98a213..c840adb02 100644 --- a/pkg/provider/aws/action/openshift-snc/cloud-config +++ b/pkg/provider/aws/action/openshift-snc/cloud-config @@ -1,23 +1,17 @@ #cloud-config bootcmd: + - 'echo "bootcmd executed by service: $(ps -o comm= $PPID)" > /tmp/bootcmd_proof.txt' # Resize the partition (4 = /dev/nvme0n1p4 typically) - growpart /dev/nvme0n1 4 - # Resize the XFS filesystem on /sysroot - - xfs_growfs /sysroot runcmd: + - 'echo "runcmd executed by service: $(ps -o comm= $PPID)" > /tmp/runcmd_proof.txt' - systemctl enable --now kubelet - - export PS=$(podman run --rm docker.io/amazon/aws-cli ssm get-parameter --name "{{ .SSMPullSecretName }}" --with-decryption --query "Parameter.Value" --output text) - - echo ${PS} > /opt/crc/pull-secret - - chmod 0644 /opt/crc/pull-secret - - export KP=$(podman run --rm docker.io/amazon/aws-cli ssm get-parameter --name "{{ .SSMKubeAdminPasswordName }}" --with-decryption --query "Parameter.Value" --output text) - - echo ${KP} > /opt/crc/pass_kubeadmin - - chmod 0644 /opt/crc/pass_kubeadmin - - export DV=$(podman run --rm docker.io/amazon/aws-cli ssm get-parameter --name "{{ .SSMDeveloperPasswordName }}" --with-decryption --query "Parameter.Value" --output text) - - echo ${DV} > /opt/crc/pass_developer - - chmod 0644 /opt/crc/pass_developer - - echo "{{ .PublicIP }}" > /opt/crc/eip - - chmod 0644 /opt/crc/eip + - /usr/local/bin/mapt-crc-aws-fetch-secrets-workaround.sh write_files: +- path: /opt/crc/eip + content: "{{ .PublicIP }}" + owner: root:root + permissions: '0644' - path: /home/core/.ssh/authorized_keys content: {{ .PubKey }} owner: {{ .Username }} @@ -29,6 +23,150 @@ write_files: - content: | CRC_SELF_SUFFICIENT=1 CRC_NETWORK_MODE_USER=0 + CRC_SOURCE=mapt/snc owner: root:root path: /etc/sysconfig/crc-env permissions: '0644' +- owner: root:root + path: /usr/local/bin/mapt-crc-aws-fetch-secrets-workaround.sh + permissions: '0755' + content: | + #!/bin/bash + if [[ -f /usr/local/bin/crc-aws-fetch-secrets.sh ]]; then + script=/usr/local/bin/crc-aws-fetch-secrets.sh + else + echo "crc-aws-fetch-secrets.sh not found, falling back to MAPT's copy" + script=/usr/local/bin/mapt-crc-aws-fetch-secrets.sh + fi + + exec "$script" "{{ .SSMPullSecretName }}" "{{ .SSMKubeAdminPasswordName }}" "{{ .SSMDeveloperPasswordName }}" +- owner: root:root + path: /usr/local/bin/mapt-crc-aws-fetch-secrets.sh + permissions: '0755' + content: | + #!/bin/bash + + set -o pipefail + set -o errexit + set -o nounset + set -o errtrace + set -x + + # set -x is safe, the secrets are passed via stdin + + AWS_CLI_IMG=docker.io/amazon/aws-cli + MIN_CHAR_COUNT=8 # minimum number of chars for the secret to be + # assumed valid + + umask 0077 # 0600 file permission for secrets + + PULL_SECRETS_KEY=${1:-} + KUBEADM_PASS_KEY=${2:-} + DEVELOPER_PASS_KEY=${3:-} + + if [[ -z "$PULL_SECRETS_KEY" || -z "$KUBEADM_PASS_KEY" || -z "$DEVELOPER_PASS_KEY" ]]; then + echo "ERROR: expected to receive 3 parameters: PULL_SECRETS_KEY KUBEADM_PASS_KEY DEVELOPER_PASS_KEY" + exit 1 + fi + + SECONDS=0 + podman pull --quiet "$AWS_CLI_IMG" + echo "Took $SECONDS seconds to pull the $AWS_CLI_IMG" + + wait_imds_available_and_get_region() { + total_timeout_minutes=5 + retry_interval_seconds=5 + + IMDS_TOKEN_COMMAND=( + curl + --connect-timeout 1 + -X PUT + "http://169.254.169.254/latest/api/token" + -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" + -Ssf + ) + success=false + deadline=$(( $(date +%s) + (total_timeout_minutes * 60) )) + while [[ $(date +%s) -lt $deadline ]]; do + # By placing the command in an 'if' condition, we can test its exit code + # without triggering 'set -e'. The output is still captured. + if TOKEN=$("${IMDS_TOKEN_COMMAND[@]}"); then + # This block only runs if the curl command succeeds (exit code 0) + success=true + echo "Successfully fetched token." >&2 + break # Exit the loop on success + fi + + # This block runs if the curl command fails + echo "Failed to connect. Retrying in $retry_interval_seconds seconds..." >&2 + sleep "$retry_interval_seconds" + done + + if [[ "$success" != "true" ]]; then + echo "ERROR: Could not fetch token after $total_timeout_minutes minutes." >&2 + return 1 + fi + + # Then, use the token to get the region + echo "Fetching the AWS region ..." + curl -Ssf -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region > /tmp/aws-region + echo >> /tmp/aws-region # add EOL at EOF, for consistency + echo "AWS region: $(< /tmp/aws-region)" + } + + ( + set +x # disable the xtrace as the token would be leaked + echo "Waiting for the AWS IMDS service to be available ..." + SECONDS=0 + wait_imds_available_and_get_region + echo "Took $SECONDS for the IMDS service to become available." + ) + + missing_secrets=0 + + save_secret() { + name=$1 + key=$2 + dest=$3 + + # --log-driver=none avoids that the journal captures the stdout + # logs of podman and leaks the passwords in the journal ... + if ! podman run \ + --name "cloud-init-fetch-$name" \ + --env AWS_REGION="$(< /tmp/aws-region)" \ + --log-driver=none \ + --rm \ + "$AWS_CLI_IMG" \ + ssm get-parameter \ + --name "$key" \ + --with-decryption \ + --query "Parameter.Value" \ + --output text \ + > "${dest}.tmp" + then + rm -f "${dest}.tmp" + echo "ERROR: failed to get the '$name' secret ... (fetched from $key)" + ((missing_secrets += 1)) + return + fi + char_count=$(wc -c < "${dest}.tmp") + if (( char_count < MIN_CHAR_COUNT )); then + echo "ERROR: the content of the '$name' secret is too short ... (fetched from $key)" + rm -f "${dest}.tmp" + ((missing_secrets += 1)) + return + fi + + mv "${dest}.tmp" "${dest}" # atomic creation of the file + } + + save_secret "pull-secrets" "$PULL_SECRETS_KEY" /opt/crc/pull-secret + save_secret "kubeadmin-pass" "$KUBEADM_PASS_KEY" /opt/crc/pass_kubeadmin + save_secret "developer-pass" "$DEVELOPER_PASS_KEY" /opt/crc/pass_developer + + if (( missing_secrets != 0 )); then + echo "ERROR: failed to fetch $missing_secrets secrets ..." + exit 1 + fi + + exit 0 diff --git a/pkg/provider/aws/action/openshift-snc/mapt-crc-aws-fetch-secrets.sh b/pkg/provider/aws/action/openshift-snc/mapt-crc-aws-fetch-secrets.sh new file mode 100644 index 000000000..19fa42615 --- /dev/null +++ b/pkg/provider/aws/action/openshift-snc/mapt-crc-aws-fetch-secrets.sh @@ -0,0 +1,126 @@ +#!/bin/bash + +set -o pipefail +set -o errexit +set -o nounset +set -o errtrace +set -x + +# set -x is safe, the secrets are passed via stdin + +AWS_CLI_IMG=docker.io/amazon/aws-cli +MIN_CHAR_COUNT=8 # minimum number of chars for the secret to be + # assumed valid + +umask 0077 # 0600 file permission for secrets + +PULL_SECRETS_KEY=${1:-} +KUBEADM_PASS_KEY=${2:-} +DEVELOPER_PASS_KEY=${3:-} + +if [[ -z "$PULL_SECRETS_KEY" || -z "$KUBEADM_PASS_KEY" || -z "$DEVELOPER_PASS_KEY" ]]; then + echo "ERROR: expected to receive 3 parameters: PULL_SECRETS_KEY KUBEADM_PASS_KEY DEVELOPER_PASS_KEY" + exit 1 +fi + +SECONDS=0 +podman pull --quiet "$AWS_CLI_IMG" +echo "Took $SECONDS seconds to pull the $AWS_CLI_IMG" + +wait_imds_available_and_get_region() { + total_timeout_minutes=5 + retry_interval_seconds=5 + + IMDS_TOKEN_COMMAND=( + curl + --connect-timeout 1 + -X PUT + "http://169.254.169.254/latest/api/token" + -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" + -Ssf + ) + success=false + deadline=$(( $(date +%s) + (total_timeout_minutes * 60) )) + while [[ $(date +%s) -lt $deadline ]]; do + # By placing the command in an 'if' condition, we can test its exit code + # without triggering 'set -e'. The output is still captured. + if TOKEN=$("${IMDS_TOKEN_COMMAND[@]}"); then + # This block only runs if the curl command succeeds (exit code 0) + success=true + echo "Successfully fetched token." >&2 + break # Exit the loop on success + fi + + # This block runs if the curl command fails + echo "Failed to connect. Retrying in $retry_interval_seconds seconds..." >&2 + sleep "$retry_interval_seconds" + done + + if [[ "$success" != "true" ]]; then + echo "ERROR: Could not fetch token after $total_timeout_minutes minutes." >&2 + return 1 + fi + + # Then, use the token to get the region + echo "Fetching the AWS region ..." + curl -Ssf -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region > /tmp/aws-region + echo >> /tmp/aws-region # add EOL at EOF, for consistency + echo "AWS region: $(< /tmp/aws-region)" +} + +( + set +x # disable the xtrace as the token would be leaked + echo "Waiting for the AWS IMDS service to be available ..." + SECONDS=0 + wait_imds_available_and_get_region + echo "Took $SECONDS for the IMDS service to become available." +) + +missing_secrets=0 + +save_secret() { + name=$1 + key=$2 + dest=$3 + + # --log-driver=none avoids that the journal captures the stdout + # logs of podman and leaks the passwords in the journal ... + if ! podman run \ + --name "cloud-init-fetch-$name" \ + --env AWS_REGION="$(< /tmp/aws-region)" \ + --log-driver=none \ + --rm \ + "$AWS_CLI_IMG" \ + ssm get-parameter \ + --name "$key" \ + --with-decryption \ + --query "Parameter.Value" \ + --output text \ + > "${dest}.tmp" + then + rm -f "${dest}.tmp" + echo "ERROR: failed to get the '$name' secret ... (fetched from $key)" + ((missing_secrets += 1)) + return + fi + char_count=$(wc -c < "${dest}.tmp") + if (( char_count < MIN_CHAR_COUNT )); then + echo "ERROR: the content of the '$name' secret is too short ... (fetched from $key)" + rm -f "${dest}.tmp" + ((missing_secrets += 1)) + return + fi + + mv "${dest}.tmp" "${dest}" # atomic creation of the file +} + +save_secret "pull-secrets" "$PULL_SECRETS_KEY" /opt/crc/pull-secret +save_secret "kubeadmin-pass" "$KUBEADM_PASS_KEY" /opt/crc/pass_kubeadmin +save_secret "developer-pass" "$DEVELOPER_PASS_KEY" /opt/crc/pass_developer + +if (( missing_secrets != 0 )); then + echo "ERROR: failed to fetch $missing_secrets secrets ..." + exit 1 +fi + +exit 0