Use Debian SSH key for Gitea deploys
Homelab Main / validate-and-deploy (push) Failing after 18s Details

This commit is contained in:
juvdiaz 2026-05-27 14:07:50 -06:00
parent cc657fad6c
commit c470e64070
3 changed files with 165 additions and 3 deletions

View File

@ -174,9 +174,11 @@ jobs:
deploy_dir="${HOMELAB_DEPLOY_DIR:-/home/jv/my-homelab-configs}"
test -d "${deploy_dir}/.git"
git -C "${deploy_dir}" remote set-url gitea https://lab2025.duckdns.org/git/jv/my-homelab-configs.git || \
git -C "${deploy_dir}" remote add gitea https://lab2025.duckdns.org/git/jv/my-homelab-configs.git
git -C "${deploy_dir}" fetch gitea main
gitea_ssh_url="${GITEA_SSH_URL:-ssh://git@192.168.100.89:32222/jv/my-homelab-configs.git}"
gitea_ssh_command="${GITEA_SSH_COMMAND:-ssh -i /home/jv/.ssh/id_ed25519 -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new}"
git -C "${deploy_dir}" remote set-url gitea "${gitea_ssh_url}" || \
git -C "${deploy_dir}" remote add gitea "${gitea_ssh_url}"
GIT_SSH_COMMAND="${gitea_ssh_command}" git -C "${deploy_dir}" fetch gitea main
git -C "${deploy_dir}" checkout main
git -C "${deploy_dir}" reset --hard "${{ gitea.sha }}"
git -C "${deploy_dir}" remote set-url local-bootstrap /home/jv/git-server/my-homelab-configs.git || \

View File

@ -414,6 +414,14 @@ changes still require a manual Debian run. Lower-risk app changes proceed to
`./lab.sh apps` after validation passes, which skips Gitea, Pimox, cluster,
platform, and edge changes.
`./lab.sh bootstrap-gitea-repo` also registers the Debian host SSH public key
with the Gitea repository and switches the Debian working copy's `gitea` remote
to `ssh://git@192.168.100.89:32222/jv/my-homelab-configs.git`. The default key
is `/home/jv/.ssh/id_ed25519.pub`; set `LAB_GITEA_REPO_SSH_KEY_PATH` to use a
different Debian-host key, or `LAB_GITEA_REPO_SSH_BOOTSTRAP=false` to leave SSH
access unchanged. The Actions deploy job fetches the persistent Debian checkout
through that SSH endpoint.
Enable Actions for the repository in Gitea, then create a repository-level runner
token from:

152
lab.sh
View File

@ -1447,6 +1447,136 @@ PY
"${api_base}/user/repos" >/dev/null
}
gitea_public_key_registered() {
local api_base="$1"
local auth_user="$2"
local auth_password="$3"
local owner="$4"
local repo_name="$5"
local public_key_path="$6"
local repo_keys
local user_keys
user_keys="$(curl -fsS -u "${auth_user}:${auth_password}" "${api_base}/user/keys?limit=100")"
repo_keys="$(curl -fsS -u "${auth_user}:${auth_password}" "${api_base}/repos/${owner}/${repo_name}/keys?limit=100")"
GITEA_PUBLIC_KEY="$(<"${public_key_path}")" \
GITEA_USER_KEYS="${user_keys}" \
GITEA_REPO_KEYS="${repo_keys}" \
python3 - <<'PY'
import json
import os
import sys
public_key = os.environ["GITEA_PUBLIC_KEY"].strip()
for env_name in ("GITEA_USER_KEYS", "GITEA_REPO_KEYS"):
for key in json.loads(os.environ[env_name]) or []:
if key.get("key", "").strip() == public_key:
sys.exit(0)
sys.exit(1)
PY
}
create_gitea_repo_deploy_key() {
local api_base="$1"
local auth_user="$2"
local auth_password="$3"
local owner="$4"
local repo_name="$5"
local title="$6"
local public_key_path="$7"
local read_only="$8"
local payload
payload="$(
GITEA_DEPLOY_KEY_TITLE="${title}" \
GITEA_PUBLIC_KEY="$(<"${public_key_path}")" \
GITEA_DEPLOY_KEY_READ_ONLY="${read_only}" \
python3 - <<'PY'
import json
import os
print(json.dumps({
"title": os.environ["GITEA_DEPLOY_KEY_TITLE"],
"key": os.environ["GITEA_PUBLIC_KEY"].strip(),
"read_only": os.environ["GITEA_DEPLOY_KEY_READ_ONLY"] == "true",
}))
PY
)"
curl -fsS \
-u "${auth_user}:${auth_password}" \
-H "Content-Type: application/json" \
-X POST \
-d "${payload}" \
"${api_base}/repos/${owner}/${repo_name}/keys" >/dev/null
}
ensure_gitea_repo_ssh_access() {
local api_base="$1"
local auth_user="$2"
local auth_password="$3"
local owner="$4"
local repo_name="$5"
local ssh_host="$6"
local ssh_port="$7"
local key_path="$8"
local key_title="$9"
local key_read_only="${10}"
local key_dir
local known_hosts
local public_key_path
local read_only_json="false"
local ssh_repo_url
if [[ "${key_path}" =~ [[:space:]] || "${key_path}" == *"'"* ]]; then
echo "LAB_GITEA_REPO_SSH_KEY_PATH cannot contain whitespace or single quotes." >&2
exit 1
fi
key_dir="$(dirname "${key_path}")"
public_key_path="${key_path}.pub"
mkdir -p "${key_dir}"
chmod 0700 "${key_dir}"
if [[ ! -s "${key_path}" && ! -s "${public_key_path}" ]]; then
ssh-keygen -t ed25519 -N "" -f "${key_path}" -C "${key_title}" >/dev/null
elif [[ -s "${key_path}" && ! -s "${public_key_path}" ]]; then
ssh-keygen -y -f "${key_path}" >"${public_key_path}"
elif [[ ! -s "${key_path}" ]]; then
echo "Public key ${public_key_path} exists, but private key ${key_path} is missing." >&2
exit 1
fi
chmod 0600 "${key_path}"
chmod 0644 "${public_key_path}"
if truthy "${key_read_only}"; then
read_only_json="true"
fi
if gitea_public_key_registered "${api_base}" "${auth_user}" "${auth_password}" "${owner}" "${repo_name}" "${public_key_path}"; then
echo "Gitea already has Debian host SSH key ${public_key_path}."
else
create_gitea_repo_deploy_key "${api_base}" "${auth_user}" "${auth_password}" "${owner}" "${repo_name}" "${key_title}" "${public_key_path}" "${read_only_json}"
echo "Added Debian host SSH key ${public_key_path} to ${owner}/${repo_name}."
fi
known_hosts="${HOME}/.ssh/known_hosts"
touch "${known_hosts}"
chmod 0644 "${known_hosts}"
if ! ssh-keygen -F "[${ssh_host}]:${ssh_port}" -f "${known_hosts}" >/dev/null 2>&1; then
ssh-keyscan -p "${ssh_port}" "${ssh_host}" >>"${known_hosts}" 2>/dev/null
fi
ssh_repo_url="ssh://git@${ssh_host}:${ssh_port}/${owner}/${repo_name}.git"
git -C "${REPO_ROOT}" remote set-url gitea "${ssh_repo_url}" 2>/dev/null ||
git -C "${REPO_ROOT}" remote add gitea "${ssh_repo_url}"
git -C "${REPO_ROOT}" config core.sshCommand "ssh -i ${key_path} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new"
git -C "${REPO_ROOT}" ls-remote gitea HEAD >/dev/null
echo "Gitea SSH remote: ${ssh_repo_url}"
}
bootstrap_gitea_repo() {
local mode="${LAB_GITEA_REPO_BOOTSTRAP:-true}"
local gitea_host="${LAB_GITEA_HOST:-${LAB_RASPBERRY_HOST:-192.168.100.89}}"
@ -1454,6 +1584,7 @@ bootstrap_gitea_repo() {
local gitea_key="${LAB_GITEA_SSH_KEY_PATH:-${LAB_RASPBERRY_SSH_KEY_PATH:-/home/jv/.ssh/id_ed25519}}"
local container_name="${LAB_GITEA_CONTAINER_NAME:-homelab-gitea}"
local http_port="${LAB_GITEA_HTTP_PORT:-3000}"
local ssh_port="${LAB_GITEA_SSH_PORT:-32222}"
local root_url="${LAB_GITEA_ROOT_URL:-https://lab2025.duckdns.org/git/}"
local repo_owner="${LAB_GITEA_REPO_OWNER:-jv}"
local repo_name="${LAB_GITEA_REPO_NAME:-my-homelab-configs}"
@ -1463,6 +1594,10 @@ bootstrap_gitea_repo() {
local credentials_file="${LAB_GITEA_BOOTSTRAP_CREDENTIALS_FILE:-${HOME}/.config/homelab/gitea-bootstrap.env}"
local bootstrap_password="${LAB_GITEA_BOOTSTRAP_PASSWORD:-}"
local allow_dirty="${LAB_GITEA_BOOTSTRAP_ALLOW_DIRTY:-false}"
local ssh_bootstrap="${LAB_GITEA_REPO_SSH_BOOTSTRAP:-true}"
local ssh_key_path="${LAB_GITEA_REPO_SSH_KEY_PATH:-/home/jv/.ssh/id_ed25519}"
local ssh_key_title="${LAB_GITEA_REPO_DEPLOY_KEY_TITLE:-debian-host-${repo_name}}"
local ssh_key_read_only="${LAB_GITEA_REPO_DEPLOY_KEY_READ_ONLY:-false}"
local api_base
local public_repo_url
local direct_repo_url
@ -1490,6 +1625,10 @@ bootstrap_gitea_repo() {
echo "LAB_GITEA_BOOTSTRAP_EMAIL cannot contain a single quote." >&2
exit 1
fi
if ! [[ "${ssh_port}" =~ ^[0-9]+$ ]]; then
echo "LAB_GITEA_SSH_PORT must be numeric." >&2
exit 1
fi
if [[ -z "${bootstrap_password}" && -r "${credentials_file}" ]]; then
# shellcheck disable=SC1090
@ -1625,6 +1764,19 @@ ASKPASS_EOT
remote_status="$(git -C "${REPO_ROOT}" remote get-url gitea)"
echo "Gitea remote: ${remote_status}"
if ! disabled_value "${ssh_bootstrap}"; then
ensure_gitea_repo_ssh_access \
"${api_base}" \
"${bootstrap_user}" \
"${bootstrap_password}" \
"${repo_owner}" \
"${repo_name}" \
"${gitea_host}" \
"${ssh_port}" \
"${ssh_key_path}" \
"${ssh_key_title}" \
"${ssh_key_read_only}"
fi
}
install_gitea_backup_timer() {