Content-Type: multipart/mixed; boundary="==BOUNDARY==" MIME-Version: 1.0 --==BOUNDARY== Content-Type: text/x-shellscript; charset="us-ascii" #!/bin/bash set -e # Run Order: 1 # Run Frequency: only once, on first boot # Tasks: # - Install Dependencies # - Make users and directories # - Install download, unzip, and setup vault bin # Note: dollar-sign curly braces are template values from Terraform. # Non curly brace ones are normal bash variables... sudo apt update -y && sudo apt install gpg wget -y # Make the user useradd --system --shell /sbin/nologin vault # Make the directories mkdir -p /opt/vault mkdir -p /opt/vault/bin mkdir -p /opt/vault/config mkdir -p /opt/vault/tls # Give corret permissions chmod 755 /opt/vault chmod 755 /opt/vault/bin # Change ownership to vault user chown -R vault:vault /opt/vault # === Install Vault via apt === # Get the keyring wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg # Verify the keyring gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint # Check the exit status of the last command if [ $? -eq 0 ]; then # If the exit status is 0 (which means the previous command was successful), add the repo echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list # Install the vault sudo apt update && sudo apt install vault -y else # If the exit status is not 0 (which means the previous command failed), print an error message and exit echo "Keyring verification failed. Exiting." exit 1 fi # === Install AWS CLI === # Either x86_64 or aarm64 Architecture=$(uname -m) printf '%s\n' "Installing / Updating AWS-Cli" "-----------------" "$Architecture" echo "downloading..." curl "https://awscli.amazonaws.com/awscli-exe-linux-$Architecture.zip" -o "awscliv2.zip" sleep 1 # create public gpg key cat < aws-cli-public.gpg -----BEGIN PGP PUBLIC KEY BLOCK----- mQINBF2Cr7UBEADJZHcgusOJl7ENSyumXh85z0TRV0xJorM2B/JL0kHOyigQluUG ZMLhENaG0bYatdrKP+3H91lvK050pXwnO/R7fB/FSTouki4ciIx5OuLlnJZIxSzx PqGl0mkxImLNbGWoi6Lto0LYxqHN2iQtzlwTVmq9733zd3XfcXrZ3+LblHAgEt5G TfNxEKJ8soPLyWmwDH6HWCnjZ/aIQRBTIQ05uVeEoYxSh6wOai7ss/KveoSNBbYz gbdzoqI2Y8cgH2nbfgp3DSasaLZEdCSsIsK1u05CinE7k2qZ7KgKAUIcT/cR/grk C6VwsnDU0OUCideXcQ8WeHutqvgZH1JgKDbznoIzeQHJD238GEu+eKhRHcz8/jeG 94zkcgJOz3KbZGYMiTh277Fvj9zzvZsbMBCedV1BTg3TqgvdX4bdkhf5cH+7NtWO lrFj6UwAsGukBTAOxC0l/dnSmZhJ7Z1KmEWilro/gOrjtOxqRQutlIqG22TaqoPG fYVN+en3Zwbt97kcgZDwqbuykNt64oZWc4XKCa3mprEGC3IbJTBFqglXmZ7l9ywG EEUJYOlb2XrSuPWml39beWdKM8kzr1OjnlOm6+lpTRCBfo0wa9F8YZRhHPAkwKkX XDeOGpWRj4ohOx0d2GWkyV5xyN14p2tQOCdOODmz80yUTgRpPVQUtOEhXQARAQAB tCFBV1MgQ0xJIFRlYW0gPGF3cy1jbGlAYW1hem9uLmNvbT6JAlQEEwEIAD4CGwMF CwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQT7Xbd/1cEYuAURraimMQrMRnJHXAUC ZMKcEgUJCSEf3QAKCRCmMQrMRnJHXCilD/4vior9J5tB+icri5WbDudS3ak/ve4q XS6ZLm5S8l+CBxy5aLQUlyFhuaaEHDC11fG78OduxatzeHENASYVo3mmKNwrCBza NJaeaWKLGQT0MKwBSP5aa3dva8P/4oUP9GsQn0uWoXwNDWfrMbNI8gn+jC/3MigW vD3fu6zCOWWLITNv2SJoQlwILmb/uGfha68o4iTBOvcftVRuao6DyqF+CrHX/0j0 klEDQFMY9M4tsYT7X8NWfI8Vmc89nzpvL9fwda44WwpKIw1FBZP8S0sgDx2xDsxv L8kM2GtOiH0cHqFO+V7xtTKZyloliDbJKhu80Kc+YC/TmozD8oeGU2rEFXfLegwS zT9N+jB38+dqaP9pRDsi45iGqyA8yavVBabpL0IQ9jU6eIV+kmcjIjcun/Uo8SjJ 0xQAsm41rxPaKV6vJUn10wVNuhSkKk8mzNOlSZwu7Hua6rdcCaGeB8uJ44AP3QzW BNnrjtoN6AlN0D2wFmfE/YL/rHPxU1XwPntubYB/t3rXFL7ENQOOQH0KVXgRCley sHMglg46c+nQLRzVTshjDjmtzvh9rcV9RKRoPetEggzCoD89veDA9jPR2Kw6RYkS XzYm2fEv16/HRNYt7hJzneFqRIjHW5qAgSs/bcaRWpAU/QQzzJPVKCQNr4y0weyg B8HCtGjfod0p1A== =gdMc -----END PGP PUBLIC KEY BLOCK----- EOF gpg --import aws-cli-public.gpg curl -o awscliv2.sig https://awscli.amazonaws.com/awscli-exe-linux-$Architecture.zip.sig gpg --verify awscliv2.sig awscliv2.zip # -u for overwrite / update unzip -u awscliv2.zip sudo ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update aws --version # give ownership to the vault user chown vault:vault /opt/vault/bin/vault # create a symlink ln -s /opt/vault/bin/vault /usr/local/bin/vault # allow vault permissions to use mlock and prevent memory from swapping to disk setcap cap_ipc_lock=+ep /opt/vault/bin/vault # cleanup files rm awscliv2.zip rm aws-cli-public.gpg rm awscliv2.sig --==BOUNDARY== Content-Type: text/x-shellscript; charset="us-ascii" #!/bin/bash set -e # Run Order: 2 # Run Frequency: only once, on first boot # Tasks: # - Fetch needed data # - Create Self-Signed Certificate and Key INSTANCE_IP_ADDR=$(curl http://169.254.169.254/latest/meta-data/local-ipv4) INSTANCE_DNS_NAME=$(curl http://169.254.169.254/latest/meta-data/local-hostname) # Used for encryption between the load balancer and vault instances. # Th other alternatives are either creating an entire, private CA and hoping AWS # eventually adds the ability to add trusted CAs to load balancers... # ...or paying $400/month base for the ACM private CA. openssl req -x509 -sha256 -nodes \ -newkey rsa:4096 -days 3650 \ -keyout /opt/vault/tls/vault.key -out /opt/vault/tls/vault.crt \ -subj "/CN=$INSTANCE_DNS_NAME" \ -extensions san \ -config <(cat /etc/pki/tls/openssl.cnf <(echo -e "\n[san]\nsubjectAltName=DNS:$INSTANCE_DNS_NAME,IP:$INSTANCE_IP_ADDR")) chown vault:vault /opt/vault/tls/vault.key chown vault:vault /opt/vault/tls/vault.crt chmod 640 /opt/vault/tls/vault.key chmod 644 /opt/vault/tls/vault.crt # Trust the certificate cp /opt/vault/tls/vault.crt /etc/pki/tls/certs/vault.crt --==BOUNDARY== Content-Type: text/x-shellscript; charset="us-ascii" #!/bin/bash set -e # Run Order: 3 # Run Frequency: only once, on first boot # Tasks: # - Make the vault config file # - Make the systemd service file # The vault config file cat > /opt/vault/config/server.hcl <<- EOF cluster_name = "${VAULT_CLUSTER_NAME}" max_lease_ttl = "192h" default_lease_ttl = "192h" ui = "true" # Where can the Vault API be reached? At DNS for the load balancer, or the CNAME created. # Note: this maps to the environment variable VAULT_API_ADDR not VAULT_ADDR api_addr = "https://${VAULT_DNS}" # For forwarding between vault servers. Set to own ip. cluster_addr = "https://INSTANCE_IP_ADDR:8201" # Auto unseal the vault seal "awskms" { region = "${VAULT_CLUSTER_REGION}" kms_key_id = "${VAULT_KMS_KEY_ID}" } # Listener for loopback listener "tcp" { address = "127.0.0.1:8199" tls_disable = "true" } # Listener for private network listener "tcp" { address = "INSTANCE_IP_ADDR:8200" cluster_address = "INSTANCE_IP_ADDR:8201" tls_disable = "false" tls_cert_file = "/opt/vault/tls/vault.crt" tls_key_file = "/opt/vault/tls/vault.key" } storage "dynamodb" { ha_enabled = "true" region = "${VAULT_CLUSTER_REGION}" table = "${VAULT_DYNAMODB_TABLE}" } EOF chown vault:vault /opt/vault/config/server.hcl # The systemd service file cat > /etc/systemd/system/vault.service <<- EOF [Unit] Description=Vault Server on AWS Requires=network-online.target After=network-online.target [Service] User=vault Group=vault ProtectSystem=full ProtectHome=read-only PrivateTmp=yes PrivateDevices=yes SecureBits=keep-caps AmbientCapabilities=CAP_IPC_LOCK Capabilities=CAP_IPC_LOCK+ep CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK NoNewPrivileges=yes ExecStart=/opt/vault/bin/vault server -config=/opt/vault/config/ -log-level=info ExecReload=/bin/kill --signal HUP \$MAINPID KillMode=process KillSignal=SIGINT Restart=on-failure RestartSec=5 TimeoutStopSec=30 StartLimitInterval=60 StartLimitBurst=3 LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF --==BOUNDARY== Content-Type: text/x-shellscript; charset="us-ascii" #!/bin/bash set -e # Run Order: 4 # Run Frequency: only once, on first boot # Tasks: # - Replace values in configuration files with instance metadata # - Start vault INSTANCE_IP_ADDR=$(curl http://169.254.169.254/latest/meta-data/local-ipv4) sed -i -e "s/INSTANCE_IP_ADDR/$INSTANCE_IP_ADDR/g" /opt/vault/config/server.hcl systemctl daemon-reload systemctl enable vault systemctl restart vault --==BOUNDARY== Content-Type: text/x-shellscript; charset="us-ascii" #!/bin/bash set -e # Run Order: 5 # Run Frequency: only once, on first boot # Tasks: # - Initialize Vault # - Create credentials file # - Encrypt the file via KMS # - Send the file to S3 # - Delete the local file # - Erase bash history # Workaround to make sure the vault service is fully initialized. sleep 20 export VAULT_ADDR="http://127.0.0.1:8199" export AWS_DEFAULT_REGION="${VAULT_CLUSTER_REGION}" export VAULT_INITIALIZED=$(vault operator init -status) # avoid non-zero exit status function initialize_vault { # initialize and pipe to file vault operator init > vault_credentials.txt # encrypt it with the KMS key aws kms encrypt --key-id ${VAULT_KMS_KEY_ID} --plaintext fileb://vault_credentials.txt --output text --query CiphertextBlob | base64 --decode > vault_creds_encrypted # send the encrypted file to the s3 bucket aws s3 cp vault_creds_encrypted s3://${VAULT_S3_BUCKET_NAME}/ # cleanup rm vault_credentials.txt rm vault_creds_encrypted history -c history -w } if [ "$VAULT_INITIALIZED" = "Vault is initialized" ]; then echo "Vault is already initialized." else echo "Initializing vault..." initialize_vault fi --==BOUNDARY==--