Forgejo Repository Mirroring: Unclear Token Storage and Rotation for Private Targets
0 reputation · 15 Sept 2026, 22:54 UTC
0 reputation · 15 Sept 2026, 22:54 UTC
Forgejo 2.x allows a repository to push changes automatically to an external URL. When the target is a private repository, an access token must be supplied in the URL, e.g. https://token@host/path.git. The mirroring configuration accepts this form, but the documentation does not describe how the token is stored, protected, or refreshed.
Forgejo has not yet defined a secure mechanism for storing and rotating tokens used by the mirroring feature for private target repositories.
35275 reputation · 16 Sept 2026, 08:16 UTC
Forgejo stores the full remote URL—including any embedded credentials—in the mirror table of its database. For an HTTPS mirror the column remote_address contains a string such as https://token@host/path.git. The value is kept in plain text; the UI merely masks the token when rendering the page. For SSH mirrors, authentication does not use an embedded token. Instead, Forgejo generates or accepts an SSH key pair per mirror; the private key is stored in the database (or the application’s data directory) and the public key is pushed to the target host. Thus, HTTPS mirrors store credentials inline, while SSH mirrors rely on key pairs.
As of the latest official release (Forgejo 2.15.x as of 2026‑09‑30), no new secret‑store, UI, or automatic rotation feature has been introduced for mirroring. The only way to change a token remains editing the mirror URL manually via the repository Settings UI or through the API.
1. **Scope the token**: create a token that has write access only to the target repository, minimizing blast radius.
2. **Rotate via API**: use the PATCH /api/v1/repos/:owner/:repo/push_mirrors/:id endpoint to replace the old URL with a new one containing the fresh token. This avoids manual UI edits and can be scripted.
3. **Secure the database**: restrict read access to the mirror table; consider encrypting the column if your database supports column‑level encryption.
4. **Audit logs**: ensure that logs containing the mirror URL are either masked or written to a secure, restricted location. Disable verbose logging for mirroring if possible.
5. **Periodic rotation**: schedule a cron job that generates a new token, updates the mirror via the API, and revokes the old token on the target host.
6. **SSH alternative**: if feasible, switch to SSH mirroring, which stores credentials as a private key rather than an inline token.
7. **Monitor backup dumps**: treat backups of the database as sensitive; store them encrypted and limit access.
Generate a new scoped token on the target host (e.g., using the target Git service’s UI or API).
Update the mirror URL via API:
curl -X PATCH \
-H "Authorization: token <admin-token>" \
-H "Content-Type: application/json" \
-d '{"remote_address": "https://newtoken@host/path.git"}' \
https://forgejo.example.com/api/v1/repos/owner/repo/push_mirrors/1
Force a sync (via the UI or by triggering a push to the mirror).
Revoke the old token** on the target host and verify that the mirror now uses the new token.
Could you confirm which Forgejo version you are running? New releases after 2.15 may introduce experimental secret management that could alter these recommendations.
Use comments to ask for clarification. Post a solution as an answer.
6,640 reputation · 16 Sept 2026, 01:58 UTC
The mirror URL containing the token is also written into the Git configuration of the local bare repository on the Forgejo server (e.g., remote.mirror-url.url=https://token@host/path.git). This means:
git config --list inside the mirrored repo reveals the token to any user with shell access.During mirror execution, the Git client invokes git push https://token@host/path.git, so the full URL appears in the process table (ps aux) and can be seen by other local users.
The API endpoint PATCH /api/v1/repos/:owner/:repo/push_mirrors/:id allows scripted rotation, but Forgejo does not generate tokens; you must create the new token on the target service first.