From 0511a8db8f208e847037c5b3d56f837a021e77d3 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Tue, 2 Jun 2026 15:01:41 -0400 Subject: [PATCH 1/7] nix: Drop treefmt.nixfmt.package config This is currently a no-op as we are already using nixfmt-rfc-style. This also means we can drop the perSystem definition to a simple attrset which causes a reformat. --- nix/fmt.nix | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/nix/fmt.nix b/nix/fmt.nix index 6adc16281f..e6a72c021d 100644 --- a/nix/fmt.nix +++ b/nix/fmt.nix @@ -1,26 +1,21 @@ { inputs, ... }: { imports = [ inputs.treefmt-nix.flakeModule ]; - perSystem = - { pkgs, ... }: - { - treefmt = { - programs = { - deadnix.enable = true; - nixfmt = { - enable = true; - package = pkgs.nixfmt-rfc-style; - }; - ruff-format.enable = true; - gofmt.enable = true; - }; + perSystem = { + treefmt = { + programs = { + deadnix.enable = true; + gofmt.enable = true; + nixfmt.enable = true; + ruff-format.enable = true; + }; - settings = { - global.excludes = [ - "*.sum" - "vendor/*" - ]; - }; + settings = { + global.excludes = [ + "*.sum" + "vendor/*" + ]; }; }; + }; } From a62cc4528f08fb9f2eb228ad15a41543ce12ba45 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Tue, 2 Jun 2026 15:34:48 -0400 Subject: [PATCH 2/7] nix: Enable treefmt.shfmt We have a bunch of scripts, mixing tabs/spaces and other things is a pain that has long been solved, lets join the club. --- Dockerfile-multigres | 2 +- ansible/files/admin_api_scripts/grow_fs.sh | 76 +- .../admin_api_scripts/manage_readonly_mode.sh | 39 +- .../files/admin_api_scripts/mount-volume.sh | 80 +- .../pg_upgrade_scripts/check.sh | 7 +- .../pg_upgrade_scripts/common.sh | 128 +- .../pg_upgrade_scripts/complete.sh | 346 ++--- .../pg_upgrade_scripts/initiate.sh | 951 +++++++------- .../pg_upgrade_scripts/pgsodium_getkey.sh | 4 +- .../files/admin_api_scripts/unmount-volume.sh | 34 +- ansible/files/start-envoy.sh | 6 +- .../{pgdata-chown => pgdata-chown.sh} | 24 +- .../{pgdata-signal => pgdata-signal.sh} | 32 +- ansible/files/supascan_ami.sh | 10 +- .../systemd-networkd-check-and-fix.sh | 12 +- .../wal_change_ownership.sh | 10 +- .../files/walg_helper_scripts/wal_fetch.sh | 2 +- .../tasks/internal/supabase-admin-agent.yml | 4 +- ci/extensions-diff.sh | 6 +- ci/postgresql-diff.sh | 4 +- docker/nix/build_nix.sh | 2 +- .../{pgctld-wrapper => pgctld-wrapper.sh} | 4 +- ebssurrogate/scripts/chroot-bootstrap-nix.sh | 59 +- ebssurrogate/scripts/qemu-bootstrap-nix.sh | 39 +- .../scripts/surrogate-bootstrap-nix.sh | 101 +- migrations/db/migrate.sh | 48 +- nix/fmt.nix | 2 + nix/init.sh | 10 +- nix/packages/cli-config/pgsodium_getkey.sh | 4 +- .../cli-config/supabase-postgres-init.sh | 211 ++-- nix/tests/util/pgsodium_getkey.sh | 4 +- nix/tests/util/pgsodium_getkey_arb.sh | 2 +- scripts/00-python_install.sh | 2 +- scripts/01-postgres_check.sh | 92 +- scripts/11-lemp.sh | 4 +- scripts/12-ufw-nginx.sh | 2 +- scripts/13-force-ssh-logout.sh | 4 +- scripts/90-cleanup-qemu.sh | 127 +- scripts/90-cleanup.sh | 74 +- scripts/91-log_cleanup.sh | 3 +- scripts/99-img_check.sh | 1109 ++++++++--------- scripts/nix-provision.sh | 73 +- tests/pg_upgrade/debug.sh | 33 +- tests/pg_upgrade/scripts/entrypoint.sh | 6 +- 44 files changed, 1892 insertions(+), 1900 deletions(-) rename ansible/files/supabase_admin_agent_config/{pgdata-chown => pgdata-chown.sh} (57%) rename ansible/files/supabase_admin_agent_config/{pgdata-signal => pgdata-signal.sh} (56%) rename docker/pgctld/{pgctld-wrapper => pgctld-wrapper.sh} (92%) diff --git a/Dockerfile-multigres b/Dockerfile-multigres index bddb8407ba..19823441e7 100644 --- a/Dockerfile-multigres +++ b/Dockerfile-multigres @@ -202,7 +202,7 @@ COPY docker/pgctld/postgresql.conf.tmpl /etc/pgctld-custom/postgresql.conf.tmpl # Wrapper: injects --postgres-config-template on every pgctld call AND bridges # postgres's JSON log file to container stdout via a /proc/1/fd/1 symlink so # kubelet + Vector can ship it without a sidecar. See docker/pgctld/pgctld-wrapper. -COPY --chmod=755 docker/pgctld/pgctld-wrapper /usr/local/bin/pgctld +COPY --chmod=755 docker/pgctld/pgctld-wrapper.sh /usr/local/bin/pgctld ENV POSTGRES_CONFIG_TEMPLATE_PATH=/etc/pgctld-custom/postgresql.conf.tmpl # Strip extensions absent from pg17 vanilla build diff --git a/ansible/files/admin_api_scripts/grow_fs.sh b/ansible/files/admin_api_scripts/grow_fs.sh index 1a524d1968..d0707442b6 100644 --- a/ansible/files/admin_api_scripts/grow_fs.sh +++ b/ansible/files/admin_api_scripts/grow_fs.sh @@ -8,14 +8,14 @@ VOLUME_TYPE=${1:-data} UBUNTU_VERSION=$(lsb_release -rs) if pgrep resizefs; then - echo "resize2fs is already running" - exit 1 + echo "resize2fs is already running" + exit 1 fi # install amazon disk utilities if not present on 24.04 if [ "${UBUNTU_VERSION}" = "24.04" ] && ! /usr/bin/dpkg-query -W amazon-ec2-utils >/dev/null 2>&1; then - apt-get update - apt-get install -y amazon-ec2-utils || true + apt-get update + apt-get install -y amazon-ec2-utils || true fi # We currently mount 3 possible disks @@ -27,15 +27,15 @@ XVDA_DEVICE="/dev/nvme0n1" XVDH_DEVICE="/dev/nvme1n1" # Map AWS devices to NVMe for ubuntu 24.04 and later if [ "${UBUNTU_VERSION}" = "24.04" ] && /usr/bin/dpkg-query -W amazon-ec2-utils >/dev/null 2>&1; then - for nvme_dev in $(lsblk -dprno name,type | grep disk | awk '{print $1}'); do - if [ -b "$nvme_dev" ]; then - mapping=$(ebsnvme-id -b "$nvme_dev" 2>/dev/null) - case "$mapping" in - "xvda"|"/dev/xvda") XVDA_DEVICE="$nvme_dev" ;; - "xvdh"|"/dev/xvdh") XVDH_DEVICE="$nvme_dev" ;; - esac - fi - done + for nvme_dev in $(lsblk -dprno name,type | grep disk | awk '{print $1}'); do + if [ -b "$nvme_dev" ]; then + mapping=$(ebsnvme-id -b "$nvme_dev" 2>/dev/null) + case "$mapping" in + "xvda" | "/dev/xvda") XVDA_DEVICE="$nvme_dev" ;; + "xvdh" | "/dev/xvdh") XVDH_DEVICE="$nvme_dev" ;; + esac + fi + done fi echo "Using devices - Root: $XVDA_DEVICE, Data: $XVDH_DEVICE" @@ -45,35 +45,35 @@ ROOT_DEVICE_FULL=$(findmnt -no SOURCE /) ROOT_DEVICE=$(lsblk -no PKNAME "$ROOT_DEVICE_FULL") ROOT_PARTITION_NUMBER=$(echo "$ROOT_DEVICE_FULL" | sed "s|.*${ROOT_DEVICE}p||") -if ! [[ "$ROOT_PARTITION_NUMBER" =~ ^[0-9]+$ ]]; then - echo "Error: ROOT_PARTITION_NUMBER is not a valid number: $ROOT_PARTITION_NUMBER" - exit 1 +if ! [[ $ROOT_PARTITION_NUMBER =~ ^[0-9]+$ ]]; then + echo "Error: ROOT_PARTITION_NUMBER is not a valid number: $ROOT_PARTITION_NUMBER" + exit 1 fi -if [ -b "${XVDH_DEVICE}" ] ; then - if [[ "${VOLUME_TYPE}" == "data" ]]; then - resize2fs "${XVDH_DEVICE}" +if [ -b "${XVDH_DEVICE}" ]; then + if [[ ${VOLUME_TYPE} == "data" ]]; then + resize2fs "${XVDH_DEVICE}" - # Explicitly reserving 100MiB worth of blocks for the data volume - # - # This is owned in $GIT_DIR/ebssurrogate/scripts/surrogate-bootstrap-nix.sh - RESERVED_DATA_VOLUME_BLOCK_COUNT=$((100 * 1024 * 1024 / 4096)) - tune2fs -r $RESERVED_DATA_VOLUME_BLOCK_COUNT "${XVDH_DEVICE}" + # Explicitly reserving 100MiB worth of blocks for the data volume + # + # This is owned in $GIT_DIR/ebssurrogate/scripts/surrogate-bootstrap-nix.sh + RESERVED_DATA_VOLUME_BLOCK_COUNT=$((100 * 1024 * 1024 / 4096)) + tune2fs -r $RESERVED_DATA_VOLUME_BLOCK_COUNT "${XVDH_DEVICE}" - elif [[ "${VOLUME_TYPE}" == "root" ]] ; then - PLACEHOLDER_FL=/home/ubuntu/50M_PLACEHOLDER - rm -f "${PLACEHOLDER_FL}" || true - growpart "${XVDA_DEVICE}" "${ROOT_PARTITION_NUMBER}" - resize2fs "${XVDA_DEVICE}p${ROOT_PARTITION_NUMBER}" - if [[ ! -f "${PLACEHOLDER_FL}" ]] ; then - fallocate -l50M "${PLACEHOLDER_FL}" - fi - else - echo "Invalid disk specified: ${VOLUME_TYPE}" - exit 1 - fi + elif [[ ${VOLUME_TYPE} == "root" ]]; then + PLACEHOLDER_FL=/home/ubuntu/50M_PLACEHOLDER + rm -f "${PLACEHOLDER_FL}" || true + growpart "${XVDA_DEVICE}" "${ROOT_PARTITION_NUMBER}" + resize2fs "${XVDA_DEVICE}p${ROOT_PARTITION_NUMBER}" + if [[ ! -f ${PLACEHOLDER_FL} ]]; then + fallocate -l50M "${PLACEHOLDER_FL}" + fi + else + echo "Invalid disk specified: ${VOLUME_TYPE}" + exit 1 + fi else - growpart "${XVDA_DEVICE}" "${ROOT_PARTITION_NUMBER}" - resize2fs "${XVDA_DEVICE}p${ROOT_PARTITION_NUMBER}" + growpart "${XVDA_DEVICE}" "${ROOT_PARTITION_NUMBER}" + resize2fs "${XVDA_DEVICE}p${ROOT_PARTITION_NUMBER}" fi echo "Done resizing disk" diff --git a/ansible/files/admin_api_scripts/manage_readonly_mode.sh b/ansible/files/admin_api_scripts/manage_readonly_mode.sh index 41c9f5a1e3..6c9fef5880 100644 --- a/ansible/files/admin_api_scripts/manage_readonly_mode.sh +++ b/ansible/files/admin_api_scripts/manage_readonly_mode.sh @@ -5,13 +5,14 @@ set -euo pipefail SUBCOMMAND=$1 function set_mode { - MODE=$1 - psql -h localhost -U supabase_admin -d postgres -c "ALTER SYSTEM SET default_transaction_read_only to ${MODE};" - psql -h localhost -U supabase_admin -d postgres -c "SELECT pg_reload_conf();" + MODE=$1 + psql -h localhost -U supabase_admin -d postgres -c "ALTER SYSTEM SET default_transaction_read_only to ${MODE};" + psql -h localhost -U supabase_admin -d postgres -c "SELECT pg_reload_conf();" } function check_override { - COMMAND=$(cat < " - echo "Example: sudo ./mount-volume.sh /dev/nvme1n1 /data/150008" - logger "Usage: $0 " - logger "Example: sudo ./mount-volume.sh /dev/nvme1n1 /data/150008" - exit 1 +if [[ -z $DEVICE || -z $MOUNT_POINT ]]; then + echo "Usage: $0 " + echo "Example: sudo ./mount-volume.sh /dev/nvme1n1 /data/150008" + logger "Usage: $0 " + logger "Example: sudo ./mount-volume.sh /dev/nvme1n1 /data/150008" + exit 1 fi OWNER="postgres:postgres" @@ -24,8 +24,8 @@ LOGGER_TAG="mount-volume" # --- Helper function for echo + logger --- log() { - echo "$1" - logger -t "$LOGGER_TAG" "$1" + echo "$1" + logger -t "$LOGGER_TAG" "$1" } log "Starting mount procedure for device $DEVICE → $MOUNT_POINT" @@ -33,57 +33,57 @@ log "Starting mount procedure for device $DEVICE → $MOUNT_POINT" # --- Wait for block device --- log "Waiting for block device $DEVICE to become available..." while true; do - if [ -b "$DEVICE" ]; then - if blkid "$DEVICE" >/dev/null 2>&1 || true; then - log "$DEVICE is ready" - break - fi - fi - - ELAPSED=$((ELAPSED + INTERVAL)) - if [ $ELAPSED -ge $TIMEOUT ]; then - log "Error: $DEVICE did not become ready after $TIMEOUT seconds" - exit 3 - fi - - sleep $INTERVAL + if [ -b "$DEVICE" ]; then + if blkid "$DEVICE" >/dev/null 2>&1 || true; then + log "$DEVICE is ready" + break + fi + fi + + ELAPSED=$((ELAPSED + INTERVAL)) + if [ $ELAPSED -ge $TIMEOUT ]; then + log "Error: $DEVICE did not become ready after $TIMEOUT seconds" + exit 3 + fi + + sleep $INTERVAL done # --- Validate device --- if [ ! -b "$DEVICE" ]; then - log "Error: Block device '$DEVICE' does not exist." - exit 2 + log "Error: Block device '$DEVICE' does not exist." + exit 2 fi # --- Safety: refuse to mount over non-empty directory --- mkdir -p "$MOUNT_POINT" if [ "$(ls -A "$MOUNT_POINT" 2>/dev/null)" ]; then - if ! mountpoint -q "$MOUNT_POINT"; then - log "Error: Mount point $MOUNT_POINT is not empty. Aborting to protect existing data." - exit 4 - fi + if ! mountpoint -q "$MOUNT_POINT"; then + log "Error: Mount point $MOUNT_POINT is not empty. Aborting to protect existing data." + exit 4 + fi fi # --- Format if needed --- if ! blkid "$DEVICE" >/dev/null 2>&1; then - log "Device $DEVICE appears unformatted. Formatting as $FSTYPE..." - mkfs."$FSTYPE" -F "$DEVICE" + log "Device $DEVICE appears unformatted. Formatting as $FSTYPE..." + mkfs."$FSTYPE" -F "$DEVICE" else - log "$DEVICE already has a filesystem — skipping format." + log "$DEVICE already has a filesystem — skipping format." fi # --- Filesystem check --- if ! mountpoint -q "$MOUNT_POINT"; then - log "Running e2fsck check on $DEVICE" - e2fsck -pf "$DEVICE" || log "Warning: e2fsck returned non-zero exit code" + log "Running e2fsck check on $DEVICE" + e2fsck -pf "$DEVICE" || log "Warning: e2fsck returned non-zero exit code" fi # --- Mount --- if ! mountpoint -q "$MOUNT_POINT"; then - log "Mounting $DEVICE to $MOUNT_POINT" - mount -t "$FSTYPE" -o "$MOUNT_OPTS" "$DEVICE" "$MOUNT_POINT" + log "Mounting $DEVICE to $MOUNT_POINT" + mount -t "$FSTYPE" -o "$MOUNT_OPTS" "$DEVICE" "$MOUNT_POINT" else - log "$MOUNT_POINT is already mounted" + log "$MOUNT_POINT is already mounted" fi # --- Ownership and permissions --- @@ -96,10 +96,10 @@ UUID=$(blkid -s UUID -o value "$DEVICE") FSTAB_LINE="UUID=$UUID $MOUNT_POINT $FSTYPE $MOUNT_OPTS 0 2" if ! grep -q "$UUID" "$FSTAB_FILE"; then - log "Adding $FSTAB_LINE to $FSTAB_FILE" - echo "$FSTAB_LINE" >> "$FSTAB_FILE" + log "Adding $FSTAB_LINE to $FSTAB_FILE" + echo "$FSTAB_LINE" >>"$FSTAB_FILE" else - log "UUID $UUID already in $FSTAB_FILE — skipping" + log "UUID $UUID already in $FSTAB_FILE — skipping" fi -log "Mounted $DEVICE at $MOUNT_POINT with owner=$OWNER and mode=$PERMISSIONS" \ No newline at end of file +log "Mounted $DEVICE at $MOUNT_POINT with owner=$OWNER and mode=$PERMISSIONS" diff --git a/ansible/files/admin_api_scripts/pg_upgrade_scripts/check.sh b/ansible/files/admin_api_scripts/pg_upgrade_scripts/check.sh index f85e9571b6..2956063e42 100755 --- a/ansible/files/admin_api_scripts/pg_upgrade_scripts/check.sh +++ b/ansible/files/admin_api_scripts/pg_upgrade_scripts/check.sh @@ -8,9 +8,8 @@ set -euo pipefail STATUS_FILE="/tmp/pg-upgrade-status" if [ -f "${STATUS_FILE}" ]; then - STATUS=$(cat "${STATUS_FILE}") - echo -n "${STATUS}" + STATUS=$(cat "${STATUS_FILE}") + echo -n "${STATUS}" else - echo -n "unknown" + echo -n "unknown" fi - diff --git a/ansible/files/admin_api_scripts/pg_upgrade_scripts/common.sh b/ansible/files/admin_api_scripts/pg_upgrade_scripts/common.sh index e9e3afe8a2..a3f6f2081f 100755 --- a/ansible/files/admin_api_scripts/pg_upgrade_scripts/common.sh +++ b/ansible/files/admin_api_scripts/pg_upgrade_scripts/common.sh @@ -7,100 +7,100 @@ REPORTING_CREDENTIALS_FILE="/root/upgrade-reporting-credentials" REPORTING_ANON_KEY="" if [ -f "$REPORTING_CREDENTIALS_FILE" ]; then - REPORTING_ANON_KEY=$(cat "$REPORTING_CREDENTIALS_FILE") + REPORTING_ANON_KEY=$(cat "$REPORTING_CREDENTIALS_FILE") fi # shellcheck disable=SC2120 # Arguments are passed in other files function run_sql { - psql -h localhost -U supabase_admin -d postgres "$@" + psql -h localhost -U supabase_admin -d postgres "$@" } function ship_logs { - LOG_FILE=$1 - - if [ -z "$REPORTING_ANON_KEY" ]; then - echo "No reporting key found. Skipping log upload." - return 0 - fi - - if [ ! -f "$LOG_FILE" ]; then - echo "No log file found. Skipping log upload." - return 0 - fi - - if [ ! -s "$LOG_FILE" ]; then - echo "Log file is empty. Skipping log upload." - return 0 - fi - - HOSTNAME=$(hostname) - DERIVED_REF="${HOSTNAME##*-}" - - printf -v BODY '{ "ref": "%s", "step": "%s", "content": %s }' "$DERIVED_REF" "completion" "$(cat "$LOG_FILE" | jq -Rs '.')" - curl -sf -X POST "https://$REPORTING_PROJECT_REF.supabase.co/rest/v1/error_logs" \ - -H "apikey: ${REPORTING_ANON_KEY}" \ - -H 'Content-type: application/json' \ - -d "$BODY" + LOG_FILE=$1 + + if [ -z "$REPORTING_ANON_KEY" ]; then + echo "No reporting key found. Skipping log upload." + return 0 + fi + + if [ ! -f "$LOG_FILE" ]; then + echo "No log file found. Skipping log upload." + return 0 + fi + + if [ ! -s "$LOG_FILE" ]; then + echo "Log file is empty. Skipping log upload." + return 0 + fi + + HOSTNAME=$(hostname) + DERIVED_REF="${HOSTNAME##*-}" + + printf -v BODY '{ "ref": "%s", "step": "%s", "content": %s }' "$DERIVED_REF" "completion" "$(cat "$LOG_FILE" | jq -Rs '.')" + curl -sf -X POST "https://$REPORTING_PROJECT_REF.supabase.co/rest/v1/error_logs" \ + -H "apikey: ${REPORTING_ANON_KEY}" \ + -H 'Content-type: application/json' \ + -d "$BODY" } function retry { - local retries=$1 - shift - - local count=0 - until "$@"; do - exit=$? - wait=$((2 ** (count + 1))) - count=$((count + 1)) - if [ $count -lt "$retries" ]; then - echo "Command $* exited with code $exit, retrying..." - sleep $wait - else - echo "Command $* exited with code $exit, no more retries left." - return $exit - fi - done - return 0 + local retries=$1 + shift + + local count=0 + until "$@"; do + exit=$? + wait=$((2 ** (count + 1))) + count=$((count + 1)) + if [ $count -lt "$retries" ]; then + echo "Command $* exited with code $exit, retrying..." + sleep $wait + else + echo "Command $* exited with code $exit, no more retries left." + return $exit + fi + done + return 0 } CI_stop_postgres() { - BINDIR=$(pg_config --bindir) - ARG=${1:-""} + BINDIR=$(pg_config --bindir) + ARG=${1:-""} - if [ "$ARG" = "--new-bin" ]; then - BINDIR="/tmp/pg_upgrade_bin/$PG_MAJOR_VERSION/bin" - fi + if [ "$ARG" = "--new-bin" ]; then + BINDIR="/tmp/pg_upgrade_bin/$PG_MAJOR_VERSION/bin" + fi - su postgres -c "$BINDIR/pg_ctl stop -o '-c config_file=/etc/postgresql/postgresql.conf' -l /tmp/postgres.log" + su postgres -c "$BINDIR/pg_ctl stop -o '-c config_file=/etc/postgresql/postgresql.conf' -l /tmp/postgres.log" } CI_start_postgres() { - BINDIR=$(pg_config --bindir) - ARG=${1:-""} + BINDIR=$(pg_config --bindir) + ARG=${1:-""} - if [ "$ARG" = "--new-bin" ]; then - BINDIR="/tmp/pg_upgrade_bin/$PG_MAJOR_VERSION/bin" - fi + if [ "$ARG" = "--new-bin" ]; then + BINDIR="/tmp/pg_upgrade_bin/$PG_MAJOR_VERSION/bin" + fi - su postgres -c "$BINDIR/pg_ctl start -o '-c config_file=/etc/postgresql/postgresql.conf' -l /tmp/postgres.log" + su postgres -c "$BINDIR/pg_ctl start -o '-c config_file=/etc/postgresql/postgresql.conf' -l /tmp/postgres.log" } swap_postgres_and_supabase_admin() { - run_sql <<'EOSQL' + run_sql <<'EOSQL' alter database postgres connection limit 0; select pg_terminate_backend(pid) from pg_stat_activity where backend_type = 'client backend' and pid != pg_backend_pid(); EOSQL - if [ -z "$IS_CI" ]; then - retry 5 systemctl restart postgresql - else - CI_start_postgres "" - fi + if [ -z "$IS_CI" ]; then + retry 5 systemctl restart postgresql + else + CI_start_postgres "" + fi - retry 8 pg_isready -h localhost -U supabase_admin + retry 8 pg_isready -h localhost -U supabase_admin - run_sql <<'EOSQL' + run_sql <<'EOSQL' set statement_timeout = '600s'; begin; create role supabase_tmp superuser; diff --git a/ansible/files/admin_api_scripts/pg_upgrade_scripts/complete.sh b/ansible/files/admin_api_scripts/pg_upgrade_scripts/complete.sh index 3b629e5a7f..5bdb587088 100755 --- a/ansible/files/admin_api_scripts/pg_upgrade_scripts/complete.sh +++ b/ansible/files/admin_api_scripts/pg_upgrade_scripts/complete.sh @@ -7,58 +7,58 @@ set -eEuo pipefail -SCRIPT_DIR=$(dirname -- "$0";) +SCRIPT_DIR=$(dirname -- "$0") # shellcheck disable=SC1091 source "$SCRIPT_DIR/common.sh" IS_CI=${IS_CI:-} LOG_FILE="/var/log/pg-upgrade-complete.log" - # Wait for the volume mapped to /data to appear before attempting to mount it function wait_for_data_device { - local fstab_src dev="" - fstab_src=$(awk '$2 == "/data" {print $1}' /etc/fstab) - if [ -z "$fstab_src" ]; then - echo "No /data entry in /etc/fstab" - return 1 - fi - - echo "Waiting for /data device ($fstab_src) to appear" - for _ in $(seq 1 60); do - dev=$(findfs "$fstab_src" 2>/dev/null) || dev="" - if [ -n "$dev" ] && [ -b "$dev" ]; then - echo "/data device ($dev) is available" - return 0 - fi - sleep 1 - done - echo "Timed out waiting for /data device ($fstab_src)" - return 1 + local fstab_src dev="" + fstab_src=$(awk '$2 == "/data" {print $1}' /etc/fstab) + if [ -z "$fstab_src" ]; then + echo "No /data entry in /etc/fstab" + return 1 + fi + + echo "Waiting for /data device ($fstab_src) to appear" + for _ in $(seq 1 60); do + dev=$(findfs "$fstab_src" 2>/dev/null) || dev="" + if [ -n "$dev" ] && [ -b "$dev" ]; then + echo "/data device ($dev) is available" + return 0 + fi + sleep 1 + done + echo "Timed out waiting for /data device ($fstab_src)" + return 1 } function cleanup { - UPGRADE_STATUS=${1:-"failed"} - EXIT_CODE=${?:-0} + UPGRADE_STATUS=${1:-"failed"} + EXIT_CODE=${?:-0} - echo "$UPGRADE_STATUS" > /tmp/pg-upgrade-status + echo "$UPGRADE_STATUS" >/tmp/pg-upgrade-status - ship_logs "$LOG_FILE" || true + ship_logs "$LOG_FILE" || true - exit "$EXIT_CODE" + exit "$EXIT_CODE" } function execute_extension_upgrade_patches { - if [ -f "/var/lib/postgresql/extension/wrappers--0.3.1--0.4.1.sql" ] && [ ! -f "/usr/share/postgresql/15/extension/wrappers--0.3.0--0.4.1.sql" ]; then - cp /var/lib/postgresql/extension/wrappers--0.3.1--0.4.1.sql /var/lib/postgresql/extension/wrappers--0.3.0--0.4.1.sql - ln -s /var/lib/postgresql/extension/wrappers--0.3.0--0.4.1.sql /usr/share/postgresql/15/extension/wrappers--0.3.0--0.4.1.sql - fi + if [ -f "/var/lib/postgresql/extension/wrappers--0.3.1--0.4.1.sql" ] && [ ! -f "/usr/share/postgresql/15/extension/wrappers--0.3.0--0.4.1.sql" ]; then + cp /var/lib/postgresql/extension/wrappers--0.3.1--0.4.1.sql /var/lib/postgresql/extension/wrappers--0.3.0--0.4.1.sql + ln -s /var/lib/postgresql/extension/wrappers--0.3.0--0.4.1.sql /usr/share/postgresql/15/extension/wrappers--0.3.0--0.4.1.sql + fi } function execute_wrappers_patch { - # If upgrading to pgsodium-less Vault, Wrappers need to be updated so that - # foreign servers use `vault.secrets.id` instead of `vault.secrets.key_id` - UPDATE_WRAPPERS_SERVER_OPTIONS_QUERY=$(cat < 0 from pg_extension where extname = 'pg_net';") + # Patch pg_net grants + PG_NET_ENABLED=$(run_sql -A -t -c "select count(*) > 0 from pg_extension where extname = 'pg_net';") - if [ "$PG_NET_ENABLED" = "t" ]; then - PG_NET_GRANT_QUERY=$(cat < 0 from pg_extension where extname = 'pg_cron' and extowner::regrole::text = 'postgres';") + # Patching pg_cron ownership as it resets during upgrade + HAS_PG_CRON_OWNED_BY_POSTGRES=$(run_sql -A -t -c "select count(*) > 0 from pg_extension where extname = 'pg_cron' and extowner::regrole::text = 'postgres';") - if [ "$HAS_PG_CRON_OWNED_BY_POSTGRES" = "t" ]; then - RECREATE_PG_CRON_QUERY=$(cat < 0 from pg_extension where extname = 'pgmq';") - if [ "$HAS_PGMQ" = "t" ]; then - run_sql -c "update pg_extension set extowner = 'postgres'::regrole where extname = 'pgmq';" - fi + # Patching pgmq ownership as it resets during upgrade + HAS_PGMQ=$(run_sql -A -t -c "select count(*) > 0 from pg_extension where extname = 'pgmq';") + if [ "$HAS_PGMQ" = "t" ]; then + run_sql -c "update pg_extension set extowner = 'postgres'::regrole where extname = 'pgmq';" + fi - # Patch to handle upgrading to pgsodium-less Vault - REENCRYPT_VAULT_SECRETS_QUERY=$(cat < /tmp/pg-upgrade-status - - echo "1. Mounting data disk" - if [ -z "$IS_CI" ]; then - # Let udev finish detecting the vollume before mounting - udevadm settle --timeout=60 || true - wait_for_data_device - - retry 8 mount -a -v - - # `nofail` in /etc/fstab makes `mount -a` exit with a code of 0 even when the volume is absent - # In the offchance of the volume not being mounted or detected, explicitly fail here - if ! mountpoint -q /data; then - echo "FATAL: /data is not a mountpoint" - exit 1 - fi - else - echo "Skipping mount -a -v" - fi - - # copying custom configurations - echo "2. Copying custom configurations" - retry 3 copy_configs - - echo "3. Starting postgresql" - if [ -z "$IS_CI" ]; then - retry 3 service postgresql start - else - CI_start_postgres --new-bin - fi - - execute_extension_upgrade_patches || true - - # For this to work we need `vault.secrets` from the old project to be - # preserved, but `run_generated_sql` includes `ALTER EXTENSION - # supabase_vault UPDATE` which modifies that. So we need to run it - # beforehand. - echo "3.1. Patch Wrappers server options" - execute_wrappers_patch - - echo "4. Running generated SQL files" - retry 3 run_generated_sql - - echo "4.1. Applying patches" - execute_patches || true - - run_sql -c "ALTER USER postgres WITH NOSUPERUSER;" - - echo "4.2. Applying authentication scheme updates" - retry 3 apply_auth_scheme_updates - - sleep 5 - - echo "5. Restarting postgresql" - if [ -z "$IS_CI" ]; then - retry 3 service postgresql restart - - echo "5.1. Restarting gotrue and postgrest" - retry 3 service gotrue restart - retry 3 service postgrest restart - - else - retry 3 CI_stop_postgres || true - retry 3 CI_start_postgres - fi - - echo "6. Starting vacuum analyze" - retry 3 start_vacuum_analyze + if [ -f /tmp/pg-upgrade-status ]; then + echo "Upgrade job already started. Bailing." + exit 0 + fi + + echo "running" >/tmp/pg-upgrade-status + + echo "1. Mounting data disk" + if [ -z "$IS_CI" ]; then + # Let udev finish detecting the vollume before mounting + udevadm settle --timeout=60 || true + wait_for_data_device + + retry 8 mount -a -v + + # `nofail` in /etc/fstab makes `mount -a` exit with a code of 0 even when the volume is absent + # In the offchance of the volume not being mounted or detected, explicitly fail here + if ! mountpoint -q /data; then + echo "FATAL: /data is not a mountpoint" + exit 1 + fi + else + echo "Skipping mount -a -v" + fi + + # copying custom configurations + echo "2. Copying custom configurations" + retry 3 copy_configs + + echo "3. Starting postgresql" + if [ -z "$IS_CI" ]; then + retry 3 service postgresql start + else + CI_start_postgres --new-bin + fi + + execute_extension_upgrade_patches || true + + # For this to work we need `vault.secrets` from the old project to be + # preserved, but `run_generated_sql` includes `ALTER EXTENSION + # supabase_vault UPDATE` which modifies that. So we need to run it + # beforehand. + echo "3.1. Patch Wrappers server options" + execute_wrappers_patch + + echo "4. Running generated SQL files" + retry 3 run_generated_sql + + echo "4.1. Applying patches" + execute_patches || true + + run_sql -c "ALTER USER postgres WITH NOSUPERUSER;" + + echo "4.2. Applying authentication scheme updates" + retry 3 apply_auth_scheme_updates + + sleep 5 + + echo "5. Restarting postgresql" + if [ -z "$IS_CI" ]; then + retry 3 service postgresql restart + + echo "5.1. Restarting gotrue and postgrest" + retry 3 service gotrue restart + retry 3 service postgrest restart + + else + retry 3 CI_stop_postgres || true + retry 3 CI_start_postgres + fi + + echo "6. Starting vacuum analyze" + retry 3 start_vacuum_analyze } function copy_configs { - cp -R /data/conf/* /etc/postgresql-custom/ - chown -R postgres:postgres /var/lib/postgresql/data - chown -R postgres:postgres /data/pgdata - chmod -R 0750 /data/pgdata + cp -R /data/conf/* /etc/postgresql-custom/ + chown -R postgres:postgres /var/lib/postgresql/data + chown -R postgres:postgres /data/pgdata + chmod -R 0750 /data/pgdata } function run_generated_sql { - if [ -d /data/sql ]; then - for FILE in /data/sql/*.sql; do - if [ -f "$FILE" ]; then - run_sql -f "$FILE" || true - fi - done - fi + if [ -d /data/sql ]; then + for FILE in /data/sql/*.sql; do + if [ -f "$FILE" ]; then + run_sql -f "$FILE" || true + fi + done + fi } # Projects which had their passwords hashed using md5 need to have their passwords reset # Passwords for managed roles are already present in /etc/postgresql.schema.sql function apply_auth_scheme_updates { - PASSWORD_ENCRYPTION_SETTING=$(run_sql -A -t -c "SHOW password_encryption;") - if [ "$PASSWORD_ENCRYPTION_SETTING" = "md5" ]; then - run_sql -c "ALTER SYSTEM SET password_encryption TO 'scram-sha-256';" - run_sql -c "SELECT pg_reload_conf();" - - if [ -z "$IS_CI" ]; then - run_sql -f /etc/postgresql.schema.sql - fi - fi + PASSWORD_ENCRYPTION_SETTING=$(run_sql -A -t -c "SHOW password_encryption;") + if [ "$PASSWORD_ENCRYPTION_SETTING" = "md5" ]; then + run_sql -c "ALTER SYSTEM SET password_encryption TO 'scram-sha-256';" + run_sql -c "SELECT pg_reload_conf();" + + if [ -z "$IS_CI" ]; then + run_sql -f /etc/postgresql.schema.sql + fi + fi } function start_vacuum_analyze { - echo "complete" > /tmp/pg-upgrade-status - - # shellcheck disable=SC1091 - if [ -f "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" ]; then - # shellcheck disable=SC1091 - source "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" - fi - vacuumdb --all --analyze-in-stages -U supabase_admin -h localhost -p 5432 - echo "Upgrade job completed" + echo "complete" >/tmp/pg-upgrade-status + + # shellcheck disable=SC1091 + if [ -f "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" ]; then + # shellcheck disable=SC1091 + source "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" + fi + vacuumdb --all --analyze-in-stages -U supabase_admin -h localhost -p 5432 + echo "Upgrade job completed" } trap cleanup ERR -echo "C.UTF-8 UTF-8" > /etc/locale.gen -echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen +echo "C.UTF-8 UTF-8" >/etc/locale.gen +echo "en_US.UTF-8 UTF-8" >>/etc/locale.gen locale-gen if [ -z "$IS_CI" ]; then - complete_pg_upgrade >> $LOG_FILE 2>&1 & + complete_pg_upgrade >>$LOG_FILE 2>&1 & else - CI_stop_postgres || true + CI_stop_postgres || true - rm -f /tmp/pg-upgrade-status - mv /data_migration /data + rm -f /tmp/pg-upgrade-status + mv /data_migration /data - rm -rf /var/lib/postgresql/data - ln -s /data/pgdata /var/lib/postgresql/data + rm -rf /var/lib/postgresql/data + ln -s /data/pgdata /var/lib/postgresql/data - complete_pg_upgrade + complete_pg_upgrade fi diff --git a/ansible/files/admin_api_scripts/pg_upgrade_scripts/initiate.sh b/ansible/files/admin_api_scripts/pg_upgrade_scripts/initiate.sh index 49fec29005..64020bf04c 100755 --- a/ansible/files/admin_api_scripts/pg_upgrade_scripts/initiate.sh +++ b/ansible/files/admin_api_scripts/pg_upgrade_scripts/initiate.sh @@ -10,23 +10,23 @@ # Running an upgrade with these extensions enabled will result in errors due to # them depending on regtypes referencing system OIDs or outdated library files. EXTENSIONS_TO_DISABLE=( - "pg_graphql" - "pg_stat_monitor" - "pg_backtrace" + "pg_graphql" + "pg_stat_monitor" + "pg_backtrace" ) PG14_EXTENSIONS_TO_DISABLE=( - "wrappers" - "pgrouting" + "wrappers" + "pgrouting" ) PG13_EXTENSIONS_TO_DISABLE=( - "pgrouting" + "pgrouting" ) set -eEuo pipefail -SCRIPT_DIR=$(dirname -- "$0";) +SCRIPT_DIR=$(dirname -- "$0") # shellcheck disable=SC1091 source "$SCRIPT_DIR/common.sh" @@ -44,9 +44,9 @@ POST_UPGRADE_POSTGRES_PERMS_SCRIPT="/tmp/pg_upgrade/pg_upgrade_postgres_perms.sq OLD_PGVERSION=$(run_sql -A -t -c "SHOW server_version;") # Skip locale settings if both versions are PostgreSQL 16+ -if ! [[ "${OLD_PGVERSION%%.*}" -ge 16 && "${PGVERSION%%.*}" -ge 16 ]]; then - SERVER_LC_COLLATE=$(run_sql -A -t -c "SHOW lc_collate;") - SERVER_LC_CTYPE=$(run_sql -A -t -c "SHOW lc_ctype;") +if ! [[ ${OLD_PGVERSION%%.*} -ge 16 && ${PGVERSION%%.*} -ge 16 ]]; then + SERVER_LC_COLLATE=$(run_sql -A -t -c "SHOW lc_collate;") + SERVER_LC_CTYPE=$(run_sql -A -t -c "SHOW lc_ctype;") fi SERVER_ENCODING=$(run_sql -A -t -c "SHOW server_encoding;") @@ -59,87 +59,87 @@ NIX_INSTALLER_PATH="/tmp/persistent/nix-installer" NIX_INSTALLER_PACKAGE_PATH="$NIX_INSTALLER_PATH.tar.gz" if [ -L "$PGBINOLD/pg_upgrade" ]; then - BINARY_PATH=$(readlink -f "$PGBINOLD/pg_upgrade") - if [[ "$BINARY_PATH" == *"nix"* ]]; then - IS_NIX_BASED_SYSTEM="true" - fi + BINARY_PATH=$(readlink -f "$PGBINOLD/pg_upgrade") + if [[ $BINARY_PATH == *"nix"* ]]; then + IS_NIX_BASED_SYSTEM="true" + fi fi # If upgrading from older major PG versions, disable specific extensions -if [[ "$OLD_PGVERSION" =~ ^14.* ]]; then - EXTENSIONS_TO_DISABLE+=("${PG14_EXTENSIONS_TO_DISABLE[@]}") -elif [[ "$OLD_PGVERSION" =~ ^13.* ]]; then - EXTENSIONS_TO_DISABLE+=("${PG13_EXTENSIONS_TO_DISABLE[@]}") -elif [[ "$OLD_PGVERSION" =~ ^12.* ]]; then - POSTGRES_CONFIG_PATH="/etc/postgresql/12/main/postgresql.conf" - PGBINOLD="/usr/lib/postgresql/12/bin" +if [[ $OLD_PGVERSION =~ ^14.* ]]; then + EXTENSIONS_TO_DISABLE+=("${PG14_EXTENSIONS_TO_DISABLE[@]}") +elif [[ $OLD_PGVERSION =~ ^13.* ]]; then + EXTENSIONS_TO_DISABLE+=("${PG13_EXTENSIONS_TO_DISABLE[@]}") +elif [[ $OLD_PGVERSION =~ ^12.* ]]; then + POSTGRES_CONFIG_PATH="/etc/postgresql/12/main/postgresql.conf" + PGBINOLD="/usr/lib/postgresql/12/bin" fi if [ -n "$IS_CI" ]; then - PGBINOLD="$(pg_config --bindir)" - echo "Running in CI mode; using pg_config bindir: $PGBINOLD" - echo "PGVERSION: $PGVERSION" + PGBINOLD="$(pg_config --bindir)" + echo "Running in CI mode; using pg_config bindir: $PGBINOLD" + echo "PGVERSION: $PGVERSION" fi OLD_BOOTSTRAP_USER=$(run_sql -A -t -c "select rolname from pg_authid where oid = 10;") cleanup() { - UPGRADE_STATUS=${1:-"failed"} - EXIT_CODE=${?:-0} - - if [ "$UPGRADE_STATUS" = "failed" ]; then - EXIT_CODE=1 - fi - - if [ "$UPGRADE_STATUS" = "failed" ]; then - echo "Upgrade job failed. Cleaning up and exiting." - fi - - if [ -d "${MOUNT_POINT}/pgdata/pg_upgrade_output.d/" ]; then - echo "Copying pg_upgrade output to /var/log" - cp -R "${MOUNT_POINT}/pgdata/pg_upgrade_output.d/" /var/log/ || true - chown -R postgres:postgres /var/log/pg_upgrade_output.d/ - chmod -R 0750 /var/log/pg_upgrade_output.d/ - ship_logs "$LOG_FILE" || true - tail -n +1 /var/log/pg_upgrade_output.d/*/* > /var/log/pg_upgrade_output.d/pg_upgrade.log || true - ship_logs "/var/log/pg_upgrade_output.d/pg_upgrade.log" || true - fi - - if [ -L "/usr/share/postgresql/${PGVERSION}" ]; then - rm "/usr/share/postgresql/${PGVERSION}" - - if [ -f "/usr/share/postgresql/${PGVERSION}.bak" ]; then - mv "/usr/share/postgresql/${PGVERSION}.bak" "/usr/share/postgresql/${PGVERSION}" - fi - - if [ -d "/usr/share/postgresql/${PGVERSION}.bak" ]; then - mv "/usr/share/postgresql/${PGVERSION}.bak" "/usr/share/postgresql/${PGVERSION}" - fi - fi - - echo "Restarting postgresql" - if [ -z "$IS_CI" ]; then - systemctl enable postgresql - retry 5 systemctl restart postgresql - else - CI_start_postgres - fi - - retry 8 pg_isready -h localhost -U supabase_admin - - echo "Re-enabling extensions" - if [ -f $POST_UPGRADE_EXTENSION_SCRIPT ]; then - retry 5 run_sql -f $POST_UPGRADE_EXTENSION_SCRIPT - fi - - echo "Removing SUPERUSER grant from postgres" - retry 5 run_sql -c "ALTER USER postgres WITH NOSUPERUSER;" - - echo "Resetting postgres database connection limit" - retry 5 run_sql -c "ALTER DATABASE postgres CONNECTION LIMIT -1;" - - echo "Making sure postgres still has access to pg_shadow" - cat << EOF >> $POST_UPGRADE_POSTGRES_PERMS_SCRIPT + UPGRADE_STATUS=${1:-"failed"} + EXIT_CODE=${?:-0} + + if [ "$UPGRADE_STATUS" = "failed" ]; then + EXIT_CODE=1 + fi + + if [ "$UPGRADE_STATUS" = "failed" ]; then + echo "Upgrade job failed. Cleaning up and exiting." + fi + + if [ -d "${MOUNT_POINT}/pgdata/pg_upgrade_output.d/" ]; then + echo "Copying pg_upgrade output to /var/log" + cp -R "${MOUNT_POINT}/pgdata/pg_upgrade_output.d/" /var/log/ || true + chown -R postgres:postgres /var/log/pg_upgrade_output.d/ + chmod -R 0750 /var/log/pg_upgrade_output.d/ + ship_logs "$LOG_FILE" || true + tail -n +1 /var/log/pg_upgrade_output.d/*/* >/var/log/pg_upgrade_output.d/pg_upgrade.log || true + ship_logs "/var/log/pg_upgrade_output.d/pg_upgrade.log" || true + fi + + if [ -L "/usr/share/postgresql/${PGVERSION}" ]; then + rm "/usr/share/postgresql/${PGVERSION}" + + if [ -f "/usr/share/postgresql/${PGVERSION}.bak" ]; then + mv "/usr/share/postgresql/${PGVERSION}.bak" "/usr/share/postgresql/${PGVERSION}" + fi + + if [ -d "/usr/share/postgresql/${PGVERSION}.bak" ]; then + mv "/usr/share/postgresql/${PGVERSION}.bak" "/usr/share/postgresql/${PGVERSION}" + fi + fi + + echo "Restarting postgresql" + if [ -z "$IS_CI" ]; then + systemctl enable postgresql + retry 5 systemctl restart postgresql + else + CI_start_postgres + fi + + retry 8 pg_isready -h localhost -U supabase_admin + + echo "Re-enabling extensions" + if [ -f $POST_UPGRADE_EXTENSION_SCRIPT ]; then + retry 5 run_sql -f $POST_UPGRADE_EXTENSION_SCRIPT + fi + + echo "Removing SUPERUSER grant from postgres" + retry 5 run_sql -c "ALTER USER postgres WITH NOSUPERUSER;" + + echo "Resetting postgres database connection limit" + retry 5 run_sql -c "ALTER DATABASE postgres CONNECTION LIMIT -1;" + + echo "Making sure postgres still has access to pg_shadow" + cat <>$POST_UPGRADE_POSTGRES_PERMS_SCRIPT DO \$\$ begin if exists (select from pg_authid where rolname = 'pg_read_all_data') then @@ -150,54 +150,54 @@ end grant pg_signal_backend to postgres; EOF - if [ -f $POST_UPGRADE_POSTGRES_PERMS_SCRIPT ]; then - retry 5 run_sql -f $POST_UPGRADE_POSTGRES_PERMS_SCRIPT - fi - - if [ -z "$IS_CI" ] && [ -z "$IS_LOCAL_UPGRADE" ]; then - echo "Unmounting data disk from ${MOUNT_POINT}" - retry 3 umount $MOUNT_POINT - fi - echo "$UPGRADE_STATUS" > /tmp/pg-upgrade-status - - if [ -z "$IS_CI" ]; then - exit "$EXIT_CODE" - else - echo "CI run complete with code ${EXIT_CODE}. Exiting." - exit "$EXIT_CODE" - fi + if [ -f $POST_UPGRADE_POSTGRES_PERMS_SCRIPT ]; then + retry 5 run_sql -f $POST_UPGRADE_POSTGRES_PERMS_SCRIPT + fi + + if [ -z "$IS_CI" ] && [ -z "$IS_LOCAL_UPGRADE" ]; then + echo "Unmounting data disk from ${MOUNT_POINT}" + retry 3 umount $MOUNT_POINT + fi + echo "$UPGRADE_STATUS" >/tmp/pg-upgrade-status + + if [ -z "$IS_CI" ]; then + exit "$EXIT_CODE" + else + echo "CI run complete with code ${EXIT_CODE}. Exiting." + exit "$EXIT_CODE" + fi } function handle_extensions { - if [ -z "$IS_CI" ]; then - retry 5 systemctl restart postgresql - else - CI_start_postgres - fi + if [ -z "$IS_CI" ]; then + retry 5 systemctl restart postgresql + else + CI_start_postgres + fi - retry 8 pg_isready -h localhost -U supabase_admin + retry 8 pg_isready -h localhost -U supabase_admin - rm -f $POST_UPGRADE_EXTENSION_SCRIPT - touch $POST_UPGRADE_EXTENSION_SCRIPT + rm -f $POST_UPGRADE_EXTENSION_SCRIPT + touch $POST_UPGRADE_EXTENSION_SCRIPT - PASSWORD_ENCRYPTION_SETTING=$(run_sql -A -t -c "SHOW password_encryption;") - if [ "$PASSWORD_ENCRYPTION_SETTING" = "md5" ]; then - echo "ALTER SYSTEM SET password_encryption = 'md5';" >> $POST_UPGRADE_EXTENSION_SCRIPT - fi + PASSWORD_ENCRYPTION_SETTING=$(run_sql -A -t -c "SHOW password_encryption;") + if [ "$PASSWORD_ENCRYPTION_SETTING" = "md5" ]; then + echo "ALTER SYSTEM SET password_encryption = 'md5';" >>$POST_UPGRADE_EXTENSION_SCRIPT + fi - cat << EOF >> $POST_UPGRADE_EXTENSION_SCRIPT + cat <>$POST_UPGRADE_EXTENSION_SCRIPT ALTER SYSTEM SET jit = off; SELECT pg_reload_conf(); EOF - # Disable extensions if they're enabled - # Generate SQL script to re-enable them after upgrade - for EXTENSION in "${EXTENSIONS_TO_DISABLE[@]}"; do - EXTENSION_ENABLED=$(run_sql -A -t -c "SELECT EXISTS(SELECT 1 FROM pg_extension WHERE extname = '${EXTENSION}');") - if [ "$EXTENSION_ENABLED" = "t" ]; then - echo "Disabling extension ${EXTENSION}" - run_sql -c "DROP EXTENSION IF EXISTS ${EXTENSION} CASCADE;" - cat << EOF >> $POST_UPGRADE_EXTENSION_SCRIPT + # Disable extensions if they're enabled + # Generate SQL script to re-enable them after upgrade + for EXTENSION in "${EXTENSIONS_TO_DISABLE[@]}"; do + EXTENSION_ENABLED=$(run_sql -A -t -c "SELECT EXISTS(SELECT 1 FROM pg_extension WHERE extname = '${EXTENSION}');") + if [ "$EXTENSION_ENABLED" = "t" ]; then + echo "Disabling extension ${EXTENSION}" + run_sql -c "DROP EXTENSION IF EXISTS ${EXTENSION} CASCADE;" + cat <>$POST_UPGRADE_EXTENSION_SCRIPT DO \$\$ BEGIN IF EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = '${EXTENSION}') THEN @@ -206,313 +206,314 @@ BEGIN END; \$\$; EOF - fi - done + fi + done } function initiate_upgrade { - mkdir -p "$MOUNT_POINT" - SHARED_PRELOAD_LIBRARIES=$(grep '^[[:space:]]*shared_preload_libraries' "$POSTGRES_CONFIG_PATH" | sed "s/shared_preload_libraries =\s\{0,1\}'\(.*\)'.*/\1/") - - # Wrappers officially launched in PG15; PG14 version is incompatible - if [[ "$OLD_PGVERSION" =~ 14* ]]; then - SHARED_PRELOAD_LIBRARIES=$(echo "$SHARED_PRELOAD_LIBRARIES" | sed "s/wrappers//" | xargs) - fi - - # Timescale is no longer supported for PG17+ upgrades - if [[ "$PGVERSION" != "15" ]]; then - SHARED_PRELOAD_LIBRARIES=$(echo "$SHARED_PRELOAD_LIBRARIES" | sed "s/timescaledb//" | xargs) - fi - - SHARED_PRELOAD_LIBRARIES=$(echo "$SHARED_PRELOAD_LIBRARIES" | sed "s/pg_cron//" | xargs) - SHARED_PRELOAD_LIBRARIES=$(echo "$SHARED_PRELOAD_LIBRARIES" | sed "s/pg_net//" | xargs) - SHARED_PRELOAD_LIBRARIES=$(echo "$SHARED_PRELOAD_LIBRARIES" | sed "s/check_role_membership//" | xargs) - SHARED_PRELOAD_LIBRARIES=$(echo "$SHARED_PRELOAD_LIBRARIES" | sed "s/safeupdate//" | xargs) - SHARED_PRELOAD_LIBRARIES=$(echo "$SHARED_PRELOAD_LIBRARIES" | sed "s/pg_backtrace//" | xargs) - - # Exclude empty-string entries, as well as leading/trailing commas and spaces resulting from the above lib exclusions - # i.e. " , pg_stat_statements, , pgsodium, " -> "pg_stat_statements, pgsodium" - SHARED_PRELOAD_LIBRARIES=$(echo "$SHARED_PRELOAD_LIBRARIES" | tr ',' ' ' | tr -s ' ' | tr ' ' ', ') - - # Account for trailing comma - # eg. "...,auto_explain,pg_tle,plan_filter," -> "...,auto_explain,pg_tle,plan_filter" - if [[ "${SHARED_PRELOAD_LIBRARIES: -1}" = "," ]]; then - # clean up trailing comma - SHARED_PRELOAD_LIBRARIES=$(echo "$SHARED_PRELOAD_LIBRARIES" | sed "s/.$//" | xargs) - fi - - PGDATAOLD=$(grep '^[[:space:]]*data_directory' "$POSTGRES_CONFIG_PATH" | sed "s/data_directory = '\(.*\)'.*/\1/") - - # Check if old cluster has data checksums enabled - CHECKSUM_VERSION=$("$PGBINOLD/pg_controldata" "$PGDATAOLD" | grep -i checksum | awk '{print $NF}') - if [ "$CHECKSUM_VERSION" != "0" ]; then - CHECKSUM_FLAG="--data-checksums" - else - CHECKSUM_FLAG="" - fi - - PGDATANEW="$MOUNT_POINT/pgdata" - - # running upgrade using at least 1 cpu core - WORKERS=$(nproc | awk '{ print ($1 == 1 ? 1 : $1 - 1) }') - - # To make nix-based upgrades work for testing, create a pg binaries tarball with the following contents: - # - nix_flake_version - a7189a68ed4ea78c1e73991b5f271043636cf074 - # Where the value is the commit hash of the nix flake that contains the binaries - - if [ -n "$IS_LOCAL_UPGRADE" ]; then - mkdir -p "$PG_UPGRADE_BIN_DIR" - mkdir -p /tmp/persistent/ - if [ -n "$NIX_FLAKE_VERSION" ]; then - echo "$NIX_FLAKE_VERSION" > "$PG_UPGRADE_BIN_DIR/nix_flake_version" - else - echo "a7189a68ed4ea78c1e73991b5f271043636cf074" > "$PG_UPGRADE_BIN_DIR/nix_flake_version" - fi - - tar -czf "/tmp/persistent/pg_upgrade_bin.tar.gz" -C "/tmp/pg_upgrade_bin" . - rm -rf /tmp/pg_upgrade_bin/ - fi - - echo "1. Extracting pg_upgrade binaries" - mkdir -p "/tmp/pg_upgrade_bin" - tar zxf "/tmp/persistent/pg_upgrade_bin.tar.gz" -C "/tmp/pg_upgrade_bin" - - PGSHARENEW="$PG_UPGRADE_BIN_DIR/share" - - if [ -f "$PG_UPGRADE_BIN_DIR/nix_flake_version" ]; then - IS_NIX_UPGRADE="true" - NIX_FLAKE_VERSION=$(cat "$PG_UPGRADE_BIN_DIR/nix_flake_version") - - if [ "$IS_NIX_BASED_SYSTEM" = "false" ]; then - if [ ! -f /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh ]; then - if ! command -v nix > /dev/null; then - echo "1.1. Nix is not installed; installing." - - if [ -f "$NIX_INSTALLER_PACKAGE_PATH" ]; then - echo "1.1.1. Installing Nix using the provided installer" - tar -xzf "$NIX_INSTALLER_PACKAGE_PATH" -C /tmp/persistent/ - chmod +x "$NIX_INSTALLER_PATH" - "$NIX_INSTALLER_PATH" install --no-confirm \ - --extra-conf "substituters = https://cache.nixos.org https://nix-postgres-artifacts.s3.amazonaws.com" \ - --extra-conf "trusted-public-keys = nix-postgres-artifacts:dGZlQOvKcNEjvT7QEAJbcV6b6uk7VF/hWMjhYleiaLI= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" - else - echo "1.1.1. Installing Nix using the official installer" - sh <(curl -L https://releases.nixos.org/nix/nix-2.34.6/install) --yes --daemon --nix-extra-conf-file /dev/stdin < "pg_stat_statements, pgsodium" + SHARED_PRELOAD_LIBRARIES=$(echo "$SHARED_PRELOAD_LIBRARIES" | tr ',' ' ' | tr -s ' ' | tr ' ' ', ') + + # Account for trailing comma + # eg. "...,auto_explain,pg_tle,plan_filter," -> "...,auto_explain,pg_tle,plan_filter" + if [[ ${SHARED_PRELOAD_LIBRARIES: -1} == "," ]]; then + # clean up trailing comma + SHARED_PRELOAD_LIBRARIES=$(echo "$SHARED_PRELOAD_LIBRARIES" | sed "s/.$//" | xargs) + fi + + PGDATAOLD=$(grep '^[[:space:]]*data_directory' "$POSTGRES_CONFIG_PATH" | sed "s/data_directory = '\(.*\)'.*/\1/") + + # Check if old cluster has data checksums enabled + CHECKSUM_VERSION=$("$PGBINOLD/pg_controldata" "$PGDATAOLD" | grep -i checksum | awk '{print $NF}') + if [ "$CHECKSUM_VERSION" != "0" ]; then + CHECKSUM_FLAG="--data-checksums" + else + CHECKSUM_FLAG="" + fi + + PGDATANEW="$MOUNT_POINT/pgdata" + + # running upgrade using at least 1 cpu core + WORKERS=$(nproc | awk '{ print ($1 == 1 ? 1 : $1 - 1) }') + + # To make nix-based upgrades work for testing, create a pg binaries tarball with the following contents: + # - nix_flake_version - a7189a68ed4ea78c1e73991b5f271043636cf074 + # Where the value is the commit hash of the nix flake that contains the binaries + + if [ -n "$IS_LOCAL_UPGRADE" ]; then + mkdir -p "$PG_UPGRADE_BIN_DIR" + mkdir -p /tmp/persistent/ + if [ -n "$NIX_FLAKE_VERSION" ]; then + echo "$NIX_FLAKE_VERSION" >"$PG_UPGRADE_BIN_DIR/nix_flake_version" + else + echo "a7189a68ed4ea78c1e73991b5f271043636cf074" >"$PG_UPGRADE_BIN_DIR/nix_flake_version" + fi + + tar -czf "/tmp/persistent/pg_upgrade_bin.tar.gz" -C "/tmp/pg_upgrade_bin" . + rm -rf /tmp/pg_upgrade_bin/ + fi + + echo "1. Extracting pg_upgrade binaries" + mkdir -p "/tmp/pg_upgrade_bin" + tar zxf "/tmp/persistent/pg_upgrade_bin.tar.gz" -C "/tmp/pg_upgrade_bin" + + PGSHARENEW="$PG_UPGRADE_BIN_DIR/share" + + if [ -f "$PG_UPGRADE_BIN_DIR/nix_flake_version" ]; then + IS_NIX_UPGRADE="true" + NIX_FLAKE_VERSION=$(cat "$PG_UPGRADE_BIN_DIR/nix_flake_version") + + if [ "$IS_NIX_BASED_SYSTEM" = "false" ]; then + if [ ! -f /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh ]; then + if ! command -v nix >/dev/null; then + echo "1.1. Nix is not installed; installing." + + if [ -f "$NIX_INSTALLER_PACKAGE_PATH" ]; then + echo "1.1.1. Installing Nix using the provided installer" + tar -xzf "$NIX_INSTALLER_PACKAGE_PATH" -C /tmp/persistent/ + chmod +x "$NIX_INSTALLER_PATH" + "$NIX_INSTALLER_PATH" install --no-confirm \ + --extra-conf "substituters = https://cache.nixos.org https://nix-postgres-artifacts.s3.amazonaws.com" \ + --extra-conf "trusted-public-keys = nix-postgres-artifacts:dGZlQOvKcNEjvT7QEAJbcV6b6uk7VF/hWMjhYleiaLI= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" + else + echo "1.1.1. Installing Nix using the official installer" + sh <(curl -L https://releases.nixos.org/nix/nix-2.34.6/install) --yes --daemon --nix-extra-conf-file /dev/stdin < /tmp/pg_upgrade-nix-gc.log 2>&1 || true - - # Determine system architecture - ARCH=$(uname -m) - if [ "$ARCH" = "aarch64" ]; then - SYSTEM="aarch64-linux" - elif [ "$ARCH" = "x86_64" ]; then - SYSTEM="x86_64-linux" - else - echo "ERROR: Unsupported architecture: $ARCH" - exit 1 - fi - - # Fetch store path from catalog (avoids expensive nix eval - prevents OOM on small instances) - # Each postgres version has its own catalog file: {git_sha}-psql_{version}.json - CATALOG_S3="s3://supabase-internal-artifacts/nix-catalog/${NIX_FLAKE_VERSION}-psql_${PGVERSION}-${SYSTEM}.json" - CATALOG_LOCAL="/tmp/nix-catalog-${NIX_FLAKE_VERSION}-psql_${PGVERSION}-${SYSTEM}.json" - echo "Fetching catalog from: $CATALOG_S3" - - if ! aws s3 cp "$CATALOG_S3" "$CATALOG_LOCAL" --region ap-southeast-1; then - echo "ERROR: Failed to fetch catalog from $CATALOG_S3" - exit 1 - fi - - STORE_PATH=$(jq -r ".\"${SYSTEM}\"" "$CATALOG_LOCAL") - - if [ -z "$STORE_PATH" ] || [ "$STORE_PATH" = "null" ]; then - echo "ERROR: Could not find store path in catalog for ${SYSTEM}" - echo "Catalog contents:" - jq . "$CATALOG_LOCAL" - exit 1 - fi - - echo "Store path: $STORE_PATH" - - # Realize from binary cache (no nix evaluation needed!) - nix-store -r "$STORE_PATH" - - PG_UPGRADE_BIN_DIR="$STORE_PATH" - PGSHARENEW="$PG_UPGRADE_BIN_DIR/share/postgresql" - fi - - PGBINNEW="$PG_UPGRADE_BIN_DIR/bin" - PGLIBNEW="$PG_UPGRADE_BIN_DIR/lib" - - # copy upgrade-specific pgsodium_getkey script into the share dir - chmod +x "$SCRIPT_DIR/pgsodium_getkey.sh" - mkdir -p "$PGSHARENEW/extension" - cp "$SCRIPT_DIR/pgsodium_getkey.sh" "$PGSHARENEW/extension/pgsodium_getkey" - if [ -d "/var/lib/postgresql/extension/" ]; then - cp "$SCRIPT_DIR/pgsodium_getkey.sh" "/var/lib/postgresql/extension/pgsodium_getkey" - chown postgres:postgres "/var/lib/postgresql/extension/pgsodium_getkey" - fi - - chown -R postgres:postgres "/tmp/pg_upgrade_bin/$PGVERSION" - - # upgrade job outputs a log in the cwd; needs write permissions - mkdir -p /tmp/pg_upgrade/ - chown -R postgres:postgres /tmp/pg_upgrade/ - cd /tmp/pg_upgrade/ - - # Fixing erros generated by previous dpkg executions (package upgrades et co) - echo "2. Fixing potential errors generated by dpkg" - echo "2.1 Killing off any old hanging apt-get processes" - # One hour is old enough to be bad - pkill -f apt-get --older 3600 2>/dev/null || echo "No hanging apt-get processes found" - DEBIAN_FRONTEND=noninteractive dpkg --configure -a --force-confold || true # handle errors generated by dpkg - - # Needed for PostGIS, since it's compiled with Protobuf-C support now - echo "3. Installing libprotobuf-c1 and libicu66 if missing" - if [[ ! "$(apt list --installed libprotobuf-c1 | grep "installed")" ]]; then - apt-get -o DPkg::Lock::Timeout=600 update -y # wait up to 10 minutes for any dpkg locks to clear before updating package lists - apt --fix-broken install -y libprotobuf-c1 libicu66 || true # apt has builtin 2 minute wait lock - fi - - echo "4. Setup locale if required" - if ! grep -q "^en_US.UTF-8" /etc/locale.gen ; then - echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen - fi - if ! grep -q "^C.UTF-8" /etc/locale.gen ; then - echo "C.UTF-8 UTF-8" >> /etc/locale.gen - fi - locale-gen - - if [ -z "$IS_CI" ] && [ -z "$IS_LOCAL_UPGRADE" ]; then - # DATABASE_UPGRADE_DATA_MIGRATION_DEVICE_NAME = '/dev/xvdp' can be derived from the worker mount - echo "5. Determining block device to mount" - # lsb release - UBUNTU_VERSION=$(lsb_release -rs) - # install amazon disk utilities if not present on 24.04 - if [ "${UBUNTU_VERSION}" = "24.04" ] && ! /usr/bin/dpkg-query -W amazon-ec2-utils >/dev/null 2>&1; then - apt-get -o DPkg::Lock::Timeout=600 update - apt-get -o DPkg::Lock::Timeout=600 install -y amazon-ec2-utils || true - fi - if command -v ebsnvme-id >/dev/null 2>&1 && /usr/bin/dpkg-query -W amazon-ec2-utils >/dev/null 2>&1; then - for nvme_dev in $(lsblk -dprno name,size,mountpoint,type | grep disk | awk '{print $1}'); do - if [ -b "$nvme_dev" ]; then - mapping=$(ebsnvme-id -b "$nvme_dev" 2>/dev/null) - if [[ "$mapping" == "xvdp" || $mapping == "/dev/xvdp" ]]; then - BLOCK_DEVICE="$nvme_dev" - break - fi - fi - done - fi - - # Fallback to lsblk if ebsnvme-id is not available or no mapping found, pre ubuntu 20.04 - if [ -z "${BLOCK_DEVICE:-}" ]; then - echo "No block device found using ebsnvme-id, falling back to lsblk" - # awk NF==3 prints lines with exactly 3 fields, which are the block devices currently not mounted anywhere - # excluding nvme0 since it is the root disk - BLOCK_DEVICE=$(lsblk -dprno name,size,mountpoint,type | grep "disk" | grep -v "nvme0" | awk 'NF==3 { print $1; exit }') # exit ensures we grab the first only - fi - - echo "Block device found: $BLOCK_DEVICE" - - mkdir -p "$MOUNT_POINT" - echo "6. Mounting block device" - - sleep 5 - e2fsck -pf "$BLOCK_DEVICE" - - sleep 1 - mount "$BLOCK_DEVICE" "$MOUNT_POINT" - - sleep 1 - resize2fs "$BLOCK_DEVICE" - else - mkdir -p "$MOUNT_POINT" - fi - - if [ -f "$MOUNT_POINT/pgsodium_root.key" ]; then - cp "$MOUNT_POINT/pgsodium_root.key" /etc/postgresql-custom/pgsodium_root.key - chown postgres:postgres /etc/postgresql-custom/pgsodium_root.key - chmod 600 /etc/postgresql-custom/pgsodium_root.key - fi - - echo "7. Disabling extensions and generating post-upgrade script" - handle_extensions - - echo "8.1. Granting SUPERUSER to postgres user" - run_sql -c "ALTER USER postgres WITH SUPERUSER;" - - if [ "$OLD_BOOTSTRAP_USER" = "postgres" ]; then - echo "8.2. Swap postgres & supabase_admin roles as we're upgrading a project with postgres as bootstrap user" - swap_postgres_and_supabase_admin - fi - - if [ -z "$IS_NIX_UPGRADE" ]; then - if [ -d "/usr/share/postgresql/${PGVERSION}" ]; then - mv "/usr/share/postgresql/${PGVERSION}" "/usr/share/postgresql/${PGVERSION}.bak" - fi - - ln -s "$PGSHARENEW" "/usr/share/postgresql/${PGVERSION}" - cp --remove-destination "$PGLIBNEW"/*.control "$PGSHARENEW/extension/" - cp --remove-destination "$PGLIBNEW"/*.sql "$PGSHARENEW/extension/" - - export LD_LIBRARY_PATH="${PGLIBNEW}" - fi - - echo "9. Creating new data directory, initializing database" - chown -R postgres:postgres "$MOUNT_POINT/" - rm -rf "${PGDATANEW:?}/" - - if [ "$IS_NIX_UPGRADE" = "true" ]; then - if [[ "${PGVERSION%%.*}" -ge 16 ]]; then - LC_ALL=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LANG=en_US.UTF-8 LOCALE_ARCHIVE=/usr/lib/locale/locale-archive su -c ". /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && $PGBINNEW/initdb $CHECKSUM_FLAG --encoding=$SERVER_ENCODING --locale-provider=icu --icu-locale=en_US.UTF-8 -L $PGSHARENEW -D $PGDATANEW/ --username=supabase_admin" -s "$SHELL" postgres - else - LC_ALL=en_US.UTF-8 LC_CTYPE=$SERVER_LC_CTYPE LC_COLLATE=$SERVER_LC_COLLATE LANGUAGE=en_US.UTF-8 LANG=en_US.UTF-8 LOCALE_ARCHIVE=/usr/lib/locale/locale-archive su -c ". /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && $PGBINNEW/initdb $CHECKSUM_FLAG --encoding=$SERVER_ENCODING --lc-collate=$SERVER_LC_COLLATE --lc-ctype=$SERVER_LC_CTYPE -L $PGSHARENEW -D $PGDATANEW/ --username=supabase_admin" -s "$SHELL" postgres - fi - else - su -c "$PGBINNEW/initdb $CHECKSUM_FLAG -L $PGSHARENEW -D $PGDATANEW/ --username=supabase_admin" -s "$SHELL" postgres - - fi - - # This line avoids the need to supply the supabase_admin password on the old - # instance, since pg_upgrade connects to the db as supabase_admin using unix - # sockets, which is gated behind scram-sha-256 per pg_hba.conf.j2. The new - # instance is unaffected. - if ! grep -q "local all supabase_admin trust" /etc/postgresql/pg_hba.conf; then - echo "local all supabase_admin trust -$(cat /etc/postgresql/pg_hba.conf)" > /etc/postgresql/pg_hba.conf - run_sql -c "select pg_reload_conf();" - fi - - TMP_CONFIG="/tmp/pg_upgrade/postgresql.conf" - cp "$POSTGRES_CONFIG_PATH" "$TMP_CONFIG" - - # Check if max_slot_wal_keep_size exists in the config - # Add the setting if not found - echo "max_slot_wal_keep_size = -1" >> "$TMP_CONFIG" - - # Remove db_user_namespace if upgrading from PG15 or lower to PG16+ - if [[ "${OLD_PGVERSION%%.*}" -le 15 && "${PGVERSION%%.*}" -ge 16 ]]; then - sed -i '/^db_user_namespace/d' "$TMP_CONFIG" - fi - - chown postgres:postgres "$TMP_CONFIG" - - UPGRADE_COMMAND=$(cat </tmp/pg_upgrade-nix-gc.log 2>&1 || true + + # Determine system architecture + ARCH=$(uname -m) + if [ "$ARCH" = "aarch64" ]; then + SYSTEM="aarch64-linux" + elif [ "$ARCH" = "x86_64" ]; then + SYSTEM="x86_64-linux" + else + echo "ERROR: Unsupported architecture: $ARCH" + exit 1 + fi + + # Fetch store path from catalog (avoids expensive nix eval - prevents OOM on small instances) + # Each postgres version has its own catalog file: {git_sha}-psql_{version}.json + CATALOG_S3="s3://supabase-internal-artifacts/nix-catalog/${NIX_FLAKE_VERSION}-psql_${PGVERSION}-${SYSTEM}.json" + CATALOG_LOCAL="/tmp/nix-catalog-${NIX_FLAKE_VERSION}-psql_${PGVERSION}-${SYSTEM}.json" + echo "Fetching catalog from: $CATALOG_S3" + + if ! aws s3 cp "$CATALOG_S3" "$CATALOG_LOCAL" --region ap-southeast-1; then + echo "ERROR: Failed to fetch catalog from $CATALOG_S3" + exit 1 + fi + + STORE_PATH=$(jq -r ".\"${SYSTEM}\"" "$CATALOG_LOCAL") + + if [ -z "$STORE_PATH" ] || [ "$STORE_PATH" = "null" ]; then + echo "ERROR: Could not find store path in catalog for ${SYSTEM}" + echo "Catalog contents:" + jq . "$CATALOG_LOCAL" + exit 1 + fi + + echo "Store path: $STORE_PATH" + + # Realize from binary cache (no nix evaluation needed!) + nix-store -r "$STORE_PATH" + + PG_UPGRADE_BIN_DIR="$STORE_PATH" + PGSHARENEW="$PG_UPGRADE_BIN_DIR/share/postgresql" + fi + + PGBINNEW="$PG_UPGRADE_BIN_DIR/bin" + PGLIBNEW="$PG_UPGRADE_BIN_DIR/lib" + + # copy upgrade-specific pgsodium_getkey script into the share dir + chmod +x "$SCRIPT_DIR/pgsodium_getkey.sh" + mkdir -p "$PGSHARENEW/extension" + cp "$SCRIPT_DIR/pgsodium_getkey.sh" "$PGSHARENEW/extension/pgsodium_getkey" + if [ -d "/var/lib/postgresql/extension/" ]; then + cp "$SCRIPT_DIR/pgsodium_getkey.sh" "/var/lib/postgresql/extension/pgsodium_getkey" + chown postgres:postgres "/var/lib/postgresql/extension/pgsodium_getkey" + fi + + chown -R postgres:postgres "/tmp/pg_upgrade_bin/$PGVERSION" + + # upgrade job outputs a log in the cwd; needs write permissions + mkdir -p /tmp/pg_upgrade/ + chown -R postgres:postgres /tmp/pg_upgrade/ + cd /tmp/pg_upgrade/ + + # Fixing erros generated by previous dpkg executions (package upgrades et co) + echo "2. Fixing potential errors generated by dpkg" + echo "2.1 Killing off any old hanging apt-get processes" + # One hour is old enough to be bad + pkill -f apt-get --older 3600 2>/dev/null || echo "No hanging apt-get processes found" + DEBIAN_FRONTEND=noninteractive dpkg --configure -a --force-confold || true # handle errors generated by dpkg + + # Needed for PostGIS, since it's compiled with Protobuf-C support now + echo "3. Installing libprotobuf-c1 and libicu66 if missing" + if [[ ! "$(apt list --installed libprotobuf-c1 | grep "installed")" ]]; then + apt-get -o DPkg::Lock::Timeout=600 update -y # wait up to 10 minutes for any dpkg locks to clear before updating package lists + apt --fix-broken install -y libprotobuf-c1 libicu66 || true # apt has builtin 2 minute wait lock + fi + + echo "4. Setup locale if required" + if ! grep -q "^en_US.UTF-8" /etc/locale.gen; then + echo "en_US.UTF-8 UTF-8" >>/etc/locale.gen + fi + if ! grep -q "^C.UTF-8" /etc/locale.gen; then + echo "C.UTF-8 UTF-8" >>/etc/locale.gen + fi + locale-gen + + if [ -z "$IS_CI" ] && [ -z "$IS_LOCAL_UPGRADE" ]; then + # DATABASE_UPGRADE_DATA_MIGRATION_DEVICE_NAME = '/dev/xvdp' can be derived from the worker mount + echo "5. Determining block device to mount" + # lsb release + UBUNTU_VERSION=$(lsb_release -rs) + # install amazon disk utilities if not present on 24.04 + if [ "${UBUNTU_VERSION}" = "24.04" ] && ! /usr/bin/dpkg-query -W amazon-ec2-utils >/dev/null 2>&1; then + apt-get -o DPkg::Lock::Timeout=600 update + apt-get -o DPkg::Lock::Timeout=600 install -y amazon-ec2-utils || true + fi + if command -v ebsnvme-id >/dev/null 2>&1 && /usr/bin/dpkg-query -W amazon-ec2-utils >/dev/null 2>&1; then + for nvme_dev in $(lsblk -dprno name,size,mountpoint,type | grep disk | awk '{print $1}'); do + if [ -b "$nvme_dev" ]; then + mapping=$(ebsnvme-id -b "$nvme_dev" 2>/dev/null) + if [[ $mapping == "xvdp" || $mapping == "/dev/xvdp" ]]; then + BLOCK_DEVICE="$nvme_dev" + break + fi + fi + done + fi + + # Fallback to lsblk if ebsnvme-id is not available or no mapping found, pre ubuntu 20.04 + if [ -z "${BLOCK_DEVICE:-}" ]; then + echo "No block device found using ebsnvme-id, falling back to lsblk" + # awk NF==3 prints lines with exactly 3 fields, which are the block devices currently not mounted anywhere + # excluding nvme0 since it is the root disk + BLOCK_DEVICE=$(lsblk -dprno name,size,mountpoint,type | grep "disk" | grep -v "nvme0" | awk 'NF==3 { print $1; exit }') # exit ensures we grab the first only + fi + + echo "Block device found: $BLOCK_DEVICE" + + mkdir -p "$MOUNT_POINT" + echo "6. Mounting block device" + + sleep 5 + e2fsck -pf "$BLOCK_DEVICE" + + sleep 1 + mount "$BLOCK_DEVICE" "$MOUNT_POINT" + + sleep 1 + resize2fs "$BLOCK_DEVICE" + else + mkdir -p "$MOUNT_POINT" + fi + + if [ -f "$MOUNT_POINT/pgsodium_root.key" ]; then + cp "$MOUNT_POINT/pgsodium_root.key" /etc/postgresql-custom/pgsodium_root.key + chown postgres:postgres /etc/postgresql-custom/pgsodium_root.key + chmod 600 /etc/postgresql-custom/pgsodium_root.key + fi + + echo "7. Disabling extensions and generating post-upgrade script" + handle_extensions + + echo "8.1. Granting SUPERUSER to postgres user" + run_sql -c "ALTER USER postgres WITH SUPERUSER;" + + if [ "$OLD_BOOTSTRAP_USER" = "postgres" ]; then + echo "8.2. Swap postgres & supabase_admin roles as we're upgrading a project with postgres as bootstrap user" + swap_postgres_and_supabase_admin + fi + + if [ -z "$IS_NIX_UPGRADE" ]; then + if [ -d "/usr/share/postgresql/${PGVERSION}" ]; then + mv "/usr/share/postgresql/${PGVERSION}" "/usr/share/postgresql/${PGVERSION}.bak" + fi + + ln -s "$PGSHARENEW" "/usr/share/postgresql/${PGVERSION}" + cp --remove-destination "$PGLIBNEW"/*.control "$PGSHARENEW/extension/" + cp --remove-destination "$PGLIBNEW"/*.sql "$PGSHARENEW/extension/" + + export LD_LIBRARY_PATH="${PGLIBNEW}" + fi + + echo "9. Creating new data directory, initializing database" + chown -R postgres:postgres "$MOUNT_POINT/" + rm -rf "${PGDATANEW:?}/" + + if [ "$IS_NIX_UPGRADE" = "true" ]; then + if [[ ${PGVERSION%%.*} -ge 16 ]]; then + LC_ALL=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LANG=en_US.UTF-8 LOCALE_ARCHIVE=/usr/lib/locale/locale-archive su -c ". /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && $PGBINNEW/initdb $CHECKSUM_FLAG --encoding=$SERVER_ENCODING --locale-provider=icu --icu-locale=en_US.UTF-8 -L $PGSHARENEW -D $PGDATANEW/ --username=supabase_admin" -s "$SHELL" postgres + else + LC_ALL=en_US.UTF-8 LC_CTYPE=$SERVER_LC_CTYPE LC_COLLATE=$SERVER_LC_COLLATE LANGUAGE=en_US.UTF-8 LANG=en_US.UTF-8 LOCALE_ARCHIVE=/usr/lib/locale/locale-archive su -c ". /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && $PGBINNEW/initdb $CHECKSUM_FLAG --encoding=$SERVER_ENCODING --lc-collate=$SERVER_LC_COLLATE --lc-ctype=$SERVER_LC_CTYPE -L $PGSHARENEW -D $PGDATANEW/ --username=supabase_admin" -s "$SHELL" postgres + fi + else + su -c "$PGBINNEW/initdb $CHECKSUM_FLAG -L $PGSHARENEW -D $PGDATANEW/ --username=supabase_admin" -s "$SHELL" postgres + + fi + + # This line avoids the need to supply the supabase_admin password on the old + # instance, since pg_upgrade connects to the db as supabase_admin using unix + # sockets, which is gated behind scram-sha-256 per pg_hba.conf.j2. The new + # instance is unaffected. + if ! grep -q "local all supabase_admin trust" /etc/postgresql/pg_hba.conf; then + echo "local all supabase_admin trust +$(cat /etc/postgresql/pg_hba.conf)" >/etc/postgresql/pg_hba.conf + run_sql -c "select pg_reload_conf();" + fi + + TMP_CONFIG="/tmp/pg_upgrade/postgresql.conf" + cp "$POSTGRES_CONFIG_PATH" "$TMP_CONFIG" + + # Check if max_slot_wal_keep_size exists in the config + # Add the setting if not found + echo "max_slot_wal_keep_size = -1" >>"$TMP_CONFIG" + + # Remove db_user_namespace if upgrading from PG15 or lower to PG16+ + if [[ ${OLD_PGVERSION%%.*} -le 15 && ${PGVERSION%%.*} -ge 16 ]]; then + sed -i '/^db_user_namespace/d' "$TMP_CONFIG" + fi + + chown postgres:postgres "$TMP_CONFIG" + + UPGRADE_COMMAND=$( + cat < /etc/postgresql/pg_hba.conf --new-options="-c config_file=$TMP_CONFIG" \ --new-options="-c shared_preload_libraries='${SHARED_PRELOAD_LIBRARIES}'" EOF - ) - - if [ "$IS_NIX_BASED_SYSTEM" = "true" ]; then - UPGRADE_COMMAND=". /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && $UPGRADE_COMMAND" - fi - - if [[ "${PGVERSION%%.*}" -ge 16 ]]; then - GRN_PLUGINS_DIR=/var/lib/postgresql/.nix-profile/lib/groonga/plugins LC_ALL=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LANG=en_US.UTF-8 LOCALE_ARCHIVE=/usr/lib/locale/locale-archive su -pc "$UPGRADE_COMMAND --check" -s "$SHELL" postgres - else - GRN_PLUGINS_DIR=/var/lib/postgresql/.nix-profile/lib/groonga/plugins LC_ALL=en_US.UTF-8 LC_CTYPE=$SERVER_LC_CTYPE LC_COLLATE=$SERVER_LC_COLLATE LANGUAGE=en_US.UTF-8 LANG=en_US.UTF-8 LOCALE_ARCHIVE=/usr/lib/locale/locale-archive su -pc "$UPGRADE_COMMAND --check" -s "$SHELL" postgres - fi - - echo "10. Stopping postgres; running pg_upgrade" - # Extra work to ensure postgres is actually stopped - # Mostly needed for PG12 projects with odd systemd unit behavior - if [ -z "$IS_CI" ]; then - retry 5 systemctl restart postgresql - systemctl disable postgresql - retry 5 systemctl stop postgresql - - sleep 3 - systemctl stop postgresql - - else - CI_stop_postgres - fi - - # Start the old PostgreSQL instance with version-specific options - if [[ "${PGVERSION%%.*}" -ge 16 ]]; then - GRN_PLUGINS_DIR=/var/lib/postgresql/.nix-profile/lib/groonga/plugins LC_ALL=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LANG=en_US.UTF-8 LOCALE_ARCHIVE=/usr/lib/locale/locale-archive su -pc "$UPGRADE_COMMAND" -s "$SHELL" postgres - else - GRN_PLUGINS_DIR=/var/lib/postgresql/.nix-profile/lib/groonga/plugins LC_ALL=en_US.UTF-8 LC_CTYPE=$SERVER_LC_CTYPE LC_COLLATE=$SERVER_LC_COLLATE LANGUAGE=en_US.UTF-8 LANG=en_US.UTF-8 LOCALE_ARCHIVE=/usr/lib/locale/locale-archive su -pc "$UPGRADE_COMMAND" -s "$SHELL" postgres - fi - - # copying custom configurations - echo "11. Copying custom configurations" - mkdir -p "$MOUNT_POINT/conf" - cp -R /etc/postgresql-custom/* "$MOUNT_POINT/conf/" - # removing supautils config as to allow the latest one provided by the latest image to be used - rm -f "$MOUNT_POINT/conf/supautils.conf" || true - rm -rf "$MOUNT_POINT/conf/extension-custom-scripts" || true - - # removing wal-g config as to allow it to be explicitly enabled on the new instance - rm -f "$MOUNT_POINT/conf/wal-g.conf" - - # copy sql files generated by pg_upgrade - echo "12. Copying sql files generated by pg_upgrade" - mkdir -p "$MOUNT_POINT/sql" - cp /tmp/pg_upgrade/*.sql "$MOUNT_POINT/sql/" || true - chown -R postgres:postgres "$MOUNT_POINT/sql/" - - echo "13. Cleaning up" - cleanup "complete" + ) + + if [ "$IS_NIX_BASED_SYSTEM" = "true" ]; then + UPGRADE_COMMAND=". /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && $UPGRADE_COMMAND" + fi + + if [[ ${PGVERSION%%.*} -ge 16 ]]; then + GRN_PLUGINS_DIR=/var/lib/postgresql/.nix-profile/lib/groonga/plugins LC_ALL=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LANG=en_US.UTF-8 LOCALE_ARCHIVE=/usr/lib/locale/locale-archive su -pc "$UPGRADE_COMMAND --check" -s "$SHELL" postgres + else + GRN_PLUGINS_DIR=/var/lib/postgresql/.nix-profile/lib/groonga/plugins LC_ALL=en_US.UTF-8 LC_CTYPE=$SERVER_LC_CTYPE LC_COLLATE=$SERVER_LC_COLLATE LANGUAGE=en_US.UTF-8 LANG=en_US.UTF-8 LOCALE_ARCHIVE=/usr/lib/locale/locale-archive su -pc "$UPGRADE_COMMAND --check" -s "$SHELL" postgres + fi + + echo "10. Stopping postgres; running pg_upgrade" + # Extra work to ensure postgres is actually stopped + # Mostly needed for PG12 projects with odd systemd unit behavior + if [ -z "$IS_CI" ]; then + retry 5 systemctl restart postgresql + systemctl disable postgresql + retry 5 systemctl stop postgresql + + sleep 3 + systemctl stop postgresql + + else + CI_stop_postgres + fi + + # Start the old PostgreSQL instance with version-specific options + if [[ ${PGVERSION%%.*} -ge 16 ]]; then + GRN_PLUGINS_DIR=/var/lib/postgresql/.nix-profile/lib/groonga/plugins LC_ALL=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LANG=en_US.UTF-8 LOCALE_ARCHIVE=/usr/lib/locale/locale-archive su -pc "$UPGRADE_COMMAND" -s "$SHELL" postgres + else + GRN_PLUGINS_DIR=/var/lib/postgresql/.nix-profile/lib/groonga/plugins LC_ALL=en_US.UTF-8 LC_CTYPE=$SERVER_LC_CTYPE LC_COLLATE=$SERVER_LC_COLLATE LANGUAGE=en_US.UTF-8 LANG=en_US.UTF-8 LOCALE_ARCHIVE=/usr/lib/locale/locale-archive su -pc "$UPGRADE_COMMAND" -s "$SHELL" postgres + fi + + # copying custom configurations + echo "11. Copying custom configurations" + mkdir -p "$MOUNT_POINT/conf" + cp -R /etc/postgresql-custom/* "$MOUNT_POINT/conf/" + # removing supautils config as to allow the latest one provided by the latest image to be used + rm -f "$MOUNT_POINT/conf/supautils.conf" || true + rm -rf "$MOUNT_POINT/conf/extension-custom-scripts" || true + + # removing wal-g config as to allow it to be explicitly enabled on the new instance + rm -f "$MOUNT_POINT/conf/wal-g.conf" + + # copy sql files generated by pg_upgrade + echo "12. Copying sql files generated by pg_upgrade" + mkdir -p "$MOUNT_POINT/sql" + cp /tmp/pg_upgrade/*.sql "$MOUNT_POINT/sql/" || true + chown -R postgres:postgres "$MOUNT_POINT/sql/" + + echo "13. Cleaning up" + cleanup "complete" } trap cleanup ERR -echo "running" > /tmp/pg-upgrade-status +echo "running" >/tmp/pg-upgrade-status if [ -z "$IS_CI" ] && [ -z "$IS_LOCAL_UPGRADE" ]; then - initiate_upgrade >> "$LOG_FILE" 2>&1 & - echo "Upgrade initiate job completed" + initiate_upgrade >>"$LOG_FILE" 2>&1 & + echo "Upgrade initiate job completed" else - rm -f /tmp/pg-upgrade-status - initiate_upgrade + rm -f /tmp/pg-upgrade-status + initiate_upgrade fi diff --git a/ansible/files/admin_api_scripts/pg_upgrade_scripts/pgsodium_getkey.sh b/ansible/files/admin_api_scripts/pg_upgrade_scripts/pgsodium_getkey.sh index 5a5a90e444..45d169cff8 100755 --- a/ansible/files/admin_api_scripts/pg_upgrade_scripts/pgsodium_getkey.sh +++ b/ansible/files/admin_api_scripts/pg_upgrade_scripts/pgsodium_getkey.sh @@ -5,8 +5,8 @@ set -euo pipefail KEY_FILE=/etc/postgresql-custom/pgsodium_root.key # if key file doesn't exist (project previously didn't use pgsodium), generate a new key -if [[ ! -f "${KEY_FILE}" ]]; then - head -c 32 /dev/urandom | od -A n -t x1 | tr -d ' \n' > $KEY_FILE +if [[ ! -f ${KEY_FILE} ]]; then + head -c 32 /dev/urandom | od -A n -t x1 | tr -d ' \n' >$KEY_FILE fi cat $KEY_FILE diff --git a/ansible/files/admin_api_scripts/unmount-volume.sh b/ansible/files/admin_api_scripts/unmount-volume.sh index 6250b8c47c..f606ecb6f4 100644 --- a/ansible/files/admin_api_scripts/unmount-volume.sh +++ b/ansible/files/admin_api_scripts/unmount-volume.sh @@ -5,39 +5,39 @@ set -euo pipefail MOUNT_POINT=${1:-} DELETE_FLAG=${2:-} -if [[ -z "$MOUNT_POINT" ]]; then - echo "Usage: $0 [--delete-dir]" - echo "Unmount only: sudo ./unmount-volume.sh /data/150008" - echo "Unmount delete dir: sudo ./unmount-volume.sh /data/150008 --delete-dir" - exit 1 +if [[ -z $MOUNT_POINT ]]; then + echo "Usage: $0 [--delete-dir]" + echo "Unmount only: sudo ./unmount-volume.sh /data/150008" + echo "Unmount delete dir: sudo ./unmount-volume.sh /data/150008 --delete-dir" + exit 1 fi # Unmount a block device from a specified mount point -# Remove the corresponding entry from /etc/fstab for persistence across reboots +# Remove the corresponding entry from /etc/fstab for persistence across reboots FSTAB_FILE="/etc/fstab" BACKUP_FILE="/etc/fstab.bak" if mountpoint -q "$MOUNT_POINT"; then - echo "Unmounting $MOUNT_POINT" - umount "$MOUNT_POINT" + echo "Unmounting $MOUNT_POINT" + umount "$MOUNT_POINT" else - echo "$MOUNT_POINT is not currently mounted — skipping umount" + echo "$MOUNT_POINT is not currently mounted — skipping umount" fi UUID=$(findmnt -no UUID "$MOUNT_POINT" 2>/dev/null || true) -if [[ -n "$UUID" ]]; then - echo "Removing UUID=$UUID from $FSTAB_FILE" - cp "$FSTAB_FILE" "$BACKUP_FILE" - sed -i "/UUID=${UUID//\//\\/}/d" "$FSTAB_FILE" +if [[ -n $UUID ]]; then + echo "Removing UUID=$UUID from $FSTAB_FILE" + cp "$FSTAB_FILE" "$BACKUP_FILE" + sed -i "/UUID=${UUID//\//\\/}/d" "$FSTAB_FILE" else - echo "Could not find UUID for $MOUNT_POINT — skipping fstab cleanup" + echo "Could not find UUID for $MOUNT_POINT — skipping fstab cleanup" fi -if [[ "$DELETE_FLAG" == "--delete-dir" ]]; then - echo "Deleting mount point directory: $MOUNT_POINT" - rm -rf "$MOUNT_POINT" +if [[ $DELETE_FLAG == "--delete-dir" ]]; then + echo "Deleting mount point directory: $MOUNT_POINT" + rm -rf "$MOUNT_POINT" fi echo "Unmount and cleanup complete for $MOUNT_POINT" diff --git a/ansible/files/start-envoy.sh b/ansible/files/start-envoy.sh index edd6fe09e4..391a20ddc4 100644 --- a/ansible/files/start-envoy.sh +++ b/ansible/files/start-envoy.sh @@ -1,10 +1,10 @@ #!/usr/bin/env bash set -eou pipefail -if [[ $(cat /sys/module/ipv6/parameters/disable) = 1 ]]; then - sed -i -e "s/address: '::'/address: '0.0.0.0'/" -e 's/ipv4_compat: true/ipv4_compat: false/' /etc/envoy/lds.yaml +if [[ $(cat /sys/module/ipv6/parameters/disable) == 1 ]]; then + sed -i -e "s/address: '::'/address: '0.0.0.0'/" -e 's/ipv4_compat: true/ipv4_compat: false/' /etc/envoy/lds.yaml else - sed -i -e "s/address: '0.0.0.0'/address: '::'/" -e 's/ipv4_compat: false/ipv4_compat: true/' /etc/envoy/lds.yaml + sed -i -e "s/address: '0.0.0.0'/address: '::'/" -e 's/ipv4_compat: false/ipv4_compat: true/' /etc/envoy/lds.yaml fi # Workaround using `tee` to get `/dev/stdout` access logging to work, see: diff --git a/ansible/files/supabase_admin_agent_config/pgdata-chown b/ansible/files/supabase_admin_agent_config/pgdata-chown.sh similarity index 57% rename from ansible/files/supabase_admin_agent_config/pgdata-chown rename to ansible/files/supabase_admin_agent_config/pgdata-chown.sh index 05af5bd7d9..56e4e243d5 100644 --- a/ansible/files/supabase_admin_agent_config/pgdata-chown +++ b/ansible/files/supabase_admin_agent_config/pgdata-chown.sh @@ -10,25 +10,25 @@ set -euo pipefail if [[ $# -ne 2 ]]; then - echo "usage: pgdata-chown " >&2 - exit 1 + echo "usage: pgdata-chown " >&2 + exit 1 fi ACTION="$1" TARGET="$2" REAL=$(realpath "$TARGET") -if [[ "$REAL" != "/data/pgdata" && "$REAL" != /data/pgdata/* ]]; then - echo "error: '${TARGET}' resolves to '${REAL}', which is not under /data/pgdata" >&2 - exit 1 +if [[ $REAL != "/data/pgdata" && $REAL != /data/pgdata/* ]]; then + echo "error: '${TARGET}' resolves to '${REAL}', which is not under /data/pgdata" >&2 + exit 1 fi case "$ACTION" in - to-pgbackrest|to-postgres) - exec /usr/bin/chown -R "${ACTION:3}:postgres" "$REAL" - ;; - *) - echo "error: unknown action '${ACTION}'; expected to-pgbackrest or to-postgres" >&2 - exit 1 - ;; +to-pgbackrest | to-postgres) + exec /usr/bin/chown -R "${ACTION:3}:postgres" "$REAL" + ;; +*) + echo "error: unknown action '${ACTION}'; expected to-pgbackrest or to-postgres" >&2 + exit 1 + ;; esac diff --git a/ansible/files/supabase_admin_agent_config/pgdata-signal b/ansible/files/supabase_admin_agent_config/pgdata-signal.sh similarity index 56% rename from ansible/files/supabase_admin_agent_config/pgdata-signal rename to ansible/files/supabase_admin_agent_config/pgdata-signal.sh index 2479dd6dc1..01839f4f25 100644 --- a/ansible/files/supabase_admin_agent_config/pgdata-signal +++ b/ansible/files/supabase_admin_agent_config/pgdata-signal.sh @@ -13,32 +13,32 @@ set -euo pipefail # prevent PostgreSQL from starting after a restore. Handled as a single-arg # command to keep the sudoers entry scoped to this script rather than allowing # a broad "rm" entry. -if [[ $# -eq 1 && "$1" == "remove-pid" ]]; then - exec /usr/bin/rm -f "/data/pgdata/postmaster.pid" +if [[ $# -eq 1 && $1 == "remove-pid" ]]; then + exec /usr/bin/rm -f "/data/pgdata/postmaster.pid" fi if [[ $# -ne 2 ]]; then - echo "usage: pgdata-signal " >&2 - echo " pgdata-signal remove-pid" >&2 - exit 1 + echo "usage: pgdata-signal " >&2 + echo " pgdata-signal remove-pid" >&2 + exit 1 fi ACTION="$1" SIGNAL_TYPE="$2" case "$SIGNAL_TYPE" in - recovery|standby) FILE="/data/pgdata/${SIGNAL_TYPE}.signal" ;; - *) - echo "error: unknown signal type '${SIGNAL_TYPE}'; expected recovery or standby" >&2 - exit 1 - ;; +recovery | standby) FILE="/data/pgdata/${SIGNAL_TYPE}.signal" ;; +*) + echo "error: unknown signal type '${SIGNAL_TYPE}'; expected recovery or standby" >&2 + exit 1 + ;; esac case "$ACTION" in - create) exec /usr/bin/touch "${FILE}" ;; - remove) exec /usr/bin/rm -f "${FILE}" ;; - *) - echo "error: unknown action '${ACTION}'; expected create or remove" >&2 - exit 1 - ;; +create) exec /usr/bin/touch "${FILE}" ;; +remove) exec /usr/bin/rm -f "${FILE}" ;; +*) + echo "error: unknown action '${ACTION}'; expected create or remove" >&2 + exit 1 + ;; esac diff --git a/ansible/files/supascan_ami.sh b/ansible/files/supascan_ami.sh index 4696191889..8a025ffaf0 100644 --- a/ansible/files/supascan_ami.sh +++ b/ansible/files/supascan_ami.sh @@ -19,8 +19,8 @@ echo "" # Check baselines directory exists if [[ ! -d $BASELINES_DIR ]]; then - echo "ERROR: Baselines directory not found: $BASELINES_DIR" - exit 1 + echo "ERROR: Baselines directory not found: $BASELINES_DIR" + exit 1 fi # Add ubuntu user's nix profile to PATH @@ -28,9 +28,9 @@ export PATH="/home/ubuntu/.nix-profile/bin:$PATH" # Verify supascan is available if ! command -v supascan &>/dev/null; then - echo "ERROR: supascan not found in PATH" - echo "PATH: $PATH" - exit 1 + echo "ERROR: supascan not found in PATH" + echo "PATH: $PATH" + exit 1 fi # Run supascan validate (it calls sudo goss internally for privileged checks) diff --git a/ansible/files/systemd-networkd/systemd-networkd-check-and-fix.sh b/ansible/files/systemd-networkd/systemd-networkd-check-and-fix.sh index af00b412b3..10e4aab832 100644 --- a/ansible/files/systemd-networkd/systemd-networkd-check-and-fix.sh +++ b/ansible/files/systemd-networkd/systemd-networkd-check-and-fix.sh @@ -6,15 +6,15 @@ journalctl --no-pager --unit systemd-networkd --since "1 minutes ago" --grep "Co NDISC_ERROR=$? if systemctl is-active --quiet systemd-networkd.service && [ "${NDISC_ERROR}" == 0 ]; then - echo "$(date) systemd-network running but NDisc routes are broken. Restarting systemd.networkd.service" - /usr/bin/systemctl restart systemd-networkd.service - exit # no need to check further + echo "$(date) systemd-network running but NDisc routes are broken. Restarting systemd.networkd.service" + /usr/bin/systemctl restart systemd-networkd.service + exit # no need to check further fi # check for routes ROUTES=$(ip -6 route list) -if ! echo "${ROUTES}" | grep default >/dev/null || ! echo "${ROUTES}" | grep "::1 dev lo">/dev/null; then - echo "IPv6 routing table messed up. Restarting systemd.networkd.service" - /usr/bin/systemctl restart systemd-networkd.service +if ! echo "${ROUTES}" | grep default >/dev/null || ! echo "${ROUTES}" | grep "::1 dev lo" >/dev/null; then + echo "IPv6 routing table messed up. Restarting systemd.networkd.service" + /usr/bin/systemctl restart systemd-networkd.service fi diff --git a/ansible/files/walg_helper_scripts/wal_change_ownership.sh b/ansible/files/walg_helper_scripts/wal_change_ownership.sh index 3f0112d2fa..01541f1605 100644 --- a/ansible/files/walg_helper_scripts/wal_change_ownership.sh +++ b/ansible/files/walg_helper_scripts/wal_change_ownership.sh @@ -4,7 +4,7 @@ set -euo pipefail filename=$1 -if [[ -z "$filename" ]]; then +if [[ -z $filename ]]; then echo "Nothing supplied. Exiting." exit 1 fi @@ -15,18 +15,18 @@ num_paths=$(readlink -f "$full_path" | wc -l) # Checks if supplied filename string contains multiple paths # For example, "correct/path /var/lib/injected/path /var/lib/etc" -if [[ "$num_paths" -gt 1 ]]; then +if [[ $num_paths -gt 1 ]]; then echo "Multiple paths supplied. Exiting." exit 1 fi base_dir=$(readlink -f "$full_path" | cut -d'/' -f2) -# Checks if directory/ file to be manipulated +# Checks if directory/ file to be manipulated # is indeed within the /tmp directory -# For example, "/tmp/../var/lib/postgresql/..." +# For example, "/tmp/../var/lib/postgresql/..." # will return "var" as the value for $base_dir -if [[ "$base_dir" != "tmp" ]]; then +if [[ $base_dir != "tmp" ]]; then echo "Attempt to manipulate a file not in /tmp. Exiting." exit 1 fi diff --git a/ansible/files/walg_helper_scripts/wal_fetch.sh b/ansible/files/walg_helper_scripts/wal_fetch.sh index 33448ac95c..0e3109f364 100644 --- a/ansible/files/walg_helper_scripts/wal_fetch.sh +++ b/ansible/files/walg_helper_scripts/wal_fetch.sh @@ -3,7 +3,7 @@ set -euo pipefail # Fetch the WAL file and temporarily store them in /tmp -sudo -u wal-g wal-g wal-fetch "$1" /tmp/wal_fetch_dir/"$1" --config /etc/wal-g/config.json +sudo -u wal-g wal-g wal-fetch "$1" /tmp/wal_fetch_dir/"$1" --config /etc/wal-g/config.json # Ensure WAL file is owned by the postgres Linux user sudo -u root /root/wal_change_ownership.sh "$1" diff --git a/ansible/tasks/internal/supabase-admin-agent.yml b/ansible/tasks/internal/supabase-admin-agent.yml index 3f4d802db1..69e7861291 100644 --- a/ansible/tasks/internal/supabase-admin-agent.yml +++ b/ansible/tasks/internal/supabase-admin-agent.yml @@ -41,7 +41,7 @@ - name: supabase-admin-agent - pgdata-chown script copy: - src: files/supabase_admin_agent_config/pgdata-chown + src: files/supabase_admin_agent_config/pgdata-chown.sh dest: /usr/local/lib/supabase-admin-agent/pgdata-chown owner: root group: root @@ -50,7 +50,7 @@ - name: supabase-admin-agent - pgdata-signal script copy: - src: files/supabase_admin_agent_config/pgdata-signal + src: files/supabase_admin_agent_config/pgdata-signal.sh dest: /usr/local/lib/supabase-admin-agent/pgdata-signal owner: root group: root diff --git a/ci/extensions-diff.sh b/ci/extensions-diff.sh index 0965a08d54..de3f6cd9bb 100755 --- a/ci/extensions-diff.sh +++ b/ci/extensions-diff.sh @@ -350,13 +350,13 @@ analyze_variant_extension_deps() { echo "
" echo "Raw Dependency Tree" echo "" - echo "\`\`\`" + echo '```' echo "Old ($old_so):" echo "$old_deps" echo "" echo "New ($new_so):" echo "$new_deps" - echo "\`\`\`" + echo '```' echo "" echo "
" echo "" @@ -406,7 +406,7 @@ SUMMARY_CONTENT=$(generate_summary) # Insert summary after the header but before PostgreSQL version sections # We need to insert it after the header (line with "Analysis Date") and before first "##" heading -if [[ "$OSTYPE" == "darwin"* ]]; then +if [[ $OSTYPE == "darwin"* ]]; then # macOS - create temp file with proper structure { # Read header (including bullet point lines) diff --git a/ci/postgresql-diff.sh b/ci/postgresql-diff.sh index 08a3331f3b..3f807977fe 100755 --- a/ci/postgresql-diff.sh +++ b/ci/postgresql-diff.sh @@ -341,7 +341,7 @@ analyze_variant_deps() { echo "
" echo "Raw Dependency Closure" echo "" - echo "\`\`\`" + echo '```' echo "Old Dependencies (closure: ${old_closure_mb:-?} MB):" while read -r path; do dep_size=$(nix path-info -S "$path" --json 2>/dev/null | jq -r '.[].narSize' 2>/dev/null || echo "") @@ -363,7 +363,7 @@ analyze_variant_deps() { echo " $path" fi done <"/tmp/new-$variant-deps-$$.txt" - echo "\`\`\`" + echo '```' echo "" echo "
" echo "" diff --git a/docker/nix/build_nix.sh b/docker/nix/build_nix.sh index 118fdf61d9..018075d78f 100755 --- a/docker/nix/build_nix.sh +++ b/docker/nix/build_nix.sh @@ -3,7 +3,7 @@ set -eou pipefail nix --version if [ -d "/workspace" ]; then - cd /workspace + cd /workspace fi nix run "github:Mic92/nix-fast-build?rev=b1dae483ab7d4139a6297e02b6de9e5d30e43d48" -- --skip-cached --no-nom --flake ".#checks" diff --git a/docker/pgctld/pgctld-wrapper b/docker/pgctld/pgctld-wrapper.sh similarity index 92% rename from docker/pgctld/pgctld-wrapper rename to docker/pgctld/pgctld-wrapper.sh index 9683d33f00..b5537589c3 100644 --- a/docker/pgctld/pgctld-wrapper +++ b/docker/pgctld/pgctld-wrapper.sh @@ -28,5 +28,5 @@ mkdir -p /var/log/postgresql 2>/dev/null || true ln -sf /proc/1/fd/1 /var/log/postgresql/postgresql.json exec /nix/var/nix/profiles/default/bin/pgctld \ - --postgres-config-template "${POSTGRES_CONFIG_TEMPLATE_PATH:-/etc/pgctld-custom/postgresql.conf.tmpl}" \ - "$@" + --postgres-config-template "${POSTGRES_CONFIG_TEMPLATE_PATH:-/etc/pgctld-custom/postgresql.conf.tmpl}" \ + "$@" diff --git a/ebssurrogate/scripts/chroot-bootstrap-nix.sh b/ebssurrogate/scripts/chroot-bootstrap-nix.sh index 3a58d73ba7..e084f7d546 100755 --- a/ebssurrogate/scripts/chroot-bootstrap-nix.sh +++ b/ebssurrogate/scripts/chroot-bootstrap-nix.sh @@ -16,17 +16,16 @@ export APT_OPTIONS="-oAPT::Install-Recommends=false \ # Prevent services from starting during package installation in chroot # This avoids hangs from cloud-init, dbus, etc. trying to start services -cat > /usr/sbin/policy-rc.d <<'EOF' +cat >/usr/sbin/policy-rc.d <<'EOF' #!/bin/sh exit 101 EOF chmod +x /usr/sbin/policy-rc.d -if [ $(dpkg --print-architecture) = "amd64" ]; -then - ARCH="amd64"; +if [ $(dpkg --print-architecture) = "amd64" ]; then + ARCH="amd64" else - ARCH="arm64"; + ARCH="arm64" fi # Get current mirror from sources.list @@ -196,8 +195,6 @@ function apt_install_with_fallback { return 1 } - - function update_install_packages { source /etc/os-release @@ -213,7 +210,7 @@ function update_install_packages { if [ "${ARCH}" = "amd64" ]; then echo 'grub-pc grub-pc/install_devices_empty select true' | debconf-set-selections echo 'grub-pc grub-pc/install_devices select' | debconf-set-selections - # Install various packages needed for a booting system (with mirror fallback) + # Install various packages needed for a booting system (with mirror fallback) if ! apt_install_with_fallback install -y linux-aws grub-pc e2fsprogs; then echo "FATAL: Failed to install boot packages" exit 1 @@ -277,11 +274,11 @@ function update_install_packages { } function setup_locale { -cat << EOF >> /etc/locale.gen + cat <>/etc/locale.gen en_US.UTF-8 UTF-8 EOF -cat << EOF > /etc/default/locale + cat </etc/default/locale LANG="C.UTF-8" LC_CTYPE="C.UTF-8" EOF @@ -289,11 +286,11 @@ EOF } function setup_postgesql_env { - # Create the directory if it doesn't exist - sudo mkdir -p /etc/environment.d - - # Define the contents of the PostgreSQL environment file - cat </dev/null + # Create the directory if it doesn't exist + sudo mkdir -p /etc/environment.d + + # Define the contents of the PostgreSQL environment file + cat </dev/null LOCALE_ARCHIVE=/usr/lib/locale/locale-archive LANG="en_US.UTF-8" LANGUAGE="en_US.UTF-8" @@ -304,15 +301,15 @@ EOF function install_packages_for_build { apt-get install -y --no-install-recommends linux-libc-dev \ - acl \ - magic-wormhole sysstat \ - build-essential libreadline-dev zlib1g-dev flex bison libxml2-dev libxslt-dev libssl-dev libsystemd-dev libpq-dev libxml2-utils uuid-dev xsltproc ssl-cert \ - gcc-10 g++-10 \ - libgeos-dev libproj-dev libgdal-dev libjson-c-dev libboost-all-dev libcgal-dev libmpfr-dev libgmp-dev cmake \ - libkrb5-dev \ - maven default-jre default-jdk \ - curl gpp apt-transport-https cmake libc++-dev libc++abi-dev libc++1 libglib2.0-dev libtinfo5 libc++abi1 ninja-build python \ - liblzo2-dev + acl \ + magic-wormhole sysstat \ + build-essential libreadline-dev zlib1g-dev flex bison libxml2-dev libxslt-dev libssl-dev libsystemd-dev libpq-dev libxml2-utils uuid-dev xsltproc ssl-cert \ + gcc-10 g++-10 \ + libgeos-dev libproj-dev libgdal-dev libjson-c-dev libboost-all-dev libcgal-dev libmpfr-dev libgmp-dev cmake \ + libkrb5-dev \ + maven default-jre default-jdk \ + curl gpp apt-transport-https cmake libc++-dev libc++abi-dev libc++1 libglib2.0-dev libtinfo5 libc++abi1 ninja-build python \ + liblzo2-dev source /etc/os-release @@ -332,7 +329,7 @@ function setup_apparmor { } function setup_grub_conf { -cat << EOF > /etc/default/grub + cat </etc/default/grub GRUB_DEFAULT=0 GRUB_TIMEOUT=0 GRUB_TIMEOUT_STYLE="hidden" @@ -363,10 +360,10 @@ function disable_fsck { # Don't request hostname during boot but set hostname function setup_hostname { # Set the static hostname - echo "ubuntu" > /etc/hostname + echo "ubuntu" >/etc/hostname chmod 644 /etc/hostname # Update netplan configuration to not send hostname - cat << EOF > /etc/netplan/01-hostname.yaml + cat </etc/netplan/01-hostname.yaml network: version: 2 ethernets: @@ -381,7 +378,7 @@ EOF # Set options for the default interface function setup_eth0_interface { -cat << EOF > /etc/netplan/eth0.yaml + cat </etc/netplan/eth0.yaml network: version: 2 ethernets: @@ -394,8 +391,8 @@ EOF function disable_sshd_passwd_auth { sed -i -E -e 's/^#?\s*PasswordAuthentication\s+(yes|no)\s*$/PasswordAuthentication no/g' \ - -e 's/^#?\s*ChallengeResponseAuthentication\s+(yes|no)\s*$/ChallengeResponseAuthentication no/g' \ - /etc/ssh/sshd_config + -e 's/^#?\s*ChallengeResponseAuthentication\s+(yes|no)\s*$/ChallengeResponseAuthentication no/g' \ + /etc/ssh/sshd_config } function create_admin_account { @@ -413,7 +410,7 @@ function setup_ccache { apt-get install ccache -y mkdir -p /tmp/ccache export PATH=/usr/lib/ccache:$PATH - echo "PATH=$PATH" >> /etc/environment + echo "PATH=$PATH" >>/etc/environment } # Clear apt caches diff --git a/ebssurrogate/scripts/qemu-bootstrap-nix.sh b/ebssurrogate/scripts/qemu-bootstrap-nix.sh index a76186bf75..d61c3c628e 100755 --- a/ebssurrogate/scripts/qemu-bootstrap-nix.sh +++ b/ebssurrogate/scripts/qemu-bootstrap-nix.sh @@ -42,7 +42,7 @@ EOF --extra-vars "postgresql_major_version=${POSTGRES_MAJOR_VERSION}" \ --extra-vars "postgresql_major=${POSTGRES_MAJOR_VERSION}" \ --extra-vars "psql_version=psql_${POSTGRES_MAJOR_VERSION}" \ - --extra-vars @./ansible/qemu-vars.yaml + --extra-vars @./ansible/qemu-vars.yaml } function setup_postgesql_env { @@ -84,14 +84,14 @@ execute_playbook #################### function install_nix() { - sudo su -c "sh <(curl -L https://releases.nixos.org/nix/nix-2.34.6/install) --yes --daemon --nix-extra-conf-file /dev/stdin < sda - - # Create /dev/xvd* device symlink - if [[ ! -z "$mapping" ]] && [[ -b "${blkdev}" ]] && [[ ! -L "${mapping}" ]]; then - ln -s "$blkdev" "$mapping" - - blkdev_mappings["$blkdev"]="$mapping" - fi + for blkdev in $( # /dev/nvme*n* + nvme list | awk '/^\/dev/ { print $1 }' + ); do + # Mapping info from disk headers + header=$(nvme id-ctrl --raw-binary "${blkdev}" | cut -c3073-3104 | tr -s ' ' | sed 's/ $//g' | sed 's!/dev/!!') + mapping="/dev/${header%%[0-9]}" # normalize sda1 => sda + + # Create /dev/xvd* device symlink + if [[ -n $mapping ]] && [[ -b ${blkdev} ]] && [[ ! -L ${mapping} ]]; then + ln -s "$blkdev" "$mapping" + + blkdev_mappings["$blkdev"]="$mapping" + fi done create_partition_table # NVMe EBS launch device partition mappings (symlinks): /dev/nvme*n*p* to /dev/xvd*[0-9]+ declare -A partdev_mappings - for blkdev in "${!blkdev_mappings[@]}"; do # /dev/nvme*n* - mapping="${blkdev_mappings[$blkdev]}" + for blkdev in "${!blkdev_mappings[@]}"; do # /dev/nvme*n* + mapping="${blkdev_mappings[$blkdev]}" - # Create /dev/xvd*[0-9]+ partition device symlink - for partdev in "${blkdev}"p*; do - partnum=${partdev##*p} - if [[ ! -L "${mapping}${partnum}" ]]; then - ln -s "${blkdev}p${partnum}" "${mapping}${partnum}" + # Create /dev/xvd*[0-9]+ partition device symlink + for partdev in "${blkdev}"p*; do + partnum=${partdev##*p} + if [[ ! -L "${mapping}${partnum}" ]]; then + ln -s "${blkdev}p${partnum}" "${mapping}${partnum}" - partdev_mappings["${blkdev}p${partnum}"]="${mapping}${partnum}" - fi - done + partdev_mappings["${blkdev}p${partnum}"]="${mapping}${partnum}" + fi + done done } - #Download and install latest e2fsprogs for fast_commit feature,if required. function format_and_mount_rootfs { mkfs.ext4 -m0.1 /dev/xvdf2 @@ -248,17 +248,17 @@ function create_fstab { [ -n "${EFI_LINE}" ] && echo "${EFI_LINE}" echo "${DATA_LINE}" echo "${SWAP_LINE}" - } > "/mnt/etc/fstab" + } >"/mnt/etc/fstab" unset FMT } function setup_chroot_environment { UBUNTU_VERSION=$(lsb_release -cs) # 'noble' for Ubuntu 24.04 - # sometimes debootstrap will get stuck on a download for a long time - # the default read timeout in wget is 900s, which can cause a ~15min increase in build time - # this forces the process to fail-fast and retry - cat < ~/.wgetrc + # sometimes debootstrap will get stuck on a download for a long time + # the default read timeout in wget is 900s, which can cause a ~15min increase in build time + # this forces the process to fail-fast and retry + cat <~/.wgetrc read_timeout = 30 timeout = 35 tries = 5 @@ -285,7 +285,7 @@ EOF mount --rbind /proc /mnt/proc mount --rbind /sys /mnt/sys - # Create build mount point and mount + # Create build mount point and mount mkdir -p /mnt/tmp mount /dev/xvdc /mnt/tmp chmod 777 /mnt/tmp @@ -301,10 +301,10 @@ EOF cp /tmp/chroot-bootstrap-nix.sh /mnt/tmp/chroot-bootstrap-nix.sh chroot /mnt /tmp/chroot-bootstrap-nix.sh rm -f /mnt/tmp/chroot-bootstrap-nix.sh - echo "${POSTGRES_SUPABASE_VERSION}" > /mnt/root/supabase-release + echo "${POSTGRES_SUPABASE_VERSION}" >/mnt/root/supabase-release # Copy the AMI version into the /etc/supabase-release file - echo "${POSTGRES_SUPABASE_VERSION}" > /mnt/etc/supabase-release + echo "${POSTGRES_SUPABASE_VERSION}" >/mnt/etc/supabase-release chmod 644 /mnt/etc/supabase-release # Copy the nvme identification script into /sbin inside the chroot @@ -330,7 +330,7 @@ function download_ccache { function execute_playbook { sudo mkdir -p /etc/ansible -tee /etc/ansible/ansible.cfg < /dev/null 2>&1 && pwd ) +db=$(cd -- "$(dirname -- "$0")" >/dev/null 2>&1 && pwd) if [ -z "${USE_DBMATE:-}" ]; then - psql -v ON_ERROR_STOP=1 --no-password --no-psqlrc -U supabase_admin <> $PGDATA/postgresql.conf - echo "unix_socket_directories='$PGHOST'" >> $PGDATA/postgresql.conf - echo "unix_socket_permissions=0700" >> $PGDATA/postgresql.conf + echo 'Initializing postgresql database...' + initdb $PGDATA --locale=C --username $PGUSER -A md5 --pwfile=<(echo $PGPASS) --auth=trust + echo "listen_addresses='*'" >>$PGDATA/postgresql.conf + echo "unix_socket_directories='$PGHOST'" >>$PGDATA/postgresql.conf + echo "unix_socket_permissions=0700" >>$PGDATA/postgresql.conf fi chmod o-rwx $PGDATA diff --git a/nix/packages/cli-config/pgsodium_getkey.sh b/nix/packages/cli-config/pgsodium_getkey.sh index 9a074363bb..bc4da10fd4 100755 --- a/nix/packages/cli-config/pgsodium_getkey.sh +++ b/nix/packages/cli-config/pgsodium_getkey.sh @@ -8,7 +8,7 @@ KEY_DIR="$(dirname "$KEY_FILE")" # Create directory if it doesn't exist mkdir -p "$KEY_DIR" -if [[ ! -f "${KEY_FILE}" ]]; then - head -c 32 /dev/urandom | od -A n -t x1 | tr -d ' \n' > "${KEY_FILE}" +if [[ ! -f ${KEY_FILE} ]]; then + head -c 32 /dev/urandom | od -A n -t x1 | tr -d ' \n' >"${KEY_FILE}" fi cat "$KEY_FILE" diff --git a/nix/packages/cli-config/supabase-postgres-init.sh b/nix/packages/cli-config/supabase-postgres-init.sh index 1af0691df1..8f90f4c8a4 100755 --- a/nix/packages/cli-config/supabase-postgres-init.sh +++ b/nix/packages/cli-config/supabase-postgres-init.sh @@ -19,172 +19,173 @@ PGBIN="$BUNDLE_DIR/bin" # Logging functions postgres_log() { - local type="$1"; shift - printf '%s [%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$type" "$*" + local type="$1" + shift + printf '%s [%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$type" "$*" } postgres_note() { - postgres_log Note "$@" + postgres_log Note "$@" } postgres_error() { - postgres_log ERROR "$@" >&2 + postgres_log ERROR "$@" >&2 } # Check if PGDATA is initialized postgres_is_initialized() { - [ -s "$PGDATA/PG_VERSION" ] + [ -s "$PGDATA/PG_VERSION" ] } # Setup initial database postgres_setup_db() { - postgres_note "Initializing database in $PGDATA" + postgres_note "Initializing database in $PGDATA" - # Create PGDATA directory if it doesn't exist - mkdir -p "$PGDATA" + # Create PGDATA directory if it doesn't exist + mkdir -p "$PGDATA" - # Run initdb - "$PGBIN/initdb" \ - -D "$PGDATA" \ - -U "$POSTGRES_USER" \ - --encoding=UTF8 \ - --locale=C \ - --no-instructions + # Run initdb + "$PGBIN/initdb" \ + -D "$PGDATA" \ + -U "$POSTGRES_USER" \ + --encoding=UTF8 \ + --locale=C \ + --no-instructions - postgres_note "Database initialized" + postgres_note "Database initialized" } # Setup configuration files postgres_setup_config() { - postgres_note "Setting up configuration files" + postgres_note "Setting up configuration files" - # Copy config templates - cp "$BUNDLE_DIR/share/supabase-cli/config/postgresql.conf.template" "$PGDATA/postgresql.conf" - cp "$BUNDLE_DIR/share/supabase-cli/config/pg_hba.conf.template" "$PGDATA/pg_hba.conf" - cp "$BUNDLE_DIR/share/supabase-cli/config/pg_ident.conf.template" "$PGDATA/pg_ident.conf" + # Copy config templates + cp "$BUNDLE_DIR/share/supabase-cli/config/postgresql.conf.template" "$PGDATA/postgresql.conf" + cp "$BUNDLE_DIR/share/supabase-cli/config/pg_hba.conf.template" "$PGDATA/pg_hba.conf" + cp "$BUNDLE_DIR/share/supabase-cli/config/pg_ident.conf.template" "$PGDATA/pg_ident.conf" - # Set absolute path to getkey script in postgresql.conf - GETKEY_SCRIPT="$BUNDLE_DIR/share/supabase-cli/config/pgsodium_getkey.sh" + # Set absolute path to getkey script in postgresql.conf + GETKEY_SCRIPT="$BUNDLE_DIR/share/supabase-cli/config/pgsodium_getkey.sh" - # Ensure getkey script is executable - if [ -f "$GETKEY_SCRIPT" ]; then - chmod +x "$GETKEY_SCRIPT" - fi + # Ensure getkey script is executable + if [ -f "$GETKEY_SCRIPT" ]; then + chmod +x "$GETKEY_SCRIPT" + fi - cat >> "$PGDATA/postgresql.conf" << EOF + cat >>"$PGDATA/postgresql.conf" < "$KEY_FILE" +if [[ ! -f $KEY_FILE ]]; then + head -c 32 /dev/urandom | od -A n -t x1 | tr -d ' \n' >"$KEY_FILE" fi cat $KEY_FILE diff --git a/nix/tests/util/pgsodium_getkey_arb.sh b/nix/tests/util/pgsodium_getkey_arb.sh index 446dbba2f7..46614d4aff 100755 --- a/nix/tests/util/pgsodium_getkey_arb.sh +++ b/nix/tests/util/pgsodium_getkey_arb.sh @@ -1 +1 @@ -echo -n 8359dafbba5c05568799c1c24eb6c2fbff497654bc6aa5e9a791c666768875a1 \ No newline at end of file +echo -n 8359dafbba5c05568799c1c24eb6c2fbff497654bc6aa5e9a791c666768875a1 diff --git a/scripts/00-python_install.sh b/scripts/00-python_install.sh index 3a7bb75608..aa1e5c9778 100644 --- a/scripts/00-python_install.sh +++ b/scripts/00-python_install.sh @@ -1,3 +1,3 @@ sudo apt-get update sudo apt-get install python -y -sudo apt-get install python-pip -y \ No newline at end of file +sudo apt-get install python-pip -y diff --git a/scripts/01-postgres_check.sh b/scripts/01-postgres_check.sh index d131528eed..2d9298b05e 100644 --- a/scripts/01-postgres_check.sh +++ b/scripts/01-postgres_check.sh @@ -1,7 +1,7 @@ #!/bin/bash # # Scripts in this directory are run during the build process. -# each script will be uploaded to /tmp on your build droplet, +# each script will be uploaded to /tmp on your build droplet, # given execute permissions and run. The cleanup process will # remove the scripts from your build system after they have run # if you use the build_image task. @@ -9,64 +9,64 @@ echo "Commencing Checks" function check_database_is_ready { - echo -e "\nChecking if database is ready and accepting connections:" - if [ "$(pg_isready)" = "/tmp:5432 - accepting connections" ]; then - echo "Database is ready" - else - echo "Error: Database is not ready. Exiting" - exit 1 - fi + echo -e "\nChecking if database is ready and accepting connections:" + if [ "$(pg_isready)" = "/tmp:5432 - accepting connections" ]; then + echo "Database is ready" + else + echo "Error: Database is not ready. Exiting" + exit 1 + fi } function check_postgres_owned_dir_exists { - DIR=$1 - USER="postgres" + DIR=$1 + USER="postgres" - echo -e "\nChecking if $DIR exists and owned by postgres user:" + echo -e "\nChecking if $DIR exists and owned by postgres user:" - if [ -d "$DIR" ]; then - echo "$DIR exists" - if [ $(stat -c '%U' $DIR) = "$USER" ]; then - echo "$DIR is owned by $USER" - else - echo "Error: $DIR is not owned by $USER" - exit 1 - fi - else - echo "Error: ${DIR} not found. Exiting." - exit 1 - fi + if [ -d "$DIR" ]; then + echo "$DIR exists" + if [ $(stat -c '%U' $DIR) = "$USER" ]; then + echo "$DIR is owned by $USER" + else + echo "Error: $DIR is not owned by $USER" + exit 1 + fi + else + echo "Error: ${DIR} not found. Exiting." + exit 1 + fi } function check_lse_enabled { - ARCH=$(uname -m) - if [ $ARCH = "aarch64" ]; then - echo -e "\nArchitecture is $ARCH. Checking for LSE:" + ARCH=$(uname -m) + if [ $ARCH = "aarch64" ]; then + echo -e "\nArchitecture is $ARCH. Checking for LSE:" - LSE_COUNT=$(objdump -d /usr/lib/postgresql/bin/postgres | grep -i 'ldxr\|ldaxr\|stxr\|stlxr' | wc -l) - MOUTLINE_ATOMICS_COUNT=$(nm /usr/lib/postgresql/bin/postgres | grep __aarch64_have_lse_atomics | wc -l) + LSE_COUNT=$(objdump -d /usr/lib/postgresql/bin/postgres | grep -i 'ldxr\|ldaxr\|stxr\|stlxr' | wc -l) + MOUTLINE_ATOMICS_COUNT=$(nm /usr/lib/postgresql/bin/postgres | grep __aarch64_have_lse_atomics | wc -l) - # Checking for load and store exclusives - if [ $LSE_COUNT -gt 0 ]; then - echo "Postgres has LSE enabled" - else - echo "Error: Postgres failed to be compiled with LSE. Exiting" - exit 1 - fi + # Checking for load and store exclusives + if [ $LSE_COUNT -gt 0 ]; then + echo "Postgres has LSE enabled" + else + echo "Error: Postgres failed to be compiled with LSE. Exiting" + exit 1 + fi - # Checking if successfully compiled with -moutline-atomics - if [ $MOUTLINE_ATOMICS_COUNT -gt 0 ]; then - echo "Postgres has been compiled with -moutline-atomics" - else - echo "Error: Postgres failed to be compiled with -moutline-atomics. Exiting" - exit 1 - fi - else - echo "Architecture is $ARCH. Not checking for LSE." - fi + # Checking if successfully compiled with -moutline-atomics + if [ $MOUTLINE_ATOMICS_COUNT -gt 0 ]; then + echo "Postgres has been compiled with -moutline-atomics" + else + echo "Error: Postgres failed to be compiled with -moutline-atomics. Exiting" + exit 1 + fi + else + echo "Architecture is $ARCH. Not checking for LSE." + fi } check_database_is_ready check_postgres_owned_dir_exists "/var/lib/postgresql" check_postgres_owned_dir_exists "/etc/postgresql" -check_lse_enabled \ No newline at end of file +check_lse_enabled diff --git a/scripts/11-lemp.sh b/scripts/11-lemp.sh index c340f5e9fd..726a57a1b3 100644 --- a/scripts/11-lemp.sh +++ b/scripts/11-lemp.sh @@ -7,8 +7,8 @@ rm -rvf /etc/nginx/sites-enabled/default ln -s /etc/nginx/sites-available/digitalocean \ - /etc/nginx/sites-enabled/digitalocean + /etc/nginx/sites-enabled/digitalocean rm -rf /var/www/html/index*debian.html -chown -R www-data: /var/www \ No newline at end of file +chown -R www-data: /var/www diff --git a/scripts/12-ufw-nginx.sh b/scripts/12-ufw-nginx.sh index 7c47366cd9..f5c2e3a45f 100644 --- a/scripts/12-ufw-nginx.sh +++ b/scripts/12-ufw-nginx.sh @@ -7,4 +7,4 @@ ufw limit ssh ufw allow 'Nginx Full' -ufw --force enable \ No newline at end of file +ufw --force enable diff --git a/scripts/13-force-ssh-logout.sh b/scripts/13-force-ssh-logout.sh index 99e28c180a..ddbcd1cbf0 100644 --- a/scripts/13-force-ssh-logout.sh +++ b/scripts/13-force-ssh-logout.sh @@ -4,7 +4,7 @@ # © 2021 DigitalOcean LLC. # This code is licensed under Apache 2.0 license (see LICENSE.md for details) -cat >> /etc/ssh/sshd_config <>/etc/ssh/sshd_config < /root/.bash_history +cat /dev/null >/root/.bash_history unset HISTFILE journalctl --rotate @@ -90,16 +90,17 @@ rm -f /root/.ssh/authorized_keys /etc/ssh/*key* touch /etc/ssh/revoked_keys chmod 600 /etc/ssh/revoked_keys -cat /dev/null > /var/log/lastlog -cat /dev/null > /var/log/wtmp +cat /dev/null >/var/log/lastlog +cat /dev/null >/var/log/wtmp dd if=/dev/zero of=/zerofile & - PID=$! - while [ -d /proc/$PID ] - do - printf "." - sleep 5 - done -sync; rm /zerofile; sync +PID=$! +while [ -d /proc/$PID ]; do + printf "." + sleep 5 +done +sync +rm /zerofile +sync fstrim / diff --git a/scripts/90-cleanup.sh b/scripts/90-cleanup.sh index ecb63a8d67..bdb2993f3b 100644 --- a/scripts/90-cleanup.sh +++ b/scripts/90-cleanup.sh @@ -10,44 +10,44 @@ set -o errexit # checking for security updates # https://github.com/digitalocean/marketplace-partners/issues/94 if [[ ! -d /tmp ]]; then - mkdir /tmp + mkdir /tmp fi chmod 1777 /tmp if [ -n "$(command -v yum)" ]; then - yum update -y - yum clean all + yum update -y + yum clean all elif [ -n "$(command -v apt-get)" ]; then - # Cleanup more packages - apt-get -y remove --purge \ - automake \ - autoconf \ - autotools-dev \ - cmake-data \ - cpp-9 \ - cpp-10 \ - gcc-9 \ - gcc-10 \ - git \ - git-man \ - ansible \ - libicu-dev \ - libcgal-dev \ - libgcc-9-dev \ - ansible + # Cleanup more packages + apt-get -y remove --purge \ + automake \ + autoconf \ + autotools-dev \ + cmake-data \ + cpp-9 \ + cpp-10 \ + gcc-9 \ + gcc-10 \ + git \ + git-man \ + ansible \ + libicu-dev \ + libcgal-dev \ + libgcc-9-dev \ + ansible - # add-apt-repository --yes --remove ppa:ansible/ansible + # add-apt-repository --yes --remove ppa:ansible/ansible - source /etc/os-release - - apt-get -y update - apt-get -y upgrade - apt-get -y autoremove - apt-get -y autoclean + source /etc/os-release + + apt-get -y update + apt-get -y upgrade + apt-get -y autoremove + apt-get -y autoclean fi rm -rf /tmp/* /var/tmp/* history -c -cat /dev/null > /root/.bash_history +cat /dev/null >/root/.bash_history unset HISTFILE find /var/log -mtime -1 -type f -exec truncate -s 0 {} \; rm -rf /var/log/*.gz /var/log/*.[0-9] /var/log/*-???????? @@ -67,11 +67,13 @@ The secure erase will complete successfully when you see:${NC} Beginning secure erase now\n" dd if=/dev/zero of=/zerofile & - PID=$! - while [ -d /proc/$PID ] - do - printf "." - sleep 5 - done -sync; rm /zerofile; sync -cat /dev/null > /var/log/lastlog; cat /dev/null > /var/log/wtmp +PID=$! +while [ -d /proc/$PID ]; do + printf "." + sleep 5 +done +sync +rm /zerofile +sync +cat /dev/null >/var/log/lastlog +cat /dev/null >/var/log/wtmp diff --git a/scripts/91-log_cleanup.sh b/scripts/91-log_cleanup.sh index 24073afcb8..648c46d354 100644 --- a/scripts/91-log_cleanup.sh +++ b/scripts/91-log_cleanup.sh @@ -4,7 +4,7 @@ echo "Clearing all log files" rm -rf /var/log/* -# creating system stats directory +# creating system stats directory mkdir /var/log/sysstat # https://github.com/fail2ban/fail2ban/issues/1593 @@ -21,4 +21,3 @@ cd /var/log/wal-g touch backup-push.log backup-fetch.log wal-push.log wal-fetch.log pitr.log chown -R postgres:postgres /var/log/wal-g chmod -R 0300 /var/log/wal-g - diff --git a/scripts/99-img_check.sh b/scripts/99-img_check.sh index ac958a5fc3..205ad07f81 100755 --- a/scripts/99-img_check.sh +++ b/scripts/99-img_check.sh @@ -5,12 +5,12 @@ # This code is licensed under Apache 2.0 license (see LICENSE.md for details) VERSION="v. 1.6" -RUNDATE=$( date ) +RUNDATE=$(date) # Script should be run with SUDO -if [ "$EUID" -ne 0 ] - then echo "[Error] - This script must be run with sudo or as the root user." - exit 1 +if [ "$EUID" -ne 0 ]; then + echo "[Error] - This script must be run with sudo or as the root user." + exit 1 fi STATUS=0 @@ -21,539 +21,527 @@ FAIL=0 # $1 == command to check for # returns: 0 == true, 1 == false cmdExists() { - if command -v "$1" > /dev/null 2>&1; then - return 0 - else - return 1 - fi + if command -v "$1" >/dev/null 2>&1; then + return 0 + else + return 1 + fi } function getDistro { - if [ -f /etc/os-release ]; then - # freedesktop.org and systemd - . /etc/os-release - OS=$NAME - VER=$VERSION_ID -elif type lsb_release >/dev/null 2>&1; then - # linuxbase.org - OS=$(lsb_release -si) - VER=$(lsb_release -sr) -elif [ -f /etc/lsb-release ]; then - # For some versions of Debian/Ubuntu without lsb_release command - . /etc/lsb-release - OS=$DISTRIB_ID - VER=$DISTRIB_RELEASE -elif [ -f /etc/debian_version ]; then - # Older Debian/Ubuntu/etc. - OS=Debian - VER=$(cat /etc/debian_version) -elif [ -f /etc/SuSe-release ]; then - # Older SuSE/etc. - : -elif [ -f /etc/redhat-release ]; then - # Older Red Hat, CentOS, etc. - VER=$( cat /etc/redhat-release | cut -d" " -f3 | cut -d "." -f1) - d=$( cat /etc/redhat-release | cut -d" " -f1 | cut -d "." -f1) - if [[ $d == "CentOS" ]]; then - OS="CentOS Linux" - fi -else - # Fall back to uname, e.g. "Linux ", also works for BSD, etc. - OS=$(uname -s) - VER=$(uname -r) -fi + if [ -f /etc/os-release ]; then + # freedesktop.org and systemd + . /etc/os-release + OS=$NAME + VER=$VERSION_ID + elif type lsb_release >/dev/null 2>&1; then + # linuxbase.org + OS=$(lsb_release -si) + VER=$(lsb_release -sr) + elif [ -f /etc/lsb-release ]; then + # For some versions of Debian/Ubuntu without lsb_release command + . /etc/lsb-release + OS=$DISTRIB_ID + VER=$DISTRIB_RELEASE + elif [ -f /etc/debian_version ]; then + # Older Debian/Ubuntu/etc. + OS=Debian + VER=$(cat /etc/debian_version) + elif [ -f /etc/SuSe-release ]; then + # Older SuSE/etc. + : + elif [ -f /etc/redhat-release ]; then + # Older Red Hat, CentOS, etc. + VER=$(cat /etc/redhat-release | cut -d" " -f3 | cut -d "." -f1) + d=$(cat /etc/redhat-release | cut -d" " -f1 | cut -d "." -f1) + if [[ $d == "CentOS" ]]; then + OS="CentOS Linux" + fi + else + # Fall back to uname, e.g. "Linux ", also works for BSD, etc. + OS=$(uname -s) + VER=$(uname -r) + fi } function loadPasswords { -SHADOW=$(cat /etc/shadow) + SHADOW=$(cat /etc/shadow) } function checkAgent { - # Check for the presence of the do-agent in the filesystem - if [ -d /var/opt/digitalocean/do-agent ];then - echo -en "\e[41m[FAIL]\e[0m DigitalOcean Monitoring Agent detected.\n" - ((FAIL++)) - STATUS=2 - if [[ $OS == "CentOS Linux" ]]; then - echo "The agent can be removed with 'sudo yum remove do-agent' " - elif [[ $OS == "Ubuntu" ]]; then - echo "The agent can be removed with 'sudo apt-get purge do-agent' " - fi - else - echo -en "\e[32m[PASS]\e[0m DigitalOcean Monitoring agent was not found\n" - ((PASS++)) - fi + # Check for the presence of the do-agent in the filesystem + if [ -d /var/opt/digitalocean/do-agent ]; then + echo -en "\e[41m[FAIL]\e[0m DigitalOcean Monitoring Agent detected.\n" + ((FAIL++)) + STATUS=2 + if [[ $OS == "CentOS Linux" ]]; then + echo "The agent can be removed with 'sudo yum remove do-agent' " + elif [[ $OS == "Ubuntu" ]]; then + echo "The agent can be removed with 'sudo apt-get purge do-agent' " + fi + else + echo -en "\e[32m[PASS]\e[0m DigitalOcean Monitoring agent was not found\n" + ((PASS++)) + fi } function checkLogs { - cp_ignore="/var/log/cpanel-install.log" - echo -en "\nChecking for log files in /var/log\n\n" - # Check if there are log archives or log files that have not been recently cleared. - for f in /var/log/*-????????; do - [[ -e $f ]] || break - if [ $f != $cp_ignore ]; then - echo -en "\e[93m[WARN]\e[0m Log archive ${f} found\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - done - for f in /var/log/*.[0-9];do - [[ -e $f ]] || break - echo -en "\e[93m[WARN]\e[0m Log archive ${f} found\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - done - for f in /var/log/*.log; do - [[ -e $f ]] || break - if [[ "${f}" = '/var/log/lfd.log' && "$( cat "${f}" | egrep -v '/var/log/messages has been reset| Watching /var/log/messages' | wc -c)" -gt 50 ]]; then - if [ $f != $cp_ignore ]; then - echo -en "\e[93m[WARN]\e[0m un-cleared log file, ${f} found\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - elif [[ "${f}" != '/var/log/lfd.log' && "$( cat "${f}" | wc -c)" -gt 50 ]]; then - if [ $f != $cp_ignore ]; then - echo -en "\e[93m[WARN]\e[0m un-cleared log file, ${f} found\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - fi - done + cp_ignore="/var/log/cpanel-install.log" + echo -en "\nChecking for log files in /var/log\n\n" + # Check if there are log archives or log files that have not been recently cleared. + for f in /var/log/*-????????; do + [[ -e $f ]] || break + if [ $f != $cp_ignore ]; then + echo -en "\e[93m[WARN]\e[0m Log archive ${f} found\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then + STATUS=1 + fi + fi + done + for f in /var/log/*.[0-9]; do + [[ -e $f ]] || break + echo -en "\e[93m[WARN]\e[0m Log archive ${f} found\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then + STATUS=1 + fi + done + for f in /var/log/*.log; do + [[ -e $f ]] || break + if [[ ${f} == '/var/log/lfd.log' && "$(cat "${f}" | egrep -v '/var/log/messages has been reset| Watching /var/log/messages' | wc -c)" -gt 50 ]]; then + if [ $f != $cp_ignore ]; then + echo -en "\e[93m[WARN]\e[0m un-cleared log file, ${f} found\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then + STATUS=1 + fi + fi + elif [[ ${f} != '/var/log/lfd.log' && "$(cat "${f}" | wc -c)" -gt 50 ]]; then + if [ $f != $cp_ignore ]; then + echo -en "\e[93m[WARN]\e[0m un-cleared log file, ${f} found\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then + STATUS=1 + fi + fi + fi + done } function checkTMP { - # Check the /tmp directory to ensure it is empty. Warn on any files found. - return 1 + # Check the /tmp directory to ensure it is empty. Warn on any files found. + return 1 } function checkRoot { - user="root" - uhome="/root" - for usr in $SHADOW - do - IFS=':' read -r -a u <<< "$usr" - if [[ "${u[0]}" == "${user}" ]]; then - if [[ ${u[1]} == "!" ]] || [[ ${u[1]} == "!!" ]] || [[ ${u[1]} == "*" ]]; then - echo -en "\e[32m[PASS]\e[0m User ${user} has no password set.\n" - ((PASS++)) - else - echo -en "\e[41m[FAIL]\e[0m User ${user} has a password set on their account.\n" - ((FAIL++)) - STATUS=2 - fi - fi - done - if [ -d ${uhome}/ ]; then - if [ -d ${uhome}/.ssh/ ]; then - if ls ${uhome}/.ssh/*> /dev/null 2>&1; then - for key in ${uhome}/.ssh/* - do - if [ "${key}" == "${uhome}/.ssh/authorized_keys" ]; then - - if [ "$( cat "${key}" | wc -c)" -gt 50 ]; then - echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a populated authorized_keys file in \e[93m${key}\e[0m\n" - akey=$(cat ${key}) - echo "File Contents:" - echo $akey - echo "--------------" - ((FAIL++)) - STATUS=2 - fi - elif [ "${key}" == "${uhome}/.ssh/id_rsa" ]; then - if [ "$( cat "${key}" | wc -c)" -gt 0 ]; then - echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a private key file in \e[93m${key}\e[0m\n" - akey=$(cat ${key}) - echo "File Contents:" - echo $akey - echo "--------------" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has empty private key file in \e[93m${key}\e[0m\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - elif [ "${key}" != "${uhome}/.ssh/known_hosts" ]; then - echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a file in their .ssh directory at \e[93m${key}\e[0m\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - else - if [ "$( cat "${key}" | wc -c)" -gt 50 ]; then - echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a populated known_hosts file in \e[93m${key}\e[0m\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - fi - done - else - echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m has no SSH keys present\n" - fi - else - echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have an .ssh directory\n" - fi - if [ -f /root/.bash_history ];then - - BH_S=$( cat /root/.bash_history | wc -c) - - if [[ $BH_S -lt 200 ]]; then - echo -en "\e[32m[PASS]\e[0m ${user}'s Bash History appears to have been cleared\n" - ((PASS++)) - else - echo -en "\e[41m[FAIL]\e[0m ${user}'s Bash History should be cleared to prevent sensitive information from leaking\n" - ((FAIL++)) - STATUS=2 - fi - - return 1; - else - echo -en "\e[32m[PASS]\e[0m The Root User's Bash History is not present\n" - ((PASS++)) - fi - else - echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have a directory in /home\n" - fi - echo -en "\n\n" - return 1 + user="root" + uhome="/root" + for usr in $SHADOW; do + IFS=':' read -r -a u <<<"$usr" + if [[ ${u[0]} == "${user}" ]]; then + if [[ ${u[1]} == "!" ]] || [[ ${u[1]} == "!!" ]] || [[ ${u[1]} == "*" ]]; then + echo -en "\e[32m[PASS]\e[0m User ${user} has no password set.\n" + ((PASS++)) + else + echo -en "\e[41m[FAIL]\e[0m User ${user} has a password set on their account.\n" + ((FAIL++)) + STATUS=2 + fi + fi + done + if [ -d ${uhome}/ ]; then + if [ -d ${uhome}/.ssh/ ]; then + if ls ${uhome}/.ssh/* >/dev/null 2>&1; then + for key in ${uhome}/.ssh/*; do + if [ "${key}" == "${uhome}/.ssh/authorized_keys" ]; then + + if [ "$(cat "${key}" | wc -c)" -gt 50 ]; then + echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a populated authorized_keys file in \e[93m${key}\e[0m\n" + akey=$(cat ${key}) + echo "File Contents:" + echo $akey + echo "--------------" + ((FAIL++)) + STATUS=2 + fi + elif [ "${key}" == "${uhome}/.ssh/id_rsa" ]; then + if [ "$(cat "${key}" | wc -c)" -gt 0 ]; then + echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a private key file in \e[93m${key}\e[0m\n" + akey=$(cat ${key}) + echo "File Contents:" + echo $akey + echo "--------------" + ((FAIL++)) + STATUS=2 + else + echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has empty private key file in \e[93m${key}\e[0m\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then + STATUS=1 + fi + fi + elif [ "${key}" != "${uhome}/.ssh/known_hosts" ]; then + echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a file in their .ssh directory at \e[93m${key}\e[0m\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then + STATUS=1 + fi + else + if [ "$(cat "${key}" | wc -c)" -gt 50 ]; then + echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a populated known_hosts file in \e[93m${key}\e[0m\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then + STATUS=1 + fi + fi + fi + done + else + echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m has no SSH keys present\n" + fi + else + echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have an .ssh directory\n" + fi + if [ -f /root/.bash_history ]; then + + BH_S=$(cat /root/.bash_history | wc -c) + + if [[ $BH_S -lt 200 ]]; then + echo -en "\e[32m[PASS]\e[0m ${user}'s Bash History appears to have been cleared\n" + ((PASS++)) + else + echo -en "\e[41m[FAIL]\e[0m ${user}'s Bash History should be cleared to prevent sensitive information from leaking\n" + ((FAIL++)) + STATUS=2 + fi + + return 1 + else + echo -en "\e[32m[PASS]\e[0m The Root User's Bash History is not present\n" + ((PASS++)) + fi + else + echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have a directory in /home\n" + fi + echo -en "\n\n" + return 1 } function checkUsers { - # Check each user-created account - for user in $(awk -F: '$3 >= 1000 && $1 != "nobody" {print $1}' /etc/passwd;) - do - # Skip some other non-user system accounts - if [[ $user == "centos" ]]; then - : - elif [[ $user == "nfsnobody" ]]; then - : - else - echo -en "\nChecking user: ${user}...\n" - for usr in $SHADOW - do - IFS=':' read -r -a u <<< "$usr" - if [[ "${u[0]}" == "${user}" ]]; then - if [[ ${u[1]} == "!" ]] || [[ ${u[1]} == "!!" ]] || [[ ${u[1]} == "*" ]]; then - echo -en "\e[32m[PASS]\e[0m User ${user} has no password set.\n" - ((PASS++)) - else - echo -en "\e[41m[FAIL]\e[0m User ${user} has a password set on their account. Only system users are allowed on the image.\n" - ((FAIL++)) - STATUS=2 - fi - fi - done - #echo "User Found: ${user}" - uhome="/home/${user}" - if [ -d "${uhome}/" ]; then - if [ -d "${uhome}/.ssh/" ]; then - if ls "${uhome}/.ssh/*"> /dev/null 2>&1; then - for key in ${uhome}/.ssh/* - do - if [ "${key}" == "${uhome}/.ssh/authorized_keys" ]; then - if [ "$( cat "${key}" | wc -c)" -gt 50 ]; then - echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a populated authorized_keys file in \e[93m${key}\e[0m\n" - akey=$(cat ${key}) - echo "File Contents:" - echo $akey - echo "--------------" - ((FAIL++)) - STATUS=2 - fi - elif [ "${key}" == "${uhome}/.ssh/id_rsa" ]; then - if [ "$( cat "${key}" | wc -c)" -gt 0 ]; then - echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a private key file in \e[93m${key}\e[0m\n" - akey=$(cat ${key}) - echo "File Contents:" - echo $akey - echo "--------------" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has empty private key file in \e[93m${key}\e[0m\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - elif [ "${key}" != "${uhome}/.ssh/known_hosts" ]; then - - echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a file in their .ssh directory named \e[93m${key}\e[0m\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - - else - if [ "$( cat "${key}" | wc -c)" -gt 50 ]; then - echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a known_hosts file in \e[93m${key}\e[0m\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - fi - - - done - else - echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m has no SSH keys present\n" - fi - else - echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have an .ssh directory\n" - fi - else - echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have a directory in /home\n" - fi - - # Check for an uncleared .bash_history for this user - if [ -f "${uhome}/.bash_history" ]; then - BH_S=$( cat "${uhome}/.bash_history" | wc -c ) - - if [[ $BH_S -lt 200 ]]; then - echo -en "\e[32m[PASS]\e[0m ${user}'s Bash History appears to have been cleared\n" - ((PASS++)) - else - echo -en "\e[41m[FAIL]\e[0m ${user}'s Bash History should be cleared to prevent sensitive information from leaking\n" - ((FAIL++)) - STATUS=2 - - fi - echo -en "\n\n" - fi - fi - done + # Check each user-created account + for user in $(awk -F: '$3 >= 1000 && $1 != "nobody" {print $1}' /etc/passwd); do + # Skip some other non-user system accounts + if [[ $user == "centos" ]]; then + : + elif [[ $user == "nfsnobody" ]]; then + : + else + echo -en "\nChecking user: ${user}...\n" + for usr in $SHADOW; do + IFS=':' read -r -a u <<<"$usr" + if [[ ${u[0]} == "${user}" ]]; then + if [[ ${u[1]} == "!" ]] || [[ ${u[1]} == "!!" ]] || [[ ${u[1]} == "*" ]]; then + echo -en "\e[32m[PASS]\e[0m User ${user} has no password set.\n" + ((PASS++)) + else + echo -en "\e[41m[FAIL]\e[0m User ${user} has a password set on their account. Only system users are allowed on the image.\n" + ((FAIL++)) + STATUS=2 + fi + fi + done + #echo "User Found: ${user}" + uhome="/home/${user}" + if [ -d "${uhome}/" ]; then + if [ -d "${uhome}/.ssh/" ]; then + if ls "${uhome}/.ssh/*" >/dev/null 2>&1; then + for key in ${uhome}/.ssh/*; do + if [ "${key}" == "${uhome}/.ssh/authorized_keys" ]; then + if [ "$(cat "${key}" | wc -c)" -gt 50 ]; then + echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a populated authorized_keys file in \e[93m${key}\e[0m\n" + akey=$(cat ${key}) + echo "File Contents:" + echo $akey + echo "--------------" + ((FAIL++)) + STATUS=2 + fi + elif [ "${key}" == "${uhome}/.ssh/id_rsa" ]; then + if [ "$(cat "${key}" | wc -c)" -gt 0 ]; then + echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a private key file in \e[93m${key}\e[0m\n" + akey=$(cat ${key}) + echo "File Contents:" + echo $akey + echo "--------------" + ((FAIL++)) + STATUS=2 + else + echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has empty private key file in \e[93m${key}\e[0m\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then + STATUS=1 + fi + fi + elif [ "${key}" != "${uhome}/.ssh/known_hosts" ]; then + + echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a file in their .ssh directory named \e[93m${key}\e[0m\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then + STATUS=1 + fi + + else + if [ "$(cat "${key}" | wc -c)" -gt 50 ]; then + echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a known_hosts file in \e[93m${key}\e[0m\n" + ((WARN++)) + if [[ $STATUS != 2 ]]; then + STATUS=1 + fi + fi + fi + + done + else + echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m has no SSH keys present\n" + fi + else + echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have an .ssh directory\n" + fi + else + echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have a directory in /home\n" + fi + + # Check for an uncleared .bash_history for this user + if [ -f "${uhome}/.bash_history" ]; then + BH_S=$(cat "${uhome}/.bash_history" | wc -c) + + if [[ $BH_S -lt 200 ]]; then + echo -en "\e[32m[PASS]\e[0m ${user}'s Bash History appears to have been cleared\n" + ((PASS++)) + else + echo -en "\e[41m[FAIL]\e[0m ${user}'s Bash History should be cleared to prevent sensitive information from leaking\n" + ((FAIL++)) + STATUS=2 + + fi + echo -en "\n\n" + fi + fi + done } function checkFirewall { - if [[ $OS == "Ubuntu" ]]; then - fw="ufw" - ufwa=$(ufw status |head -1| sed -e "s/^Status:\ //") - if [[ $ufwa == "active" ]]; then - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - elif [[ $OS == "CentOS Linux" ]]; then - if [ -f /usr/lib/systemd/system/csf.service ]; then - fw="csf" - if [[ $(systemctl status $fw >/dev/null 2>&1) ]]; then - - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - elif cmdExists "firewall-cmd"; then - if [[ $(systemctl is-active firewalld >/dev/null 2>&1 && echo 1 || echo 0) ]]; then - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - else - fw="firewalld" - if [[ $(systemctl is-active firewalld >/dev/null 2>&1 && echo 1 || echo 0) ]]; then - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - fi - elif [[ "$OS" =~ Debian.* ]]; then - # user could be using a number of different services for managing their firewall - # we will check some of the most common - if cmdExists 'ufw'; then - fw="ufw" - ufwa=$(ufw status |head -1| sed -e "s/^Status:\ //") - if [[ $ufwa == "active" ]]; then - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - elif cmdExists "firewall-cmd"; then - fw="firewalld" - if [[ $(systemctl is-active --quiet $fw) ]]; then - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - else - # user could be using vanilla iptables, check if kernel module is loaded - fw="iptables" - if [[ $(lsmod | grep -q '^ip_tables' 2>/dev/null) ]]; then - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - fi - fi + if [[ $OS == "Ubuntu" ]]; then + fw="ufw" + ufwa=$(ufw status | head -1 | sed -e "s/^Status:\ //") + if [[ $ufwa == "active" ]]; then + FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" + ((PASS++)) + else + FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" + ((WARN++)) + fi + elif [[ $OS == "CentOS Linux" ]]; then + if [ -f /usr/lib/systemd/system/csf.service ]; then + fw="csf" + if [[ $(systemctl status $fw >/dev/null 2>&1) ]]; then + + FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" + ((PASS++)) + elif cmdExists "firewall-cmd"; then + if [[ $(systemctl is-active firewalld >/dev/null 2>&1 && echo 1 || echo 0) ]]; then + FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" + ((PASS++)) + else + FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" + ((WARN++)) + fi + else + FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" + ((WARN++)) + fi + else + fw="firewalld" + if [[ $(systemctl is-active firewalld >/dev/null 2>&1 && echo 1 || echo 0) ]]; then + FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" + ((PASS++)) + else + FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" + ((WARN++)) + fi + fi + elif [[ $OS =~ Debian.* ]]; then + # user could be using a number of different services for managing their firewall + # we will check some of the most common + if cmdExists 'ufw'; then + fw="ufw" + ufwa=$(ufw status | head -1 | sed -e "s/^Status:\ //") + if [[ $ufwa == "active" ]]; then + FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" + ((PASS++)) + else + FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" + ((WARN++)) + fi + elif cmdExists "firewall-cmd"; then + fw="firewalld" + if [[ $(systemctl is-active --quiet $fw) ]]; then + FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" + ((PASS++)) + else + FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" + ((WARN++)) + fi + else + # user could be using vanilla iptables, check if kernel module is loaded + fw="iptables" + if [[ $(lsmod | grep -q '^ip_tables' 2>/dev/null) ]]; then + FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" + ((PASS++)) + else + FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" + ((WARN++)) + fi + fi + fi } function checkUpdates { - if [[ $OS == "Ubuntu" ]] || [[ "$OS" =~ Debian.* ]]; then - # Ensure /tmp exists and has the proper permissions before - # checking for security updates - # https://github.com/digitalocean/marketplace-partners/issues/94 - if [[ ! -d /tmp ]]; then - mkdir /tmp - fi - chmod 1777 /tmp - - echo -en "\nUpdating apt package database to check for security updates, this may take a minute...\n\n" - apt-get -y update > /dev/null - - uc=$(apt-get --just-print upgrade | grep -i "security" | wc -l) - if [[ $uc -gt 0 ]]; then - update_count=$(( ${uc} / 2 )) - else - update_count=0 - fi - - if [[ $update_count -gt 0 ]]; then - echo -en "\e[41m[FAIL]\e[0m There are ${update_count} security updates available for this image that have not been installed.\n" - echo -en - echo -en "Here is a list of the security updates that are not installed:\n" - sleep 2 - apt-get --just-print upgrade | grep -i security | awk '{print $2}' | awk '!seen[$0]++' - echo -en - ((FAIL++)) - STATUS=2 - else - echo -en "\e[32m[PASS]\e[0m There are no pending security updates for this image.\n\n" - fi - elif [[ $OS == "CentOS Linux" ]]; then - echo -en "\nChecking for available security updates, this may take a minute...\n\n" - - update_count=$(yum check-update --security --quiet | wc -l) - if [[ $update_count -gt 0 ]]; then - echo -en "\e[41m[FAIL]\e[0m There are ${update_count} security updates available for this image that have not been installed.\n" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[32m[PASS]\e[0m There are no pending security updates for this image.\n" - ((PASS++)) - fi - else - echo "Error encountered" - exit 1 - fi - - return 1; + if [[ $OS == "Ubuntu" ]] || [[ $OS =~ Debian.* ]]; then + # Ensure /tmp exists and has the proper permissions before + # checking for security updates + # https://github.com/digitalocean/marketplace-partners/issues/94 + if [[ ! -d /tmp ]]; then + mkdir /tmp + fi + chmod 1777 /tmp + + echo -en "\nUpdating apt package database to check for security updates, this may take a minute...\n\n" + apt-get -y update >/dev/null + + uc=$(apt-get --just-print upgrade | grep -i "security" | wc -l) + if [[ $uc -gt 0 ]]; then + update_count=$((uc / 2)) + else + update_count=0 + fi + + if [[ $update_count -gt 0 ]]; then + echo -en "\e[41m[FAIL]\e[0m There are ${update_count} security updates available for this image that have not been installed.\n" + echo -en + echo -en "Here is a list of the security updates that are not installed:\n" + sleep 2 + apt-get --just-print upgrade | grep -i security | awk '{print $2}' | awk '!seen[$0]++' + echo -en + ((FAIL++)) + STATUS=2 + else + echo -en "\e[32m[PASS]\e[0m There are no pending security updates for this image.\n\n" + fi + elif [[ $OS == "CentOS Linux" ]]; then + echo -en "\nChecking for available security updates, this may take a minute...\n\n" + + update_count=$(yum check-update --security --quiet | wc -l) + if [[ $update_count -gt 0 ]]; then + echo -en "\e[41m[FAIL]\e[0m There are ${update_count} security updates available for this image that have not been installed.\n" + ((FAIL++)) + STATUS=2 + else + echo -en "\e[32m[PASS]\e[0m There are no pending security updates for this image.\n" + ((PASS++)) + fi + else + echo "Error encountered" + exit 1 + fi + + return 1 } function checkCloudInit { - if hash cloud-init 2>/dev/null; then - CI="\e[32m[PASS]\e[0m Cloud-init is installed.\n" - ((PASS++)) - else - CI="\e[41m[FAIL]\e[0m No valid verison of cloud-init was found.\n" - ((FAIL++)) - STATUS=2 - fi - return 1 + if hash cloud-init 2>/dev/null; then + CI="\e[32m[PASS]\e[0m Cloud-init is installed.\n" + ((PASS++)) + else + CI="\e[41m[FAIL]\e[0m No valid verison of cloud-init was found.\n" + ((FAIL++)) + STATUS=2 + fi + return 1 } function checkMongoDB { - # Check if MongoDB is installed - # If it is, verify the version is allowed (non-SSPL) - - if [[ $OS == "Ubuntu" ]] || [[ "$OS" =~ Debian.* ]]; then - - if [[ -f "/usr/bin/mongod" ]]; then - version=$(/usr/bin/mongod --version --quiet | grep "db version" | sed -e "s/^db\ version\ v//") - - if version_gt $version 4.0.0; then - if version_gt $version 4.0.3; then - echo -en "\e[41m[FAIL]\e[0m An SSPL version of MongoDB is present, ${version}" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[32m[PASS]\e[0m The version of MongoDB installed, ${version} is not under the SSPL" - ((PASS++)) - fi - else - if version_gt $version 3.6.8; then - echo -en "\e[41m[FAIL]\e[0m An SSPL version of MongoDB is present, ${version}" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[32m[PASS]\e[0m The version of MongoDB installed, ${version} is not under the SSPL" - ((PASS++)) - fi - fi - - - else - echo -en "\e[32m[PASS]\e[0m MongoDB is not installed" - ((PASS++)) - fi - - elif [[ $OS == "CentOS Linux" ]]; then - - if [[ -f "/usr/bin/mongod" ]]; then - version=$(/usr/bin/mongod --version --quiet | grep "db version" | sed -e "s/^db\ version\ v//") - - - if version_gt $version 4.0.0; then - if version_gt $version 4.0.3; then - echo -en "\e[41m[FAIL]\e[0m An SSPL version of MongoDB is present" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[32m[PASS]\e[0m The version of MongoDB installed is not under the SSPL" - ((PASS++)) - fi - else - if version_gt $version 3.6.8; then - echo -en "\e[41m[FAIL]\e[0m An SSPL version of MongoDB is present" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[32m[PASS]\e[0m The version of MongoDB installed is not under the SSPL" - ((PASS++)) - fi - fi - - - - else - echo -en "\e[32m[PASS]\e[0m MongoDB is not installed" - ((PASS++)) - fi - - else - echo "ERROR: Unable to identify distribution" - ((FAIL++)) - STATUS 2 - return 1 - fi - + # Check if MongoDB is installed + # If it is, verify the version is allowed (non-SSPL) + + if [[ $OS == "Ubuntu" ]] || [[ $OS =~ Debian.* ]]; then + + if [[ -f "/usr/bin/mongod" ]]; then + version=$(/usr/bin/mongod --version --quiet | grep "db version" | sed -e "s/^db\ version\ v//") + + if version_gt $version 4.0.0; then + if version_gt $version 4.0.3; then + echo -en "\e[41m[FAIL]\e[0m An SSPL version of MongoDB is present, ${version}" + ((FAIL++)) + STATUS=2 + else + echo -en "\e[32m[PASS]\e[0m The version of MongoDB installed, ${version} is not under the SSPL" + ((PASS++)) + fi + else + if version_gt $version 3.6.8; then + echo -en "\e[41m[FAIL]\e[0m An SSPL version of MongoDB is present, ${version}" + ((FAIL++)) + STATUS=2 + else + echo -en "\e[32m[PASS]\e[0m The version of MongoDB installed, ${version} is not under the SSPL" + ((PASS++)) + fi + fi + + else + echo -en "\e[32m[PASS]\e[0m MongoDB is not installed" + ((PASS++)) + fi + + elif [[ $OS == "CentOS Linux" ]]; then + + if [[ -f "/usr/bin/mongod" ]]; then + version=$(/usr/bin/mongod --version --quiet | grep "db version" | sed -e "s/^db\ version\ v//") + + if version_gt $version 4.0.0; then + if version_gt $version 4.0.3; then + echo -en "\e[41m[FAIL]\e[0m An SSPL version of MongoDB is present" + ((FAIL++)) + STATUS=2 + else + echo -en "\e[32m[PASS]\e[0m The version of MongoDB installed is not under the SSPL" + ((PASS++)) + fi + else + if version_gt $version 3.6.8; then + echo -en "\e[41m[FAIL]\e[0m An SSPL version of MongoDB is present" + ((FAIL++)) + STATUS=2 + else + echo -en "\e[32m[PASS]\e[0m The version of MongoDB installed is not under the SSPL" + ((PASS++)) + fi + fi + + else + echo -en "\e[32m[PASS]\e[0m MongoDB is not installed" + ((PASS++)) + fi + + else + echo "ERROR: Unable to identify distribution" + ((FAIL++)) + STATUS 2 + return 1 + fi } function version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; } - clear echo "DigitalOcean Marketplace Image Validation Tool ${VERSION}" echo "Executed on: ${RUNDATE}" @@ -568,65 +556,65 @@ ost=0 osv=0 if [[ $OS == "Ubuntu" ]]; then - ost=1 - if [[ $VER == "24.04" ]]; then - osv=1 - elif [[ $VER == "18.04" ]]; then - osv=1 - elif [[ $VER == "16.04" ]]; then - osv=1 - else - osv=0 - fi - -elif [[ "$OS" =~ Debian.* ]]; then - ost=1 - case "$VER" in - 9) - osv=1 - ;; - 10) - osv=1 - ;; - *) - osv=2 - ;; - esac + ost=1 + if [[ $VER == "24.04" ]]; then + osv=1 + elif [[ $VER == "18.04" ]]; then + osv=1 + elif [[ $VER == "16.04" ]]; then + osv=1 + else + osv=0 + fi + +elif [[ $OS =~ Debian.* ]]; then + ost=1 + case "$VER" in + 9) + osv=1 + ;; + 10) + osv=1 + ;; + *) + osv=2 + ;; + esac elif [[ $OS == "CentOS Linux" ]]; then - ost=1 - if [[ $VER == "8" ]]; then - osv=1 - elif [[ $VER == "7" ]]; then - osv=1 - elif [[ $VER == "6" ]]; then - osv=1 - else - osv=2 - fi + ost=1 + if [[ $VER == "8" ]]; then + osv=1 + elif [[ $VER == "7" ]]; then + osv=1 + elif [[ $VER == "6" ]]; then + osv=1 + else + osv=2 + fi else - ost=0 + ost=0 fi if [[ $ost == 1 ]]; then - echo -en "\e[32m[PASS]\e[0m Supported Operating System Detected: ${OS}\n" - ((PASS++)) + echo -en "\e[32m[PASS]\e[0m Supported Operating System Detected: ${OS}\n" + ((PASS++)) else - echo -en "\e[41m[FAIL]\e[0m ${OS} is not a supported Operating System\n" - ((FAIL++)) - STATUS=2 + echo -en "\e[41m[FAIL]\e[0m ${OS} is not a supported Operating System\n" + ((FAIL++)) + STATUS=2 fi if [[ $osv == 1 ]]; then - echo -en "\e[32m[PASS]\e[0m Supported Release Detected: ${VER}\n" - ((PASS++)) + echo -en "\e[32m[PASS]\e[0m Supported Release Detected: ${VER}\n" + ((PASS++)) elif [[ $ost == 1 ]]; then - echo -en "\e[41m[FAIL]\e[0m ${OS} ${VER} is not a supported Operating System Version\n" - ((FAIL++)) - STATUS=2 + echo -en "\e[41m[FAIL]\e[0m ${OS} ${VER} is not a supported Operating System Version\n" + ((FAIL++)) + STATUS=2 else - echo "Exiting..." - exit 1 + echo "Exiting..." + exit 1 fi checkCloudInit @@ -653,16 +641,15 @@ checkAgent checkMongoDB - # Summary echo -en "\n\n---------------------------------------------------------------------------------------------------\n" if [[ $STATUS == 0 ]]; then - echo -en "Scan Complete.\n\e[32mAll Tests Passed!\e[0m\n" + echo -en "Scan Complete.\n\e[32mAll Tests Passed!\e[0m\n" elif [[ $STATUS == 1 ]]; then - echo -en "Scan Complete. \n\e[93mSome non-critical tests failed. Please review these items.\e[0m\e[0m\n" + echo -en "Scan Complete. \n\e[93mSome non-critical tests failed. Please review these items.\e[0m\e[0m\n" else - echo -en "Scan Complete. \n\e[41mOne or more tests failed. Please review these items and re-test.\e[0m\n" + echo -en "Scan Complete. \n\e[41mOne or more tests failed. Please review these items and re-test.\e[0m\n" fi echo "---------------------------------------------------------------------------------------------------" echo -en "\e[1m${PASS} Tests PASSED\e[0m\n" @@ -671,12 +658,12 @@ echo -en "\e[1m${FAIL} Tests FAILED\e[0m\n" echo -en "---------------------------------------------------------------------------------------------------\n" if [[ $STATUS == 0 ]]; then - echo -en "We did not detect any issues with this image. Please be sure to manually ensure that all software installed on the base system is functional, secure and properly configured (or facilities for configuration on first-boot have been created).\n\n" - exit 0 + echo -en "We did not detect any issues with this image. Please be sure to manually ensure that all software installed on the base system is functional, secure and properly configured (or facilities for configuration on first-boot have been created).\n\n" + exit 0 elif [[ $STATUS == 1 ]]; then - echo -en "Please review all [WARN] items above and ensure they are intended or resolved. If you do not have a specific requirement, we recommend resolving these items before image submission\n\n" - exit 0 + echo -en "Please review all [WARN] items above and ensure they are intended or resolved. If you do not have a specific requirement, we recommend resolving these items before image submission\n\n" + exit 0 else - echo -en "Some critical tests failed. These items must be resolved and this scan re-run before you submit your image to the DigitalOcean Marketplace.\n\n" - exit 1 -fi \ No newline at end of file + echo -en "Some critical tests failed. These items must be resolved and this scan re-run before you submit your image to the DigitalOcean Marketplace.\n\n" + exit 1 +fi diff --git a/scripts/nix-provision.sh b/scripts/nix-provision.sh index dfca0326aa..3fb2f5a530 100644 --- a/scripts/nix-provision.sh +++ b/scripts/nix-provision.sh @@ -6,65 +6,62 @@ set -o pipefail set -o xtrace function install_packages { - # Setup Ansible on host VM - sudo apt-get update && sudo apt-get install -y software-properties-common + # Setup Ansible on host VM + sudo apt-get update && sudo apt-get install -y software-properties-common - # Install EC2-specific packages that were deferred from stage 1 - # These packages have post-install scripts that need EC2 metadata service access - # which only works on a real running EC2 instance (not in chroot) - sudo apt-get install -y ec2-hibinit-agent ec2-instance-connect hibagent + # Install EC2-specific packages that were deferred from stage 1 + # These packages have post-install scripts that need EC2 metadata service access + # which only works on a real running EC2 instance (not in chroot) + sudo apt-get install -y ec2-hibinit-agent ec2-instance-connect hibagent - # Manually add GPG key with explicit keyserver - sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 93C4A3FD7BB9C367 + # Manually add GPG key with explicit keyserver + sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 93C4A3FD7BB9C367 - # Add repository and install - # TODO (darora): temporarily disabling while Launchpad is under ddos attack and very frequently timing out - # sudo add-apt-repository --yes ppa:ansible/ansible - # sudo apt-get update - sudo apt-get install -y ansible + # Add repository and install + # TODO (darora): temporarily disabling while Launchpad is under ddos attack and very frequently timing out + # sudo add-apt-repository --yes ppa:ansible/ansible + # sudo apt-get update + sudo apt-get install -y ansible - ansible-galaxy collection install community.general + ansible-galaxy collection install community.general } - - function install_nix() { - sudo su -c "sh <(curl -L https://releases.nixos.org/nix/nix-2.34.6/install) --yes --daemon --nix-extra-conf-file /dev/stdin < [INITIAL_PG_VERSION]" - exit 1 + echo "Usage: $0 [INITIAL_PG_VERSION]" + exit 1 fi INITIAL_PG_VERSION=${2:-15.1.1.60} @@ -20,11 +20,11 @@ LATEST_VERSION_SCRIPTS="scripts/pg_upgrade_scripts-${LATEST_PG_VERSION}.tar.gz" LATEST_VERSION_BIN="scripts/pg_upgrade_bin-${LATEST_PG_VERSION}.tar.gz" if [ ! -f "$LATEST_VERSION_SCRIPTS" ]; then - aws s3 cp "s3://${ARTIFACTS_BUCKET_NAME}/upgrades/postgres/supabase-postgres-${LATEST_PG_VERSION}/pg_upgrade_scripts.tar.gz" "$LATEST_VERSION_SCRIPTS" + aws s3 cp "s3://${ARTIFACTS_BUCKET_NAME}/upgrades/postgres/supabase-postgres-${LATEST_PG_VERSION}/pg_upgrade_scripts.tar.gz" "$LATEST_VERSION_SCRIPTS" fi if [ ! -f "$LATEST_VERSION_BIN" ]; then - aws s3 cp "s3://${ARTIFACTS_BUCKET_NAME}/upgrades/postgres/supabase-postgres-${LATEST_PG_VERSION}/24.04.tar.gz" "$LATEST_VERSION_BIN" + aws s3 cp "s3://${ARTIFACTS_BUCKET_NAME}/upgrades/postgres/supabase-postgres-${LATEST_PG_VERSION}/24.04.tar.gz" "$LATEST_VERSION_BIN" fi rm -rf scripts/pg_upgrade_scripts @@ -34,23 +34,23 @@ cp "$LATEST_VERSION_BIN" scripts/pg_upgrade_bin.tar.gz docker rm -f pg_upgrade_test || true docker run -t --name pg_upgrade_test --env-file .env \ - -v "$(pwd)/scripts:/tmp/upgrade" \ - --entrypoint /tmp/upgrade/entrypoint.sh -d \ - -p 5432:5432 \ - "supabase/postgres:${INITIAL_PG_VERSION}" + -v "$(pwd)/scripts:/tmp/upgrade" \ + --entrypoint /tmp/upgrade/entrypoint.sh -d \ + -p 5432:5432 \ + "supabase/postgres:${INITIAL_PG_VERSION}" sleep 3 while ! docker exec -it pg_upgrade_test bash -c "pg_isready"; do - echo "Waiting for postgres to start..." - sleep 1 + echo "Waiting for postgres to start..." + sleep 1 done echo "Running migrations" docker cp ../../migrations/db/migrations "pg_upgrade_test:/docker-entrypoint-initdb.d/" docker exec -it pg_upgrade_test bash -c '/docker-entrypoint-initdb.d/migrate.sh > /tmp/migrate.log 2>&1; exit $?' if [ $? -ne 0 ]; then - echo "Running migrations failed. Exiting." - exit 1 + echo "Running migrations failed. Exiting." + exit 1 fi echo "Running tests" @@ -62,19 +62,18 @@ psql -f "./tests/99-fixtures.sql" echo "Initiating pg_upgrade" docker exec -it pg_upgrade_test bash -c '/tmp/upgrade/pg_upgrade_scripts/initiate.sh "$PG_MAJOR_VERSION"; exit $?' if [ $? -ne 0 ]; then - echo "Initiating pg_upgrade failed. Exiting." - exit 1 + echo "Initiating pg_upgrade failed. Exiting." + exit 1 fi sleep 3 echo "Completing pg_upgrade" docker exec -it pg_upgrade_test bash -c 'rm -f /tmp/pg-upgrade-status; /tmp/upgrade/pg_upgrade_scripts/complete.sh; exit $?' if [ $? -ne 0 ]; then - echo "Completing pg_upgrade failed. Exiting." - exit 1 + echo "Completing pg_upgrade failed. Exiting." + exit 1 fi pg_prove tests/01-schema.sql pg_prove tests/02-data.sql pg_prove tests/03-settings.sql - diff --git a/tests/pg_upgrade/scripts/entrypoint.sh b/tests/pg_upgrade/scripts/entrypoint.sh index d9d80acd9e..7f4e784b72 100755 --- a/tests/pg_upgrade/scripts/entrypoint.sh +++ b/tests/pg_upgrade/scripts/entrypoint.sh @@ -1,8 +1,8 @@ #!/bin/bash -set -e +set -e -SCRIPT_DIR=$(dirname -- "$0";) +SCRIPT_DIR=$(dirname -- "$0") ls -la "$SCRIPT_DIR" @@ -21,5 +21,5 @@ su postgres -c "$(pg_config --bindir)/pg_ctl start -o '-c config_file=/etc/postg RECEIVED_EXIT_SIGNAL=false trap 'RECEIVED_EXIT_SIGNAL=true' SIGINT SIGTERM SIGUSR1 while ! ((RECEIVED_EXIT_SIGNAL)); do - sleep 5 + sleep 5 done From 2b008045d45bc5f573bdad42935ea9678dc895c9 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Tue, 2 Jun 2026 14:17:46 -0400 Subject: [PATCH 3/7] scripts: Delete unused files These haven't been touched in many years and are no longer used so drop them. --- scripts/00-python_install.sh | 3 - scripts/01-postgres_check.sh | 72 ---- scripts/02-credentials_cleanup.sh | 1 - scripts/11-lemp.sh | 14 - scripts/12-ufw-nginx.sh | 10 - scripts/13-force-ssh-logout.sh | 10 - scripts/91-log_cleanup.sh | 23 - scripts/99-img_check.sh | 669 ------------------------------ 8 files changed, 802 deletions(-) delete mode 100644 scripts/00-python_install.sh delete mode 100644 scripts/01-postgres_check.sh delete mode 100644 scripts/02-credentials_cleanup.sh delete mode 100644 scripts/11-lemp.sh delete mode 100644 scripts/12-ufw-nginx.sh delete mode 100644 scripts/13-force-ssh-logout.sh delete mode 100644 scripts/91-log_cleanup.sh delete mode 100755 scripts/99-img_check.sh diff --git a/scripts/00-python_install.sh b/scripts/00-python_install.sh deleted file mode 100644 index aa1e5c9778..0000000000 --- a/scripts/00-python_install.sh +++ /dev/null @@ -1,3 +0,0 @@ -sudo apt-get update -sudo apt-get install python -y -sudo apt-get install python-pip -y diff --git a/scripts/01-postgres_check.sh b/scripts/01-postgres_check.sh deleted file mode 100644 index 2d9298b05e..0000000000 --- a/scripts/01-postgres_check.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash -# -# Scripts in this directory are run during the build process. -# each script will be uploaded to /tmp on your build droplet, -# given execute permissions and run. The cleanup process will -# remove the scripts from your build system after they have run -# if you use the build_image task. -# -echo "Commencing Checks" - -function check_database_is_ready { - echo -e "\nChecking if database is ready and accepting connections:" - if [ "$(pg_isready)" = "/tmp:5432 - accepting connections" ]; then - echo "Database is ready" - else - echo "Error: Database is not ready. Exiting" - exit 1 - fi -} - -function check_postgres_owned_dir_exists { - DIR=$1 - USER="postgres" - - echo -e "\nChecking if $DIR exists and owned by postgres user:" - - if [ -d "$DIR" ]; then - echo "$DIR exists" - if [ $(stat -c '%U' $DIR) = "$USER" ]; then - echo "$DIR is owned by $USER" - else - echo "Error: $DIR is not owned by $USER" - exit 1 - fi - else - echo "Error: ${DIR} not found. Exiting." - exit 1 - fi -} - -function check_lse_enabled { - ARCH=$(uname -m) - if [ $ARCH = "aarch64" ]; then - echo -e "\nArchitecture is $ARCH. Checking for LSE:" - - LSE_COUNT=$(objdump -d /usr/lib/postgresql/bin/postgres | grep -i 'ldxr\|ldaxr\|stxr\|stlxr' | wc -l) - MOUTLINE_ATOMICS_COUNT=$(nm /usr/lib/postgresql/bin/postgres | grep __aarch64_have_lse_atomics | wc -l) - - # Checking for load and store exclusives - if [ $LSE_COUNT -gt 0 ]; then - echo "Postgres has LSE enabled" - else - echo "Error: Postgres failed to be compiled with LSE. Exiting" - exit 1 - fi - - # Checking if successfully compiled with -moutline-atomics - if [ $MOUTLINE_ATOMICS_COUNT -gt 0 ]; then - echo "Postgres has been compiled with -moutline-atomics" - else - echo "Error: Postgres failed to be compiled with -moutline-atomics. Exiting" - exit 1 - fi - else - echo "Architecture is $ARCH. Not checking for LSE." - fi -} - -check_database_is_ready -check_postgres_owned_dir_exists "/var/lib/postgresql" -check_postgres_owned_dir_exists "/etc/postgresql" -check_lse_enabled diff --git a/scripts/02-credentials_cleanup.sh b/scripts/02-credentials_cleanup.sh deleted file mode 100644 index a7b966f037..0000000000 --- a/scripts/02-credentials_cleanup.sh +++ /dev/null @@ -1 +0,0 @@ -sudo rm /home/ubuntu/.ssh/authorized_keys diff --git a/scripts/11-lemp.sh b/scripts/11-lemp.sh deleted file mode 100644 index 726a57a1b3..0000000000 --- a/scripts/11-lemp.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# DigitalOcean Marketplace Image Validation Tool -# © 2021 DigitalOcean LLC. -# This code is licensed under Apache 2.0 license (see LICENSE.md for details) - -rm -rvf /etc/nginx/sites-enabled/default - -ln -s /etc/nginx/sites-available/digitalocean \ - /etc/nginx/sites-enabled/digitalocean - -rm -rf /var/www/html/index*debian.html - -chown -R www-data: /var/www diff --git a/scripts/12-ufw-nginx.sh b/scripts/12-ufw-nginx.sh deleted file mode 100644 index f5c2e3a45f..0000000000 --- a/scripts/12-ufw-nginx.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -# DigitalOcean Marketplace Image Validation Tool -# © 2021 DigitalOcean LLC. -# This code is licensed under Apache 2.0 license (see LICENSE.md for details) - -ufw limit ssh -ufw allow 'Nginx Full' - -ufw --force enable diff --git a/scripts/13-force-ssh-logout.sh b/scripts/13-force-ssh-logout.sh deleted file mode 100644 index ddbcd1cbf0..0000000000 --- a/scripts/13-force-ssh-logout.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -# DigitalOcean Marketplace Image Validation Tool -# © 2021 DigitalOcean LLC. -# This code is licensed under Apache 2.0 license (see LICENSE.md for details) - -cat >>/etc/ssh/sshd_config </dev/null 2>&1; then - return 0 - else - return 1 - fi -} - -function getDistro { - if [ -f /etc/os-release ]; then - # freedesktop.org and systemd - . /etc/os-release - OS=$NAME - VER=$VERSION_ID - elif type lsb_release >/dev/null 2>&1; then - # linuxbase.org - OS=$(lsb_release -si) - VER=$(lsb_release -sr) - elif [ -f /etc/lsb-release ]; then - # For some versions of Debian/Ubuntu without lsb_release command - . /etc/lsb-release - OS=$DISTRIB_ID - VER=$DISTRIB_RELEASE - elif [ -f /etc/debian_version ]; then - # Older Debian/Ubuntu/etc. - OS=Debian - VER=$(cat /etc/debian_version) - elif [ -f /etc/SuSe-release ]; then - # Older SuSE/etc. - : - elif [ -f /etc/redhat-release ]; then - # Older Red Hat, CentOS, etc. - VER=$(cat /etc/redhat-release | cut -d" " -f3 | cut -d "." -f1) - d=$(cat /etc/redhat-release | cut -d" " -f1 | cut -d "." -f1) - if [[ $d == "CentOS" ]]; then - OS="CentOS Linux" - fi - else - # Fall back to uname, e.g. "Linux ", also works for BSD, etc. - OS=$(uname -s) - VER=$(uname -r) - fi -} -function loadPasswords { - SHADOW=$(cat /etc/shadow) -} - -function checkAgent { - # Check for the presence of the do-agent in the filesystem - if [ -d /var/opt/digitalocean/do-agent ]; then - echo -en "\e[41m[FAIL]\e[0m DigitalOcean Monitoring Agent detected.\n" - ((FAIL++)) - STATUS=2 - if [[ $OS == "CentOS Linux" ]]; then - echo "The agent can be removed with 'sudo yum remove do-agent' " - elif [[ $OS == "Ubuntu" ]]; then - echo "The agent can be removed with 'sudo apt-get purge do-agent' " - fi - else - echo -en "\e[32m[PASS]\e[0m DigitalOcean Monitoring agent was not found\n" - ((PASS++)) - fi -} - -function checkLogs { - cp_ignore="/var/log/cpanel-install.log" - echo -en "\nChecking for log files in /var/log\n\n" - # Check if there are log archives or log files that have not been recently cleared. - for f in /var/log/*-????????; do - [[ -e $f ]] || break - if [ $f != $cp_ignore ]; then - echo -en "\e[93m[WARN]\e[0m Log archive ${f} found\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - done - for f in /var/log/*.[0-9]; do - [[ -e $f ]] || break - echo -en "\e[93m[WARN]\e[0m Log archive ${f} found\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - done - for f in /var/log/*.log; do - [[ -e $f ]] || break - if [[ ${f} == '/var/log/lfd.log' && "$(cat "${f}" | egrep -v '/var/log/messages has been reset| Watching /var/log/messages' | wc -c)" -gt 50 ]]; then - if [ $f != $cp_ignore ]; then - echo -en "\e[93m[WARN]\e[0m un-cleared log file, ${f} found\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - elif [[ ${f} != '/var/log/lfd.log' && "$(cat "${f}" | wc -c)" -gt 50 ]]; then - if [ $f != $cp_ignore ]; then - echo -en "\e[93m[WARN]\e[0m un-cleared log file, ${f} found\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - fi - done -} -function checkTMP { - # Check the /tmp directory to ensure it is empty. Warn on any files found. - return 1 -} -function checkRoot { - user="root" - uhome="/root" - for usr in $SHADOW; do - IFS=':' read -r -a u <<<"$usr" - if [[ ${u[0]} == "${user}" ]]; then - if [[ ${u[1]} == "!" ]] || [[ ${u[1]} == "!!" ]] || [[ ${u[1]} == "*" ]]; then - echo -en "\e[32m[PASS]\e[0m User ${user} has no password set.\n" - ((PASS++)) - else - echo -en "\e[41m[FAIL]\e[0m User ${user} has a password set on their account.\n" - ((FAIL++)) - STATUS=2 - fi - fi - done - if [ -d ${uhome}/ ]; then - if [ -d ${uhome}/.ssh/ ]; then - if ls ${uhome}/.ssh/* >/dev/null 2>&1; then - for key in ${uhome}/.ssh/*; do - if [ "${key}" == "${uhome}/.ssh/authorized_keys" ]; then - - if [ "$(cat "${key}" | wc -c)" -gt 50 ]; then - echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a populated authorized_keys file in \e[93m${key}\e[0m\n" - akey=$(cat ${key}) - echo "File Contents:" - echo $akey - echo "--------------" - ((FAIL++)) - STATUS=2 - fi - elif [ "${key}" == "${uhome}/.ssh/id_rsa" ]; then - if [ "$(cat "${key}" | wc -c)" -gt 0 ]; then - echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a private key file in \e[93m${key}\e[0m\n" - akey=$(cat ${key}) - echo "File Contents:" - echo $akey - echo "--------------" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has empty private key file in \e[93m${key}\e[0m\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - elif [ "${key}" != "${uhome}/.ssh/known_hosts" ]; then - echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a file in their .ssh directory at \e[93m${key}\e[0m\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - else - if [ "$(cat "${key}" | wc -c)" -gt 50 ]; then - echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a populated known_hosts file in \e[93m${key}\e[0m\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - fi - done - else - echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m has no SSH keys present\n" - fi - else - echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have an .ssh directory\n" - fi - if [ -f /root/.bash_history ]; then - - BH_S=$(cat /root/.bash_history | wc -c) - - if [[ $BH_S -lt 200 ]]; then - echo -en "\e[32m[PASS]\e[0m ${user}'s Bash History appears to have been cleared\n" - ((PASS++)) - else - echo -en "\e[41m[FAIL]\e[0m ${user}'s Bash History should be cleared to prevent sensitive information from leaking\n" - ((FAIL++)) - STATUS=2 - fi - - return 1 - else - echo -en "\e[32m[PASS]\e[0m The Root User's Bash History is not present\n" - ((PASS++)) - fi - else - echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have a directory in /home\n" - fi - echo -en "\n\n" - return 1 -} - -function checkUsers { - # Check each user-created account - for user in $(awk -F: '$3 >= 1000 && $1 != "nobody" {print $1}' /etc/passwd); do - # Skip some other non-user system accounts - if [[ $user == "centos" ]]; then - : - elif [[ $user == "nfsnobody" ]]; then - : - else - echo -en "\nChecking user: ${user}...\n" - for usr in $SHADOW; do - IFS=':' read -r -a u <<<"$usr" - if [[ ${u[0]} == "${user}" ]]; then - if [[ ${u[1]} == "!" ]] || [[ ${u[1]} == "!!" ]] || [[ ${u[1]} == "*" ]]; then - echo -en "\e[32m[PASS]\e[0m User ${user} has no password set.\n" - ((PASS++)) - else - echo -en "\e[41m[FAIL]\e[0m User ${user} has a password set on their account. Only system users are allowed on the image.\n" - ((FAIL++)) - STATUS=2 - fi - fi - done - #echo "User Found: ${user}" - uhome="/home/${user}" - if [ -d "${uhome}/" ]; then - if [ -d "${uhome}/.ssh/" ]; then - if ls "${uhome}/.ssh/*" >/dev/null 2>&1; then - for key in ${uhome}/.ssh/*; do - if [ "${key}" == "${uhome}/.ssh/authorized_keys" ]; then - if [ "$(cat "${key}" | wc -c)" -gt 50 ]; then - echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a populated authorized_keys file in \e[93m${key}\e[0m\n" - akey=$(cat ${key}) - echo "File Contents:" - echo $akey - echo "--------------" - ((FAIL++)) - STATUS=2 - fi - elif [ "${key}" == "${uhome}/.ssh/id_rsa" ]; then - if [ "$(cat "${key}" | wc -c)" -gt 0 ]; then - echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a private key file in \e[93m${key}\e[0m\n" - akey=$(cat ${key}) - echo "File Contents:" - echo $akey - echo "--------------" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has empty private key file in \e[93m${key}\e[0m\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - elif [ "${key}" != "${uhome}/.ssh/known_hosts" ]; then - - echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a file in their .ssh directory named \e[93m${key}\e[0m\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - - else - if [ "$(cat "${key}" | wc -c)" -gt 50 ]; then - echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a known_hosts file in \e[93m${key}\e[0m\n" - ((WARN++)) - if [[ $STATUS != 2 ]]; then - STATUS=1 - fi - fi - fi - - done - else - echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m has no SSH keys present\n" - fi - else - echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have an .ssh directory\n" - fi - else - echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have a directory in /home\n" - fi - - # Check for an uncleared .bash_history for this user - if [ -f "${uhome}/.bash_history" ]; then - BH_S=$(cat "${uhome}/.bash_history" | wc -c) - - if [[ $BH_S -lt 200 ]]; then - echo -en "\e[32m[PASS]\e[0m ${user}'s Bash History appears to have been cleared\n" - ((PASS++)) - else - echo -en "\e[41m[FAIL]\e[0m ${user}'s Bash History should be cleared to prevent sensitive information from leaking\n" - ((FAIL++)) - STATUS=2 - - fi - echo -en "\n\n" - fi - fi - done -} -function checkFirewall { - - if [[ $OS == "Ubuntu" ]]; then - fw="ufw" - ufwa=$(ufw status | head -1 | sed -e "s/^Status:\ //") - if [[ $ufwa == "active" ]]; then - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - elif [[ $OS == "CentOS Linux" ]]; then - if [ -f /usr/lib/systemd/system/csf.service ]; then - fw="csf" - if [[ $(systemctl status $fw >/dev/null 2>&1) ]]; then - - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - elif cmdExists "firewall-cmd"; then - if [[ $(systemctl is-active firewalld >/dev/null 2>&1 && echo 1 || echo 0) ]]; then - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - else - fw="firewalld" - if [[ $(systemctl is-active firewalld >/dev/null 2>&1 && echo 1 || echo 0) ]]; then - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - fi - elif [[ $OS =~ Debian.* ]]; then - # user could be using a number of different services for managing their firewall - # we will check some of the most common - if cmdExists 'ufw'; then - fw="ufw" - ufwa=$(ufw status | head -1 | sed -e "s/^Status:\ //") - if [[ $ufwa == "active" ]]; then - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - elif cmdExists "firewall-cmd"; then - fw="firewalld" - if [[ $(systemctl is-active --quiet $fw) ]]; then - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - else - # user could be using vanilla iptables, check if kernel module is loaded - fw="iptables" - if [[ $(lsmod | grep -q '^ip_tables' 2>/dev/null) ]]; then - FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - ((PASS++)) - else - FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - ((WARN++)) - fi - fi - fi - -} -function checkUpdates { - if [[ $OS == "Ubuntu" ]] || [[ $OS =~ Debian.* ]]; then - # Ensure /tmp exists and has the proper permissions before - # checking for security updates - # https://github.com/digitalocean/marketplace-partners/issues/94 - if [[ ! -d /tmp ]]; then - mkdir /tmp - fi - chmod 1777 /tmp - - echo -en "\nUpdating apt package database to check for security updates, this may take a minute...\n\n" - apt-get -y update >/dev/null - - uc=$(apt-get --just-print upgrade | grep -i "security" | wc -l) - if [[ $uc -gt 0 ]]; then - update_count=$((uc / 2)) - else - update_count=0 - fi - - if [[ $update_count -gt 0 ]]; then - echo -en "\e[41m[FAIL]\e[0m There are ${update_count} security updates available for this image that have not been installed.\n" - echo -en - echo -en "Here is a list of the security updates that are not installed:\n" - sleep 2 - apt-get --just-print upgrade | grep -i security | awk '{print $2}' | awk '!seen[$0]++' - echo -en - ((FAIL++)) - STATUS=2 - else - echo -en "\e[32m[PASS]\e[0m There are no pending security updates for this image.\n\n" - fi - elif [[ $OS == "CentOS Linux" ]]; then - echo -en "\nChecking for available security updates, this may take a minute...\n\n" - - update_count=$(yum check-update --security --quiet | wc -l) - if [[ $update_count -gt 0 ]]; then - echo -en "\e[41m[FAIL]\e[0m There are ${update_count} security updates available for this image that have not been installed.\n" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[32m[PASS]\e[0m There are no pending security updates for this image.\n" - ((PASS++)) - fi - else - echo "Error encountered" - exit 1 - fi - - return 1 -} -function checkCloudInit { - - if hash cloud-init 2>/dev/null; then - CI="\e[32m[PASS]\e[0m Cloud-init is installed.\n" - ((PASS++)) - else - CI="\e[41m[FAIL]\e[0m No valid verison of cloud-init was found.\n" - ((FAIL++)) - STATUS=2 - fi - return 1 -} -function checkMongoDB { - # Check if MongoDB is installed - # If it is, verify the version is allowed (non-SSPL) - - if [[ $OS == "Ubuntu" ]] || [[ $OS =~ Debian.* ]]; then - - if [[ -f "/usr/bin/mongod" ]]; then - version=$(/usr/bin/mongod --version --quiet | grep "db version" | sed -e "s/^db\ version\ v//") - - if version_gt $version 4.0.0; then - if version_gt $version 4.0.3; then - echo -en "\e[41m[FAIL]\e[0m An SSPL version of MongoDB is present, ${version}" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[32m[PASS]\e[0m The version of MongoDB installed, ${version} is not under the SSPL" - ((PASS++)) - fi - else - if version_gt $version 3.6.8; then - echo -en "\e[41m[FAIL]\e[0m An SSPL version of MongoDB is present, ${version}" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[32m[PASS]\e[0m The version of MongoDB installed, ${version} is not under the SSPL" - ((PASS++)) - fi - fi - - else - echo -en "\e[32m[PASS]\e[0m MongoDB is not installed" - ((PASS++)) - fi - - elif [[ $OS == "CentOS Linux" ]]; then - - if [[ -f "/usr/bin/mongod" ]]; then - version=$(/usr/bin/mongod --version --quiet | grep "db version" | sed -e "s/^db\ version\ v//") - - if version_gt $version 4.0.0; then - if version_gt $version 4.0.3; then - echo -en "\e[41m[FAIL]\e[0m An SSPL version of MongoDB is present" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[32m[PASS]\e[0m The version of MongoDB installed is not under the SSPL" - ((PASS++)) - fi - else - if version_gt $version 3.6.8; then - echo -en "\e[41m[FAIL]\e[0m An SSPL version of MongoDB is present" - ((FAIL++)) - STATUS=2 - else - echo -en "\e[32m[PASS]\e[0m The version of MongoDB installed is not under the SSPL" - ((PASS++)) - fi - fi - - else - echo -en "\e[32m[PASS]\e[0m MongoDB is not installed" - ((PASS++)) - fi - - else - echo "ERROR: Unable to identify distribution" - ((FAIL++)) - STATUS 2 - return 1 - fi - -} - -function version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; } - -clear -echo "DigitalOcean Marketplace Image Validation Tool ${VERSION}" -echo "Executed on: ${RUNDATE}" -echo "Checking local system for Marketplace compatibility..." - -getDistro - -echo -en "\n\e[1mDistribution:\e[0m ${OS}\n" -echo -en "\e[1mVersion:\e[0m ${VER}\n\n" - -ost=0 -osv=0 - -if [[ $OS == "Ubuntu" ]]; then - ost=1 - if [[ $VER == "24.04" ]]; then - osv=1 - elif [[ $VER == "18.04" ]]; then - osv=1 - elif [[ $VER == "16.04" ]]; then - osv=1 - else - osv=0 - fi - -elif [[ $OS =~ Debian.* ]]; then - ost=1 - case "$VER" in - 9) - osv=1 - ;; - 10) - osv=1 - ;; - *) - osv=2 - ;; - esac - -elif [[ $OS == "CentOS Linux" ]]; then - ost=1 - if [[ $VER == "8" ]]; then - osv=1 - elif [[ $VER == "7" ]]; then - osv=1 - elif [[ $VER == "6" ]]; then - osv=1 - else - osv=2 - fi -else - ost=0 -fi - -if [[ $ost == 1 ]]; then - echo -en "\e[32m[PASS]\e[0m Supported Operating System Detected: ${OS}\n" - ((PASS++)) -else - echo -en "\e[41m[FAIL]\e[0m ${OS} is not a supported Operating System\n" - ((FAIL++)) - STATUS=2 -fi - -if [[ $osv == 1 ]]; then - echo -en "\e[32m[PASS]\e[0m Supported Release Detected: ${VER}\n" - ((PASS++)) -elif [[ $ost == 1 ]]; then - echo -en "\e[41m[FAIL]\e[0m ${OS} ${VER} is not a supported Operating System Version\n" - ((FAIL++)) - STATUS=2 -else - echo "Exiting..." - exit 1 -fi - -checkCloudInit - -echo -en "${CI}" - -checkFirewall - -echo -en "${FW_VER}" - -checkUpdates - -loadPasswords - -checkLogs - -echo -en "\n\nChecking all user-created accounts...\n" -checkUsers - -echo -en "\n\nChecking the root account...\n" -checkRoot - -checkAgent - -checkMongoDB - -# Summary -echo -en "\n\n---------------------------------------------------------------------------------------------------\n" - -if [[ $STATUS == 0 ]]; then - echo -en "Scan Complete.\n\e[32mAll Tests Passed!\e[0m\n" -elif [[ $STATUS == 1 ]]; then - echo -en "Scan Complete. \n\e[93mSome non-critical tests failed. Please review these items.\e[0m\e[0m\n" -else - echo -en "Scan Complete. \n\e[41mOne or more tests failed. Please review these items and re-test.\e[0m\n" -fi -echo "---------------------------------------------------------------------------------------------------" -echo -en "\e[1m${PASS} Tests PASSED\e[0m\n" -echo -en "\e[1m${WARN} WARNINGS\e[0m\n" -echo -en "\e[1m${FAIL} Tests FAILED\e[0m\n" -echo -en "---------------------------------------------------------------------------------------------------\n" - -if [[ $STATUS == 0 ]]; then - echo -en "We did not detect any issues with this image. Please be sure to manually ensure that all software installed on the base system is functional, secure and properly configured (or facilities for configuration on first-boot have been created).\n\n" - exit 0 -elif [[ $STATUS == 1 ]]; then - echo -en "Please review all [WARN] items above and ensure they are intended or resolved. If you do not have a specific requirement, we recommend resolving these items before image submission\n\n" - exit 0 -else - echo -en "Some critical tests failed. These items must be resolved and this scan re-run before you submit your image to the DigitalOcean Marketplace.\n\n" - exit 1 -fi From b0c56ddf965fd6b1f57de9c1cf768cf78fcbe1a0 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Tue, 2 Jun 2026 14:17:46 -0400 Subject: [PATCH 4/7] packer: Be more explicit about file uploads I decided to refactor these pkr.hcl files so that things are a little easier to understand. We were uploading some files/dirs straight to /tmp/ and others into /tmp/ansible-playbook. The ansible-playbook ones were pretty confusing when looking at logs and trying to find files which you'd guess was under ansible/, but they weren't. Some of the things under ansible-playbook weren't even used/run by ansible at all! So I decided to take a good look and restructure things. Now /tmp/ mimics layout in repo for easier mapping from logs, scripts/templates have been updated accordingly. I also moved the last remaining files that were in scripts/ over into ebssurrogate since thats the only place they're used. Some attempts at docs updates were made. --- README.md | 1 - amazon-amd64-nix.pkr.hcl | 32 +++++++------------ amazon-arm64-nix.pkr.hcl | 32 +++++++------------ ansible/files/supascan_ami.sh | 2 +- ansible/playbook.yml | 2 +- development-arm.vars.pkr.hcl | 1 - ebssurrogate/USAGE.md | 27 +++++++++------- .../scripts}/90-cleanup-qemu.sh | 0 .../scripts}/90-cleanup.sh | 0 .../scripts}/nix-provision.sh | 4 +-- ebssurrogate/scripts/qemu-bootstrap-nix.sh | 4 +-- .../scripts/surrogate-bootstrap-nix.sh | 4 +-- nix/packages/build-ami.nix | 1 - qemu-arm64-nix.pkr.hcl | 15 +++------ stage2-nix-psql.pkr.hcl | 17 ++++------ 15 files changed, 57 insertions(+), 85 deletions(-) rename {scripts => ebssurrogate/scripts}/90-cleanup-qemu.sh (100%) rename {scripts => ebssurrogate/scripts}/90-cleanup.sh (100%) rename {scripts => ebssurrogate/scripts}/nix-provision.sh (94%) diff --git a/README.md b/README.md index b509bdddd7..40106aaddf 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,6 @@ Here's a comprehensive overview of the project's directory structure: | **tests/** | Integration and system tests | | testinfra/ | Infrastructure tests using pytest framework | | tests/ | General integration test suites | -| **scripts/** | Utility scripts for development and deployment | | **docs/** | Additional documentation, images, and resources | | **ebssurrogate/** | AWS EBS surrogate building for AMI creation | | **http/** | HTTP-related configurations and files | diff --git a/amazon-amd64-nix.pkr.hcl b/amazon-amd64-nix.pkr.hcl index 443387d6d0..33fdd3d9a1 100644 --- a/amazon-amd64-nix.pkr.hcl +++ b/amazon-amd64-nix.pkr.hcl @@ -194,57 +194,47 @@ build { provisioner "file" { source = "ebssurrogate/files/ebsnvme-id" - destination = "/tmp/ebsnvme-id" + destination = "/tmp/" } provisioner "file" { source = "ebssurrogate/files/70-ec2-nvme-devices.rules" - destination = "/tmp/70-ec2-nvme-devices.rules" + destination = "/tmp/" } provisioner "file" { source = "ebssurrogate/scripts/chroot-bootstrap-nix.sh" - destination = "/tmp/chroot-bootstrap-nix.sh" + destination = "/tmp/" } provisioner "file" { source = "ebssurrogate/files/cloud.cfg" - destination = "/tmp/cloud.cfg" + destination = "/tmp/" } provisioner "file" { source = "ebssurrogate/files/vector.timer" - destination = "/tmp/vector.timer" + destination = "/tmp/" } provisioner "file" { source = "ebssurrogate/files/apparmor_profiles" - destination = "/tmp" + destination = "/tmp/" } provisioner "file" { source = "migrations" - destination = "/tmp" - } - - # Copy ansible playbook - provisioner "shell" { - inline = ["mkdir /tmp/ansible-playbook"] + destination = "/tmp/" } provisioner "file" { source = "ansible" - destination = "/tmp/ansible-playbook" - } - - provisioner "file" { - source = "scripts" - destination = "/tmp/ansible-playbook" + destination = "/tmp/" } provisioner "file" { - source = "ansible/vars.yml" - destination = "/tmp/ansible-playbook/vars.yml" + source = "ebssurrogate/scripts/90-cleanup.sh" + destination = "/tmp/" } provisioner "shell" { @@ -258,7 +248,7 @@ build { ] use_env_var_file = true script = "ebssurrogate/scripts/surrogate-bootstrap-nix.sh" - execute_command = "sudo -S sh -c '. {{.EnvVarFile}} && cd /tmp/ansible-playbook && {{.Path}}'" + execute_command = "sudo -S sh -c '. {{.EnvVarFile}} && {{.Path}}'" start_retry_timeout = "5m" skip_clean = true } diff --git a/amazon-arm64-nix.pkr.hcl b/amazon-arm64-nix.pkr.hcl index 4faa8aac40..49b4dfdb28 100644 --- a/amazon-arm64-nix.pkr.hcl +++ b/amazon-arm64-nix.pkr.hcl @@ -194,57 +194,47 @@ build { provisioner "file" { source = "ebssurrogate/files/ebsnvme-id" - destination = "/tmp/ebsnvme-id" + destination = "/tmp/" } provisioner "file" { source = "ebssurrogate/files/70-ec2-nvme-devices.rules" - destination = "/tmp/70-ec2-nvme-devices.rules" + destination = "/tmp/" } provisioner "file" { source = "ebssurrogate/scripts/chroot-bootstrap-nix.sh" - destination = "/tmp/chroot-bootstrap-nix.sh" + destination = "/tmp/" } provisioner "file" { source = "ebssurrogate/files/cloud.cfg" - destination = "/tmp/cloud.cfg" + destination = "/tmp/" } provisioner "file" { source = "ebssurrogate/files/vector.timer" - destination = "/tmp/vector.timer" + destination = "/tmp/" } provisioner "file" { source = "ebssurrogate/files/apparmor_profiles" - destination = "/tmp" + destination = "/tmp/" } provisioner "file" { source = "migrations" - destination = "/tmp" - } - - # Copy ansible playbook - provisioner "shell" { - inline = ["mkdir /tmp/ansible-playbook"] + destination = "/tmp/" } provisioner "file" { source = "ansible" - destination = "/tmp/ansible-playbook" - } - - provisioner "file" { - source = "scripts" - destination = "/tmp/ansible-playbook" + destination = "/tmp/" } provisioner "file" { - source = "ansible/vars.yml" - destination = "/tmp/ansible-playbook/vars.yml" + source = "ebssurrogate/scripts/90-cleanup.sh" + destination = "/tmp/" } provisioner "shell" { @@ -258,7 +248,7 @@ build { ] use_env_var_file = true script = "ebssurrogate/scripts/surrogate-bootstrap-nix.sh" - execute_command = "sudo -S sh -c '. {{.EnvVarFile}} && cd /tmp/ansible-playbook && {{.Path}}'" + execute_command = "sudo -S sh -c '. {{.EnvVarFile}} && {{.Path}}'" start_retry_timeout = "5m" skip_clean = true } diff --git a/ansible/files/supascan_ami.sh b/ansible/files/supascan_ami.sh index 8a025ffaf0..446d81771b 100644 --- a/ansible/files/supascan_ami.sh +++ b/ansible/files/supascan_ami.sh @@ -8,7 +8,7 @@ set -euo pipefail -BASELINES_DIR="${1:-/tmp/ansible-playbook/audit-specs/baselines/ami-build}" +BASELINES_DIR="${1:-/tmp/audit-specs/baselines/ami-build}" echo "============================================================" echo "Baseline Validation" diff --git a/ansible/playbook.yml b/ansible/playbook.yml index 73db87d7af..8d12b418ed 100644 --- a/ansible/playbook.yml +++ b/ansible/playbook.yml @@ -235,7 +235,7 @@ - name: Run supascan baseline validation become: yes shell: | - /bin/bash /tmp/ansible-playbook/ansible/files/supascan_ami.sh /tmp/ansible-playbook/audit-specs/baselines/ami-build + /bin/bash /tmp/ansible-playbook/ansible/files/supascan_ami.sh /tmp/audit-specs/baselines/ami-build when: stage2_nix and qemu_mode is not defined and ansible_architecture != "x86_64" - name: Remove supascan after validation diff --git a/development-arm.vars.pkr.hcl b/development-arm.vars.pkr.hcl index 3a07e0b53b..7bac0f9e72 100644 --- a/development-arm.vars.pkr.hcl +++ b/development-arm.vars.pkr.hcl @@ -4,4 +4,3 @@ environment = "dev" instance-type = "c6g.4xlarge" region= "ap-southeast-1" ubuntu-2004 = "ami-0b49a4a6e8e22fa16" - diff --git a/ebssurrogate/USAGE.md b/ebssurrogate/USAGE.md index 9a63a082cb..5815917e2e 100644 --- a/ebssurrogate/USAGE.md +++ b/ebssurrogate/USAGE.md @@ -36,15 +36,20 @@ amazon-arm64.pkr.hcl` ``` $ tree ebssurrogate/ ebssurrogate/ -├── files -│   ├── 70-ec2-nvme-devices.rules -│   ├── cloud.cfg # cloud.cfg for cloud-init -│   ├── ebsnvme-id -│   ├── sources-arm64.cfg # apt/sources.list for arm64 -│   ├── sources.cfg # apt/sources.list for amd64 -│   ├── vector.timer # systemd-timer to delay vectore execution -│   └── zfs-growpart-root.cfg -└── scripts - ├── chroot-bootstrap.sh # Installs grub and other required packages for build. Configures target AMI settings - └── surrogate-bootstrap.sh # Formats disk and setups chroot environment. Runs Ansible tasks within chrooted environment. +├── files/ +│   ├── apparmor_profiles # apparmor config files +│   ├── 70-ec2-nvme-devices.rules # udev rules for ebs volumes +│   ├── cloud.cfg # cloud.cfg for cloud-init +│   ├── ebsnvme-id # gets ebs volume info +│   ├── sources-arm64.cfg # apt/sources.list for arm64 +│   ├── sources.cfg # apt/sources.list for amd64 +│   ├── vector.timer # systemd-timer to delay vectore execution +└── scripts/ + ├── 90-cleanup-qemu.sh # Runs qemu specific clean up tasks + ├── 90-cleanup.sh # Runs AWS specific clean up tasks + ├── chroot-bootstrap-nix.sh # Installs grub and other required packages for build. Configures target AMI settings + ├── nix-provision.sh # Installs nix and runs stage-2 Ansible playbook + ├── qemu-bootstrap-nix.sh # Installs nix, runs stage-2 Ansible playbook, runs qemu specific tasks + └── surrogate-bootstrap-nix.sh # Formats disk and setups chroot environment. Runs Ansible tasks within chrooted environment. + ``` diff --git a/scripts/90-cleanup-qemu.sh b/ebssurrogate/scripts/90-cleanup-qemu.sh similarity index 100% rename from scripts/90-cleanup-qemu.sh rename to ebssurrogate/scripts/90-cleanup-qemu.sh diff --git a/scripts/90-cleanup.sh b/ebssurrogate/scripts/90-cleanup.sh similarity index 100% rename from scripts/90-cleanup.sh rename to ebssurrogate/scripts/90-cleanup.sh diff --git a/scripts/nix-provision.sh b/ebssurrogate/scripts/nix-provision.sh similarity index 94% rename from scripts/nix-provision.sh rename to ebssurrogate/scripts/nix-provision.sh index 3fb2f5a530..ae8b614246 100644 --- a/scripts/nix-provision.sh +++ b/ebssurrogate/scripts/nix-provision.sh @@ -45,11 +45,11 @@ function execute_stage2_playbook { [defaults] callbacks_enabled = timer, profile_tasks, profile_roles EOF - sed -i 's/- hosts: all/- hosts: localhost/' /tmp/ansible-playbook/ansible/playbook.yml + sed -i 's/- hosts: all/- hosts: localhost/' /tmp/ansible/playbook.yml # Run Ansible playbook export ANSIBLE_LOG_PATH=/tmp/ansible.log && export ANSIBLE_REMOTE_TEMP=/tmp - ansible-playbook /tmp/ansible-playbook/ansible/playbook.yml \ + ansible-playbook /tmp/ansible/playbook.yml \ --extra-vars '{"nixpkg_mode": false, "stage2_nix": true, "debpkg_mode": false}' \ --extra-vars "git_commit_sha=${GIT_SHA}" \ --extra-vars "psql_version=psql_${POSTGRES_MAJOR_VERSION}" \ diff --git a/ebssurrogate/scripts/qemu-bootstrap-nix.sh b/ebssurrogate/scripts/qemu-bootstrap-nix.sh index d61c3c628e..aa0f9998d4 100755 --- a/ebssurrogate/scripts/qemu-bootstrap-nix.sh +++ b/ebssurrogate/scripts/qemu-bootstrap-nix.sh @@ -121,8 +121,8 @@ function clean_legacy_things { function clean_system { # Copy cleanup scripts - chmod +x /tmp/ansible-playbook/scripts/90-cleanup-qemu.sh - /tmp/ansible-playbook/scripts/90-cleanup-qemu.sh + chmod +x /tmp/90-cleanup-qemu.sh + /tmp/90-cleanup-qemu.sh # # Cleanup logs rm -rf /var/log/* diff --git a/ebssurrogate/scripts/surrogate-bootstrap-nix.sh b/ebssurrogate/scripts/surrogate-bootstrap-nix.sh index 607527dc65..f1f290c2ca 100755 --- a/ebssurrogate/scripts/surrogate-bootstrap-nix.sh +++ b/ebssurrogate/scripts/surrogate-bootstrap-nix.sh @@ -338,7 +338,7 @@ EOF # Run Ansible playbook #export ANSIBLE_LOG_PATH=/tmp/ansible.log && export ANSIBLE_DEBUG=True && export ANSIBLE_REMOTE_TEMP=/mnt/tmp export ANSIBLE_LOG_PATH=/tmp/ansible.log && export ANSIBLE_REMOTE_TEMP=/mnt/tmp - ansible-playbook -c chroot -i '/mnt,' /tmp/ansible-playbook/ansible/playbook.yml \ + ansible-playbook -c chroot -i '/mnt,' /tmp/ansible/playbook.yml \ --extra-vars '{"nixpkg_mode": true, "debpkg_mode": false, "stage2_nix": false} ' \ --extra-vars "psql_version=psql_${POSTGRES_MAJOR_VERSION}" \ $ARGS @@ -361,7 +361,7 @@ function update_systemd_services { function clean_system { # Copy cleanup scripts - cp -v /tmp/ansible-playbook/scripts/90-cleanup.sh /mnt/tmp + cp -v /tmp/90-cleanup.sh /mnt/tmp chmod +x /mnt/tmp/90-cleanup.sh chroot /mnt /tmp/90-cleanup.sh diff --git a/nix/packages/build-ami.nix b/nix/packages/build-ami.nix index c847f47a41..ec62b36d67 100644 --- a/nix/packages/build-ami.nix +++ b/nix/packages/build-ami.nix @@ -18,7 +18,6 @@ let (root + "/ebssurrogate") (root + "/ansible") (root + "/migrations") - (root + "/scripts") (root + "/amazon-amd64-nix.pkr.hcl") (root + "/amazon-arm64-nix.pkr.hcl") (root + "/development-arm.vars.pkr.hcl") diff --git a/qemu-arm64-nix.pkr.hcl b/qemu-arm64-nix.pkr.hcl index 17cca3a6eb..83f117119d 100644 --- a/qemu-arm64-nix.pkr.hcl +++ b/qemu-arm64-nix.pkr.hcl @@ -105,24 +105,19 @@ build { name = "cloudimg.image" sources = ["source.qemu.cloudimg"] - # Copy ansible playbook - provisioner "shell" { - inline = ["mkdir /tmp/ansible-playbook"] - } - provisioner "file" { source = "ansible" - destination = "/tmp/ansible-playbook" + destination = "/tmp/" } provisioner "file" { - source = "scripts" - destination = "/tmp/ansible-playbook" + source = "ebssurrogate/scripts/90-cleanup-qemu.sh" + destination = "/tmp/" } provisioner "file" { source = "migrations" - destination = "/tmp" + destination = "/tmp/" } provisioner "shell" { @@ -133,7 +128,7 @@ build { ] use_env_var_file = true script = "ebssurrogate/scripts/qemu-bootstrap-nix.sh" - execute_command = "sudo -S sh -c '. {{.EnvVarFile}} && cd /tmp/ansible-playbook && {{.Path}}'" + execute_command = "sudo -S sh -c '. {{.EnvVarFile}} && {{.Path}}'" start_retry_timeout = "5m" skip_clean = true } diff --git a/stage2-nix-psql.pkr.hcl b/stage2-nix-psql.pkr.hcl index 58e60a2490..97e609c9ed 100644 --- a/stage2-nix-psql.pkr.hcl +++ b/stage2-nix-psql.pkr.hcl @@ -100,29 +100,24 @@ build { "source.amazon-ebs.ubuntu" ] - # Copy ansible playbook - provisioner "shell" { - inline = ["mkdir /tmp/ansible-playbook"] - } - provisioner "file" { source = "ansible" - destination = "/tmp/ansible-playbook" + destination = "/tmp/" } provisioner "file" { source = "migrations" - destination = "/tmp" + destination = "/tmp/" } provisioner "file" { - source = "scripts" - destination = "/tmp/ansible-playbook" + source = "ebssurrogate/scripts/nix-provision.sh" + destination = "/tmp/" } provisioner "file" { source = "audit-specs" - destination = "/tmp/ansible-playbook" + destination = "/tmp/" } provisioner "shell" { @@ -130,7 +125,7 @@ build { "GIT_SHA=${var.git_sha}", "POSTGRES_MAJOR_VERSION=${var.postgres_major_version}" ] - script = "scripts/nix-provision.sh" + script = "ebssurrogate/scripts/nix-provision.sh" } } From 0c164256339f1dc470284a48df00c33c862f442a Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Tue, 2 Jun 2026 14:17:46 -0400 Subject: [PATCH 5/7] packer: Use dpkg output for ARCH directly in ebssurrogate/scripts dpkg --print-architecture returns amd64|arm64 so no point using an if statement with the same values. --- ebssurrogate/scripts/chroot-bootstrap-nix.sh | 6 +----- ebssurrogate/scripts/qemu-bootstrap-nix.sh | 6 ------ ebssurrogate/scripts/surrogate-bootstrap-nix.sh | 6 +----- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/ebssurrogate/scripts/chroot-bootstrap-nix.sh b/ebssurrogate/scripts/chroot-bootstrap-nix.sh index e084f7d546..670c1cb96c 100755 --- a/ebssurrogate/scripts/chroot-bootstrap-nix.sh +++ b/ebssurrogate/scripts/chroot-bootstrap-nix.sh @@ -22,11 +22,7 @@ exit 101 EOF chmod +x /usr/sbin/policy-rc.d -if [ $(dpkg --print-architecture) = "amd64" ]; then - ARCH="amd64" -else - ARCH="arm64" -fi +ARCH=$(dpkg --print-architecture) # Get current mirror from sources.list function get_current_mirror { diff --git a/ebssurrogate/scripts/qemu-bootstrap-nix.sh b/ebssurrogate/scripts/qemu-bootstrap-nix.sh index aa0f9998d4..06eda23b29 100755 --- a/ebssurrogate/scripts/qemu-bootstrap-nix.sh +++ b/ebssurrogate/scripts/qemu-bootstrap-nix.sh @@ -4,12 +4,6 @@ set -o errexit set -o pipefail set -o xtrace -if [ $(dpkg --print-architecture) = "amd64" ]; then - ARCH="amd64" -else - ARCH="arm64" -fi - function waitfor_boot_finished { export DEBIAN_FRONTEND=noninteractive diff --git a/ebssurrogate/scripts/surrogate-bootstrap-nix.sh b/ebssurrogate/scripts/surrogate-bootstrap-nix.sh index f1f290c2ca..633e98375b 100755 --- a/ebssurrogate/scripts/surrogate-bootstrap-nix.sh +++ b/ebssurrogate/scripts/surrogate-bootstrap-nix.sh @@ -10,11 +10,7 @@ set -o errexit set -o pipefail set -o xtrace -if [ $(dpkg --print-architecture) = "amd64" ]; then - ARCH="amd64" -else - ARCH="arm64" -fi +ARCH=$(dpkg --print-architecture) # Mirror fallback function for resilient apt-get update function apt_update_with_fallback { From 9d806ee148257ea9904b04b4ea2c4405b277535c Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Tue, 2 Jun 2026 17:20:38 -0400 Subject: [PATCH 6/7] packer: Set scripts to be ultra strict These were mostly missing the `nounset` option which is very handy to have so I added it in. The DigitalOcean based clean ups have been giving trouble recently so I added -x to them too (well -u too so all scripts are maximally safe_er_) --- ebssurrogate/scripts/90-cleanup-qemu.sh | 4 ++-- ebssurrogate/scripts/90-cleanup.sh | 4 ++-- ebssurrogate/scripts/chroot-bootstrap-nix.sh | 4 +--- ebssurrogate/scripts/nix-provision.sh | 5 +---- ebssurrogate/scripts/qemu-bootstrap-nix.sh | 4 +--- ebssurrogate/scripts/surrogate-bootstrap-nix.sh | 6 ++---- 6 files changed, 9 insertions(+), 18 deletions(-) diff --git a/ebssurrogate/scripts/90-cleanup-qemu.sh b/ebssurrogate/scripts/90-cleanup-qemu.sh index c385cee53f..7d8259fe40 100644 --- a/ebssurrogate/scripts/90-cleanup-qemu.sh +++ b/ebssurrogate/scripts/90-cleanup-qemu.sh @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # DigitalOcean Marketplace Image Validation Tool # © 2021 DigitalOcean LLC. # This code is licensed under Apache 2.0 license (see LICENSE.md for details) -set -o errexit +set -euxo pipefail # Ensure /tmp exists and has the proper permissions before # checking for security updates diff --git a/ebssurrogate/scripts/90-cleanup.sh b/ebssurrogate/scripts/90-cleanup.sh index bdb2993f3b..8ac5cbc6da 100644 --- a/ebssurrogate/scripts/90-cleanup.sh +++ b/ebssurrogate/scripts/90-cleanup.sh @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # DigitalOcean Marketplace Image Validation Tool # © 2021 DigitalOcean LLC. # This code is licensed under Apache 2.0 license (see LICENSE.md for details) -set -o errexit +set -euxo pipefail # Ensure /tmp exists and has the proper permissions before # checking for security updates diff --git a/ebssurrogate/scripts/chroot-bootstrap-nix.sh b/ebssurrogate/scripts/chroot-bootstrap-nix.sh index 670c1cb96c..1ac58adbae 100755 --- a/ebssurrogate/scripts/chroot-bootstrap-nix.sh +++ b/ebssurrogate/scripts/chroot-bootstrap-nix.sh @@ -4,9 +4,7 @@ # Configuration file. # -set -o errexit -set -o pipefail -set -o xtrace +set -euxo pipefail export DEBIAN_FRONTEND=noninteractive diff --git a/ebssurrogate/scripts/nix-provision.sh b/ebssurrogate/scripts/nix-provision.sh index ae8b614246..2a4453e6d2 100644 --- a/ebssurrogate/scripts/nix-provision.sh +++ b/ebssurrogate/scripts/nix-provision.sh @@ -1,9 +1,6 @@ #!/usr/bin/env bash -# shellcheck shell=bash -set -o errexit -set -o pipefail -set -o xtrace +set -euxo pipefail function install_packages { # Setup Ansible on host VM diff --git a/ebssurrogate/scripts/qemu-bootstrap-nix.sh b/ebssurrogate/scripts/qemu-bootstrap-nix.sh index 06eda23b29..b4fbb6e424 100755 --- a/ebssurrogate/scripts/qemu-bootstrap-nix.sh +++ b/ebssurrogate/scripts/qemu-bootstrap-nix.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash -set -o errexit -set -o pipefail -set -o xtrace +set -euxo pipefail function waitfor_boot_finished { export DEBIAN_FRONTEND=noninteractive diff --git a/ebssurrogate/scripts/surrogate-bootstrap-nix.sh b/ebssurrogate/scripts/surrogate-bootstrap-nix.sh index 633e98375b..1f60a33269 100755 --- a/ebssurrogate/scripts/surrogate-bootstrap-nix.sh +++ b/ebssurrogate/scripts/surrogate-bootstrap-nix.sh @@ -6,9 +6,7 @@ # # Adapted from: https://github.com/jen20/packer-ubuntu-zfs -set -o errexit -set -o pipefail -set -o xtrace +set -euxo pipefail ARCH=$(dpkg --print-architecture) @@ -19,7 +17,7 @@ function apt_update_with_fallback { local attempt=1 # Get EC2 region if not already set - if [ -z "${REGION}" ]; then + if [ -z "${REGION:-}" ]; then REGION=$(curl --silent --fail http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -E 's|[a-z]+$||g' || echo "") fi From 9ef4252d8e2262228d7e9d2006cbfca873d0f553 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Tue, 2 Jun 2026 14:17:46 -0400 Subject: [PATCH 7/7] bump pg relese versions --- ansible/vars.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ansible/vars.yml b/ansible/vars.yml index aca4f67ac8..78b61ef919 100644 --- a/ansible/vars.yml +++ b/ansible/vars.yml @@ -10,9 +10,9 @@ postgres_major: # Full version strings for each major version postgres_release: - postgresorioledb-17: "17.6.0.089-orioledb" - postgres17: "17.6.1.132" - postgres15: "15.14.1.132" + postgresorioledb-17: "17.6.0.089-orioledb-push-spuxztvtwozk-1" + postgres17: "17.6.1.132-push-spuxztvtwozk-1" + postgres15: "15.14.1.132-push-spuxztvtwozk-1" # Non Postgres Extensions pgbouncer_release: 1.25.1