Files
hermes-skills/devops/rke2-cluster-administration/references/hermes-git-remote-cutover-2026-07.md
T
Debian 01bd921ced feat: add home-assistant-dashboard-conventions skill + update multiple skills
- New: smart-home/home-assistant-dashboard-conventions (Mushroom cards, view tabs, no Bubble Cards)
- Updated: rke2, ceph, galera, proxmox, brainstorming, compound-learning, 1password-cli, smart-home-automation skills
- New references: ceph-cluster-administration, docker-volume-forensics, ceph-crush-weight, ceph-ec-mixed-size
2026-07-14 18:35:16 +00:00

156 lines
6.8 KiB
Markdown

# Hermes Host Git Remote Cutover to K8s Gitea — 2026-07-14
## Context
After ArgoCD self-management cutover (Section 4.3e), the Hermes host's
own `iac-homelab` git remote still pointed to CT108
(`10.0.30.105:3000`). User instructed: "nutze du intern in hermes ab
jetzt auch das k8s gitea" — switch Hermes host's git remote to K8s Gitea.
## Key Learnings
### CoreDNS Is K8s-Internal Only
The CoreDNS `hosts` override (Section 4.3d) makes `git.schoen.codes`
resolve to `10.0.30.203` **only inside K8s pods**. The Hermes host,
being outside K8s, cannot use CoreDNS. Public DNS was explicitly
rejected by the user ("Nutze für Internet Zwecke nicht den externen DNS
Resolver, sondern einen internen").
Solution: `/etc/hosts` entry on the Hermes host:
```bash
echo "10.0.30.203 git.schoen.codes" | sudo tee -a /etc/hosts
```
### Gitea API Tokens Do NOT Migrate
When repos are migrated from CT108 Gitea to K8s Gitea via the Gitea
Migration API, **API tokens are NOT copied**. Tokens are stored hashed
in the database and are instance-specific. The old token
(`07efa...`) from CT108 returns `401 Unauthorized` on K8s Gitea.
New token must be generated on K8s Gitea:
```bash
# Via mgmt-runner (VM200) — Hermes host has no kubectl:
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.30.124 \
"KUBECONFIG=/root/.kube/config kubectl exec -n gitea deploy/gitea -- \
gitea admin user generate-access-token \
-u dominik -t hermes-gitops --scopes all --raw"
```
Note: The `--config /data/gitea/conf/app.ini` flag is optional —
`gitea admin user generate-access-token` auto-discovers the config when
running inside the pod. The command outputs the raw token to stdout.
### JWT ≠ Gitea API Token (Critical Distinction)
The 1Password ExternalSecrets for Gitea (`gitea-security`) contain
`internal_token`, `jwt_secret`, `secret_key`, `lfs_jwt_secret`. These
are **internal security tokens** (JWTs), NOT API access tokens.
**How to distinguish:**
- **Gitea API token**: hex string, e.g. `07efa534ea1a147fce1bf5813c669cb0ebc7b522`
- **JWT / internal security token**: three base64 parts separated by dots,
e.g. `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3ODM5NjM1NDd9.Lyr6fqc...`
A JWT passed as `Authorization: token <JWT>` or `Authorization: Bearer
<JWT>` returns `401 Unauthorized` on both old and new Gitea instances.
The `nbf` (Not Before) claim in the JWT payload identifies it as a
security token, not an access token.
**If a user provides a token that looks like a JWT (dots separating
base64 segments), it is NOT a Gitea API token.** Direct them to
Settings → Applications → Generate New Token in the Gitea web UI, or
generate one via `gitea admin user generate-access-token` (see above).
### K8s Gitea Reachability from Outside K8s
| Path | Works? | Notes |
|------|--------|-------|
| `http://10.0.30.203:3000` | ❌ Timeout | Port 3000 not exposed on VIP |
| `http://10.0.30.203` (Host: git.schoen.codes) | ✅ 200 | Traefik routes by Host header |
| `https://10.0.30.203` (Host: git.schoen.codes) | ✅ 200 | TLS via Traefik |
| `10.0.30.202:2222` (SSH) | Separate LB | Gitea SSH LoadBalancer service |
| `git.schoen.codes` (via /etc/hosts) | ✅ | Resolves to 10.0.30.203 |
### Hermes Host Has No kubectl
The Hermes host (`/home/debian`) does not have `kubectl`, `helm`, or
`kubeconfig`. All K8s operations go through SSH to mgmt-runner (VM200)
at `10.0.30.124`. See SKILL.md Section 1.2.
### ArgoCD Repo Secret Also Needs Token Update
After generating a new Gitea API token, the ArgoCD repository secret
must also be patched with the new token — otherwise ArgoCD's repo
connection breaks even though the Hermes host git remote works:
```bash
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.30.124 \
"KUBECONFIG=/root/.kube/config kubectl patch secret \
argocd-repo-k8s-gitea -n argocd --type merge \
-p '{\"data\":{\"password\":\"$(echo -n TOKEN | base64)\"}}'"
```
### Gitea Helm Chart Admin Password Extraction
The Gitea Helm Chart v12 auto-generates an admin password on first
install (unless explicitly set in values). The password is stored as
the `GITEA_ADMIN_PASSWORD` environment variable in the `configure-gitea`
init container — NOT in any K8s Secret or Helm values file.
```bash
# Extract admin password from the init container env vars:
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.30.124 \
"KUBECONFIG=/root/.kube/config kubectl get deploy gitea -n gitea \
-o jsonpath='{.spec.template.spec.initContainers[?(@.name==\"configure-gitea\")].env}'" \
| python3 -c "
import json,sys
for e in json.load(sys.stdin):
if e.get('name')=='GITEA_ADMIN_PASSWORD':
print(e['value'])
"
```
Additional env vars in the same container:
- `GITEA_ADMIN_USERNAME`: the admin username (e.g. `dominik`)
- `GITEA_ADMIN_PASSWORD_MODE`: `keepUpdated` (chart syncs password on
every sync) or `initialOnlyRequireReset`
**Pitfall:** The password is NOT in the `gitea-security` K8s Secret
(that contains JWT-based internal tokens only — see "JWT ≠ Gitea API
Token" above). It is also NOT in the Helm values `gitea.admin` section
(unless explicitly set — by default the chart generates a random one).
### Storing Credentials in 1Password
After extracting the admin password and generating an API token, store
both in 1Password vault "Hermes":
- **API Token**: Category `API Credential`, title `K8s Gitea Token
(hermes-gitops)`. Requires two-step create+edit (see 1password-cli
skill — `op item create` does not populate the `credential` field
for API Credential items).
- **Admin Login**: Category `Login`, title `K8s Gitea Admin (dominik)`.
Works in one step with `op item create --category="Login"`.
## Steps Performed (Completed)
1. Checked current remote: `git remote -v` → CT108 (`10.0.30.105:3000`)
2. Verified K8s Gitea reachable via Traefik VIP: `curl -H "Host: git.schoen.codes" http://10.0.30.203/` → 200
3. Added `/etc/hosts` entry: `10.0.30.203 git.schoen.codes`
4. Changed git remote URL to `http://dominik:TOKEN@git.schoen.codes/dominik/iac-homelab.git`
5. `git fetch origin` → `Authentication failed` (old CT108 token invalid on K8s Gitea)
6. User provided a JWT (internal security token from 1Password) — identified as NOT an API token (see pitfall above)
7. Generated new API token via mgmt-runner SSH: `gitea admin user generate-access-token -u dominik -t hermes-gitops --scopes all --raw` → `4a3fd...`
8. Updated git remote with new token: `git remote set-url origin "http://dominik:4a3fd...@git.schoen.codes/..."`
9. `git fetch origin` → ✅ success (forced update from `d9135ca` → `68adfab`)
10. `git push --dry-run origin main` → ✅ "Everything up-to-date"
11. Patched ArgoCD repo secret `argocd-repo-k8s-gitea` with new token
## Result
Hermes host git operations now use K8s Gitea exclusively. ArgoCD also
pulls from K8s Gitea. The entire GitOps loop is self-contained in K8s.
CT108 can be decommissioned (Task 10 — pending user approval).