audit fixes: error handling, docs consistency, bucket idempotency
Provision server / provision (push) Successful in 12s
Provision server / provision (push) Successful in 12s
This commit is contained in:
+8
-1
@@ -31,6 +31,11 @@ The `bws` CLI fetches secrets using machine account access tokens.
|
||||
- `hantim-deploy-ssh-public-key` -- deploy user SSH public key
|
||||
- `hantim-new-app-script` -- Gitea API token for creating repos
|
||||
- `hantim-vultr-api-key` -- Vultr API key for DNS management
|
||||
- `hantim-garage-rpc-secret` -- Garage cluster RPC secret
|
||||
- `hantim-garage-argento-node-id` -- Argento's Garage node ID + address
|
||||
- `hantim-garage-admin-token` -- Garage admin API token
|
||||
- `hantim-garage-media-key-id` -- S3 access key ID for media uploads
|
||||
- `hantim-garage-media-secret-key` -- S3 secret key for media uploads
|
||||
|
||||
Machine accounts:
|
||||
- `hantim-server` -- used by `bootstrap.sh` and `configure.sh` on the server
|
||||
@@ -69,7 +74,9 @@ docker/nginx/ # nginx reverse proxy
|
||||
docker/<domain>/compose.yml # Per-app compose files
|
||||
docker/garage/ # Garage S3-compatible object storage
|
||||
compose.yml
|
||||
garage.toml.template
|
||||
Dockerfile
|
||||
garage.toml
|
||||
.env.keys
|
||||
.gitea/workflows/deploy-<app>.yml # Per-app deploy workflows
|
||||
.gitea/workflows/provision.yml # Triggers configure.sh on setup changes
|
||||
```
|
||||
|
||||
@@ -38,6 +38,11 @@ Bitwarden Secrets Manager (project: `hantim`, fetched via `bws` CLI):
|
||||
- `hantim-deploy-ssh-public-key` -- deploy user SSH public key (used by bootstrap.sh)
|
||||
- `hantim-new-app-script` -- Gitea API token for creating repos (used by new-app.sh)
|
||||
- `hantim-vultr-api-key` -- Vultr API key for DNS management (used by new-app.sh)
|
||||
- `hantim-garage-rpc-secret` -- Garage cluster RPC secret (used by deploy.sh)
|
||||
- `hantim-garage-argento-node-id` -- Argento's Garage node ID + address (used by deploy.sh)
|
||||
- `hantim-garage-admin-token` -- Garage admin API token (used by deploy.sh, new-app.sh)
|
||||
- `hantim-garage-media-key-id` -- S3 access key ID for media uploads (used by new-app.sh)
|
||||
- `hantim-garage-media-secret-key` -- S3 secret key for media uploads (used by aws CLI)
|
||||
|
||||
Machine accounts:
|
||||
- `hantim-server` -- access token stored at `/etc/bws-token` on hantim
|
||||
|
||||
@@ -136,8 +136,9 @@ Media files (images, videos) are stored in Garage and served via nginx at
|
||||
|
||||
```bash
|
||||
# Configure AWS CLI (one-time)
|
||||
aws configure # key ID, secret key from bws, region: garage
|
||||
aws configure set default.endpoint_url https://s3.hantim.net
|
||||
aws configure # key ID, secret key from bws, region: garage, output: json
|
||||
# Then add to ~/.aws/config under [default]:
|
||||
# endpoint_url = https://s3.hantim.net
|
||||
|
||||
# Upload files
|
||||
aws s3 cp photo.jpg s3://example.com/photo.jpg
|
||||
|
||||
+17
-2
@@ -1,5 +1,19 @@
|
||||
# Use Cases
|
||||
|
||||
## 0. Initial Bitwarden Secrets Manager setup
|
||||
|
||||
One-time setup before anything else works. Create the secrets project and
|
||||
machine accounts in Bitwarden Secrets Manager.
|
||||
|
||||
1. Create a Bitwarden organization (free tier)
|
||||
2. Enable Secrets Manager for the organization
|
||||
3. Create a project named `hantim`
|
||||
4. Create all required secrets (see README.md Secrets table for the full list)
|
||||
5. Create machine accounts:
|
||||
- `hantim-server` — grant access to the `hantim` project
|
||||
- `hantim-ci` — reserved for future CI use
|
||||
6. Generate access tokens for each machine account
|
||||
|
||||
## 1. Provision a new server
|
||||
|
||||
Set up a fresh Rocky Linux 9 server from scratch.
|
||||
@@ -157,8 +171,9 @@ on each domain.
|
||||
|
||||
```bash
|
||||
# One-time setup
|
||||
aws configure # key ID + secret from bws, region: garage
|
||||
aws configure set default.endpoint_url https://s3.hantim.net
|
||||
aws configure # key ID + secret from bws, region: garage, output: json
|
||||
# Then add to ~/.aws/config under [default]:
|
||||
# endpoint_url = https://s3.hantim.net
|
||||
|
||||
# Upload a single file
|
||||
aws s3 cp photo.jpg s3://example.com/photo.jpg
|
||||
|
||||
@@ -18,8 +18,8 @@ BWS_TOKEN_FILE="/etc/bws-token"
|
||||
BWS_VERSION="2.0.0"
|
||||
|
||||
# Secret IDs (Bitwarden Secrets Manager)
|
||||
SECRET_REGISTRY_TOKEN="43f4a77d-7bb4-49ad-8044-b411016c5c6e"
|
||||
SECRET_DEPLOY_PUBKEY="945760dd-90e2-46e5-98ad-b411016e3f3b"
|
||||
SECRET_REGISTRY_TOKEN="43f4a77d-7bb4-49ad-8044-b411016c5c6e" # hantim-ci-registry-push
|
||||
SECRET_DEPLOY_PUBKEY="945760dd-90e2-46e5-98ad-b411016e3f3b" # hantim-deploy-ssh-public-key
|
||||
|
||||
# --- Prerequisites ---
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
|
||||
if systemctl list-unit-files certbot-renew.timer 2>/dev/null | grep -q certbot-renew; then
|
||||
systemctl enable --now certbot-renew.timer
|
||||
else
|
||||
echo "0 3 * * * root certbot renew --quiet" > /etc/cron.d/certbot-renew
|
||||
echo "0 3 * * * root certbot renew --quiet --deploy-hook 'docker exec nginx nginx -s reload' 2>&1 | logger -t certbot" > /etc/cron.d/certbot-renew
|
||||
fi
|
||||
|
||||
echo "==> Deploying services..."
|
||||
|
||||
+5
-2
@@ -23,11 +23,13 @@ server {
|
||||
}
|
||||
NGINXCONF
|
||||
|
||||
trap 'rm -f "/opt/hantim/docker/nginx/conf.d/$APP.conf"' EXIT
|
||||
docker exec nginx nginx -t && docker exec nginx nginx -s reload
|
||||
certbot certonly --webroot -w /opt/hantim/docker/nginx/certbot/www \
|
||||
--non-interactive --agree-tos --register-unsafely-without-email \
|
||||
--cert-name "$APP" -d "$APP" -d "www.$APP"
|
||||
rm -f "/opt/hantim/docker/nginx/conf.d/$APP.conf"
|
||||
trap - EXIT
|
||||
echo "Certificate issued for $APP"
|
||||
exit 0
|
||||
fi
|
||||
@@ -59,8 +61,9 @@ if [ -f "docker/$APP/.env.keys" ] && [ -f /etc/bws-token ]; then
|
||||
secret_key="${line#*=}"
|
||||
value=$(bws secret list | jq -r --arg key "$secret_key" '.[] | select(.key == $key) | .value')
|
||||
if [ -z "$value" ]; then
|
||||
echo "WARNING: Secret '$secret_key' not found in bws, skipping."
|
||||
continue
|
||||
echo "ERROR: Secret '$secret_key' not found in bws."
|
||||
echo " Check that the secret exists and the machine account has access."
|
||||
exit 1
|
||||
fi
|
||||
env_content="${env_content}${var_name}=${value}"$'\n'
|
||||
done < "docker/$APP/.env.keys"
|
||||
|
||||
+20
-3
@@ -204,6 +204,15 @@ done
|
||||
|
||||
# --- Create Gitea repo from template ---
|
||||
|
||||
REPO_CHECK=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
"$GITEA_URL/api/v1/repos/$GITEA_ORG/$APP" \
|
||||
-H "Authorization: token $GITEA_TOKEN")
|
||||
if [ "$REPO_CHECK" = "200" ]; then
|
||||
echo "Error: Gitea repo $GITEA_ORG/$APP already exists."
|
||||
echo " $GITEA_URL/$GITEA_ORG/$APP"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Creating Gitea repo $GITEA_ORG/$APP from template $TEMPLATE_REPO..."
|
||||
HTTP_CODE=$(curl -s -o /tmp/new-app-response.json -w "%{http_code}" \
|
||||
-X POST "$GITEA_URL/api/v1/repos/$GITEA_ORG/$TEMPLATE_REPO/generate" \
|
||||
@@ -343,9 +352,17 @@ BUCKET_RESP=$(curl -s -X POST "$GARAGE_API/v2/CreateBucket" \
|
||||
|
||||
BUCKET_ID=$(echo "$BUCKET_RESP" | jq -r '.id')
|
||||
if [ -z "$BUCKET_ID" ] || [ "$BUCKET_ID" = "null" ]; then
|
||||
echo "Error: Failed to create bucket"
|
||||
echo "$BUCKET_RESP"
|
||||
exit 1
|
||||
# Bucket may already exist — try to look it up
|
||||
BUCKET_ID=$(curl -s -X POST "$GARAGE_API/v2/GetBucketInfo" \
|
||||
-H "$GARAGE_AUTH" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"globalAlias\": \"$APP\"}" | jq -r '.id')
|
||||
if [ -z "$BUCKET_ID" ] || [ "$BUCKET_ID" = "null" ]; then
|
||||
echo "Error: Failed to create or find bucket for $APP"
|
||||
echo "$BUCKET_RESP"
|
||||
exit 1
|
||||
fi
|
||||
echo " Bucket already exists, continuing..."
|
||||
fi
|
||||
|
||||
echo " Allowing media-key access..."
|
||||
|
||||
Reference in New Issue
Block a user