2 Commits

Author SHA1 Message Date
Stephan D
0bb32ccabd vault app_role secrets pass
Some checks failed
ci/woodpecker/push/db Pipeline failed
2025-11-07 11:15:57 +01:00
Stephan D
385c98939a removed debug output 2025-11-07 11:03:18 +01:00
3 changed files with 74 additions and 39 deletions

View File

@@ -6,6 +6,7 @@ steps:
- name: version
image: alpine:latest
commands:
- set -euo pipefail
- apk add --no-cache git
- GIT_REV="$(git rev-parse --short HEAD)"
- BUILD_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
@@ -16,36 +17,36 @@ steps:
image: alpine:latest
depends_on: [ version ]
environment:
# Vault access for CI (AppRole for CI itself, NOT the app AppRole)
VAULT_ADDR: https://vault.sendico.io
VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE }
VAULT_SECRET_ID: { from_secret: VAULT_SECRET_ID }
VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE } # CI's AppRole role_id
VAULT_SECRET_ID: { from_secret: VAULT_SECRET_ID } # CI's AppRole secret_id
commands:
- set -euo pipefail
- apk add --no-cache curl bash coreutils sed python3 openssh-keygen
- mkdir -p secrets
# fetch SSH private key for deploy (base64-encoded) and decode
# Fetch SSH private key for deploy (base64-encoded) and decode
- ./ci/vlt kv_to_file kv ops/deploy/ssh_key private_b64 secrets/SSH_KEY.b64 600
- base64 -d secrets/SSH_KEY.b64 > secrets/SSH_KEY
- chmod 600 secrets/SSH_KEY
- ssh-keygen -y -f secrets/SSH_KEY >/dev/null
# Fetch AppRole creds for Vault Agent (adjust the KV path if different)
- ./ci/vlt kv_to_file kv ops/vault/approle/sendico-db role_id secrets/ROLE_ID 600
- ./ci/vlt kv_to_file kv ops/vault/approle/sendico-db secret_id secrets/SECRET_ID 600
- name: deploy
image: alpine:latest
depends_on: [ secrets ]
commands:
- set -Eeuxo pipefail
- set -euo pipefail
- apk add --no-cache bash openssh-client rsync coreutils
- mkdir -p /root/.ssh
- install -m 600 secrets/SSH_KEY /root/.ssh/id_rsa
# страхуемся от CRLF в env-файле
# Normalize CRLF if any, then export runtime env (Compose variables)
- sed -i 's/\r$//' ./ci/prod/.env.runtime
# поднимаем переменные для скрипта
- set -a
- . ./ci/prod/.env.runtime
- . ./.env.version
- set +a
# просто запускаем внешний скрипт
# Run external deploy script (quiet by default; set DEBUG_DEPLOY=1 to debug)
- bash ci/prod/scripts/deploy-db.sh

View File

@@ -8,12 +8,14 @@ volumes:
mongo1_data: {}
mongo2_data: {}
mongo3_data: {}
# In-memory store for secrets/material rendered by Vault Agent (no host persistence)
vault_secrets:
driver: local
driver_opts:
type: tmpfs
device: tmpfs
o: size=32m,uid=999,gid=999,mode=0750
# In-memory config for PBM (renders from templates, no host persistence)
pbm_cfg:
driver: local
driver_opts:
@@ -28,16 +30,24 @@ services:
container_name: vault-agent-sendico
restart: unless-stopped
cap_add: ["IPC_LOCK"]
# Only static env here. AppRole creds are injected via CI at runtime.
environment:
VAULT_ADDR: ${VAULT_ADDR}
VAULT_ROLE_ID: ${VAULT_ROLE_ID} # provided only during `docker compose up`
VAULT_SECRET_ID: ${VAULT_SECRET_ID} # provided only during `docker compose up`
volumes:
- ./vault/agent.hcl:/etc/vault/agent.hcl:ro
- ./vault/templates:/etc/vault/templates:ro
- /opt/sendico/vault/sendico-db/role_id:/vault/role_id:ro
- /opt/sendico/vault/sendico-db/secret_id:/vault/secret_id:ro
- vault_secrets:/vault/secrets:rw
- pbm_cfg:/etc/backup:rw
command: sh -lc 'vault agent -config=/etc/vault/agent.hcl'
# Write AppRole creds to tmpfs, drop them from env, then exec agent
command: >
sh -lc 'set -euo pipefail; umask 077;
: "${VAULT_ROLE_ID:?}"; : "${VAULT_SECRET_ID:?}";
printf "%s" "$VAULT_ROLE_ID" > /vault/secrets/role_id;
printf "%s" "$VAULT_SECRET_ID" > /vault/secrets/secret_id;
unset VAULT_ROLE_ID VAULT_SECRET_ID;
exec vault agent -config=/etc/vault/agent.hcl'
healthcheck:
test: ["CMD-SHELL","test -s /vault/secrets/MONGO_INITDB_ROOT_USERNAME -a -s /vault/secrets/MONGO_INITDB_ROOT_PASSWORD -a -s /vault/secrets/mongo.kf -a -s /etc/backup/pbm.env -a -s /etc/backup/.u -a -s /etc/backup/.p"]
interval: 5s
@@ -120,6 +130,7 @@ services:
- vault_secrets:/vault/secrets:ro
entrypoint: |
bash -c '
set -euo pipefail
u=$(cat /vault/secrets/MONGO_INITDB_ROOT_USERNAME)
p=$(cat /vault/secrets/MONGO_INITDB_ROOT_PASSWORD)
until mongosh --quiet --host sendico_db1 --port ${MONGO_PORT} --eval "db.adminCommand({ ping: 1 })"; do
@@ -152,6 +163,7 @@ services:
- pbm_cfg:/etc/backup:ro
command: |
sh -lc '
set -euo pipefail
. /etc/backup/pbm.env
U=$$(cat /etc/backup/.u) ; P=$$(cat /etc/backup/.p)
export AWS_EC2_METADATA_DISABLED=true
@@ -171,6 +183,7 @@ services:
- pbm_cfg:/etc/backup:ro
command: |
sh -lc '
set -euo pipefail
. /etc/backup/pbm.env
U=$$(cat /etc/backup/.u) ; P=$$(cat /etc/backup/.p)
export AWS_EC2_METADATA_DISABLED=true
@@ -190,6 +203,7 @@ services:
- pbm_cfg:/etc/backup:ro
command: |
sh -lc '
set -euo pipefail
. /etc/backup/pbm.env
U=$$(cat /etc/backup/.u) ; P=$$(cat /etc/backup/.p)
export AWS_EC2_METADATA_DISABLED=true

View File

@@ -1,8 +1,10 @@
#!/usr/bin/env bash
set -Eeuo pipefail
trap 'echo "[deploy-db] failed at line $LINENO"; exit 1' ERR
set -euo pipefail
# Enable verbose mode only when DEBUG_DEPLOY=1
[[ "${DEBUG_DEPLOY:-0}" = "1" ]] && set -x
trap 'echo "[deploy-db] error at line $LINENO" >&2' ERR
# Требуем ключевые переменные уже ПРОБРОШЕННЫЕ из шага CI
# Required env (exported by CI step from .env.runtime)
: "${REMOTE_BASE:?missing REMOTE_BASE}"
: "${DB_DIR:?missing DB_DIR}"
: "${SSH_USER:?missing SSH_USER}"
@@ -10,34 +12,52 @@ trap 'echo "[deploy-db] failed at line $LINENO"; exit 1' ERR
REMOTE_DIR="${REMOTE_BASE%/}/${DB_DIR}"
REMOTE_TARGET="${SSH_USER}@${SSH_HOST}"
SSH_OPTS=(-i /root/.ssh/id_rsa -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes -vv)
echo "[local] REMOTE_DIR=${REMOTE_DIR} REMOTE_TARGET=${REMOTE_TARGET}"
# SSH options: quiet by default; add -vv in debug mode
SSH_OPTS=(
-i /root/.ssh/id_rsa
-o StrictHostKeyChecking=no
-o UserKnownHostsFile=/dev/null
-o LogLevel=ERROR
-q
)
if [[ "${DEBUG_DEPLOY:-0}" = "1" ]]; then
SSH_OPTS=("${SSH_OPTS[@]/-q/}" -vv)
fi
# Кто мы на удалёнке (оставляет след в логе CI)
ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" 'echo "[whoami] $(whoami) @ $(hostname) $(date -Is)"'
# rsync options: quiet by default; add -v in debug mode
RSYNC_FLAGS=(-az --delete)
[[ "${DEBUG_DEPLOY:-0}" = "1" ]] && RSYNC_FLAGS=(-avz --delete)
# Создание директорий и синк
# AppRole creds for Vault Agent, fetched in the previous pipeline step
VAULT_ROLE_ID="$(cat secrets/ROLE_ID)"
VAULT_SECRET_ID="$(cat secrets/SECRET_ID)"
# Create remote directory structure
ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" "mkdir -p ${REMOTE_DIR}/{compose,env}"
rsync -avz --delete -e "ssh ${SSH_OPTS[*]}" ci/prod/compose/ "$REMOTE_TARGET:${REMOTE_DIR}/compose/"
rsync -avz -e "ssh ${SSH_OPTS[*]}" ci/prod/.env.runtime "$REMOTE_TARGET:${REMOTE_DIR}/env/.env.runtime"
# Контрольная сумма — доказательство доставки
local_sum="$(sha256sum ci/prod/compose/db.yml | awk '{print $1}')"
remote_sum="$(ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" "sha256sum ${REMOTE_DIR}/compose/db.yml | awk '{print \$1}'")"
echo "[sha256] local=$local_sum remote=$remote_sum"
# Sync compose bundle and runtime env to the remote host
rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" ci/prod/compose/ "$REMOTE_TARGET:${REMOTE_DIR}/compose/"
rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" ci/prod/.env.runtime "$REMOTE_TARGET:${REMOTE_DIR}/env/.env.runtime"
# Деплой на удалёнке (ВАЖНО: 'bash -s', чтобы heredoc выполнился)
ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" REMOTE_DIR="$REMOTE_DIR" bash -s <<'EOSSH'
set -Eeuxo pipefail
echo "[remote] whoami=$(whoami) host=$(hostname) pwd=$PWD"
# Deploy on remote: export AppRole creds ONLY to the compose commands.
# The vault-agent container will read them, write to tmpfs, unset, and exec.
ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" \
REMOTE_DIR="$REMOTE_DIR" \
VAULT_ROLE_ID="$VAULT_ROLE_ID" \
VAULT_SECRET_ID="$VAULT_SECRET_ID" \
bash -s <<'EOSSH'
set -euo pipefail
cd "${REMOTE_DIR}/compose"
set -a
. ../env/.env.runtime
set +a
docker compose -f db.yml pull
docker compose -f db.yml up -d --remove-orphans
docker compose ps
date -Is | tee .last_deploy
set -a; . ../env/.env.runtime; set +a
# Pull and start with ephemeral AppRole env present only for these invocations
VAULT_ROLE_ID="${VAULT_ROLE_ID}" VAULT_SECRET_ID="${VAULT_SECRET_ID}" docker compose -f db.yml pull --quiet 2>/dev/null || \
VAULT_ROLE_ID="${VAULT_ROLE_ID}" VAULT_SECRET_ID="${VAULT_SECRET_ID}" docker compose -f db.yml pull
VAULT_ROLE_ID="${VAULT_ROLE_ID}" VAULT_SECRET_ID="${VAULT_SECRET_ID}" docker compose -f db.yml up -d --remove-orphans
docker compose -f db.yml ps
date -Is > .last_deploy
logger -t deploy-db "db deployed at $(date -Is) in ${REMOTE_DIR}"
EOSSH