diff --git a/.gitea/workflows/build-and-deploy.yml b/.gitea/workflows/build-and-deploy.yml index 3f1c436..ad3bda7 100644 --- a/.gitea/workflows/build-and-deploy.yml +++ b/.gitea/workflows/build-and-deploy.yml @@ -180,6 +180,9 @@ jobs: echo "::error::SSH private key is not readable by ssh-keygen: $keygen_error. Use an unencrypted private key or configure a CI-specific deploy key." exit 1 fi + ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub + key_fingerprint="$(ssh-keygen -lf ~/.ssh/id_rsa.pub | awk '{print $2}')" + echo "Loaded deploy key fingerprint: $key_fingerprint" touch ~/.ssh/known_hosts ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts 2>/dev/null || true chmod 644 ~/.ssh/known_hosts @@ -190,8 +193,10 @@ jobs: run: | VPS_HOST="${VPS_HOST:-$DEPLOY_SSH_HOST}" SSH_OPTIONS="-i $HOME/.ssh/id_rsa -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=$HOME/.ssh/known_hosts" + key_fingerprint="$(ssh-keygen -lf "$HOME/.ssh/id_rsa.pub" | awk '{print $2}')" + echo "Testing SSH authentication to $VPS_HOST with deploy key fingerprint $key_fingerprint" if ! ssh $SSH_OPTIONS "${{ secrets.VPS_USER }}@$VPS_HOST" "printf 'ssh authenticated as '; whoami"; then - echo "::error::SSH authentication failed. Verify VPS_USER matches the account that has the deploy public key in ~/.ssh/authorized_keys on $VPS_HOST." + echo "::error::SSH authentication failed. Verify VPS_USER matches the account that has deploy key fingerprint $key_fingerprint in ~/.ssh/authorized_keys on $VPS_HOST." exit 1 fi @@ -374,6 +379,9 @@ jobs: echo "::error::SSH private key is not readable by ssh-keygen: $keygen_error. Use an unencrypted private key or configure a CI-specific deploy key." exit 1 fi + ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub + key_fingerprint="$(ssh-keygen -lf ~/.ssh/id_rsa.pub | awk '{print $2}')" + echo "Loaded deploy key fingerprint: $key_fingerprint" touch ~/.ssh/known_hosts ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts 2>/dev/null || true chmod 644 ~/.ssh/known_hosts @@ -385,8 +393,10 @@ jobs: run: | VPS_HOST="${VPS_HOST:-$DEPLOY_SSH_HOST}" SSH_OPTIONS="-i $HOME/.ssh/id_rsa -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=$HOME/.ssh/known_hosts" + key_fingerprint="$(ssh-keygen -lf "$HOME/.ssh/id_rsa.pub" | awk '{print $2}')" + echo "Testing SSH authentication to $VPS_HOST with deploy key fingerprint $key_fingerprint" if ! ssh $SSH_OPTIONS "${{ secrets.VPS_USER }}@$VPS_HOST" "printf 'ssh authenticated as '; whoami"; then - echo "::error::SSH authentication failed. Verify VPS_USER matches the account that has the deploy public key in ~/.ssh/authorized_keys on $VPS_HOST." + echo "::error::SSH authentication failed. Verify VPS_USER matches the account that has deploy key fingerprint $key_fingerprint in ~/.ssh/authorized_keys on $VPS_HOST." exit 1 fi diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 2ea66e4..2623a8b 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -211,24 +211,33 @@ REGISTRY_USER= REGISTRY_PASSWORD= ``` -For the deploy job, add these GitLab CI variables as well: +For the deploy job, add these Gitea Actions secrets as well: ```text VPS_IP= VPS_USER= -SSH_PRIVATE_KEY= +VPS_SSH_KEY= ``` -`SSH_PRIVATE_KEY` must be an unencrypted private key that OpenSSH can read in a non-interactive job. The pipeline accepts any of these formats: +`VPS_SSH_KEY` must be an unencrypted private key that OpenSSH can read in a non-interactive job. The workflow also accepts `VPS_SSH_KEY_B64` when you prefer storing a base64-encoded private key as a single line. -- GitLab `File` variable containing the private key -- Standard multiline key pasted directly into the variable value -- Single-line key with literal `\n` newline escapes -- Base64-encoded private key stored as a single line +Supported private key formats: + +- Standard multiline key pasted directly into `VPS_SSH_KEY` +- Single-line key with literal `\n` newline escapes in `VPS_SSH_KEY` +- Base64-encoded private key stored as a single line in `VPS_SSH_KEY_B64` If your key is passphrase-protected, generate a dedicated deploy key without a passphrase for CI instead of reusing an interactive workstation key. -The production compose file reads `APP_IMAGE` and `APP_VERSION` for pull-based deploys. The GitLab deploy job injects those values automatically and now syncs the deployment assets to the VPS before running the server-side deploy script. Production no longer depends on `git pull` during release. +If SSH authentication fails after the workflow loads the key, compare the logged deploy key fingerprint with the key installed on the server: + +```bash +ssh-keygen -lf ~/.ssh/authorized_keys +``` + +The matching public key must be present in `~/.ssh/authorized_keys` for the account named by `VPS_USER`. + +The production compose file reads `APP_IMAGE` and `APP_VERSION` for pull-based deploys. The Gitea deploy job injects those values automatically and syncs the deployment assets to the VPS before running the server-side deploy script. Production no longer depends on `git pull` during release. The CI pipeline publishes and deploys from the GitLab default branch (`$CI_DEFAULT_BRANCH`). If you change your release branch, update the repository default branch in GitLab instead of hard-coding branch names in `.gitlab-ci.yml`.