authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-22 01:54:20-08:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-22 23:36:21-08:00
loge402d6ef71dfc310caeeb4d3579bddd9d0710594
tree5f1900497cf85c0c5a0f8bd4626576a23e207549
parentc94f61522e07ad2783764708af19703355c3e999
signaturelock-open Commit is signed but in an unrecognized format.

feat: ssh auth for git services

to avoid having a separate user like `ssh evil-git@git.evil.inc`, instead the gambit is to have two intercept scripts to perform routing based on the repo that is being pushed to.

9 files changed, 119 insertions(+), 28 deletions(-)

compose.yaml+2-2
......@@ -152,7 +152,6 @@ services:
152152 image: code.forgejo.org/forgejo/forgejo:13
153153 entrypoint: ["/bin/sh", "/custom/init/entry.sh"]
154154 ports:
155 - 40243:22
156155 - 40244:3000 # port used for forgejo-runner
157156 environment:
158157 USER_UID: "$USER_ID"
......@@ -167,6 +166,7 @@ services:
167166 FORGEJO_OAUTH2_JWT_SECRET: "${FORGEJO_OAUTH2_JWT_SECRET:?}"
168167 FORGEJO_SECURITY_SECRET_KEY: "${FORGEJO_SECURITY_SECRET_KEY:?}"
169168 FORGEJO_SECURITY_INTERNAL_TOKEN: "${FORGEJO_SECURITY_INTERNAL_TOKEN:?}"
169 HOME_INFRA_HOST_DIR: "${HOME_INFRA_HOST_DIR:?}"
170170 restart: unless-stopped
171171 volumes:
172172 - "${APP_ROOT}/keycloak:/shared-keys:ro"
......@@ -590,7 +590,6 @@ services:
590590 image: code.forgejo.org/forgejo/forgejo:13
591591 entrypoint: ["/bin/sh", "/custom/init/entry.sh"]
592592 ports:
593 - 22666:22
594593 - 40666:3000 # port used for forgejo-runner
595594 environment:
596595 USER_UID: "$USER_ID"
......@@ -604,6 +603,7 @@ services:
604603 FORGEJO_SECURITY_SECRET_KEY: "${EVIL_FORGEJO_SECURITY_SECRET_KEY}"
605604 FORGEJO_SECURITY_INTERNAL_TOKEN: "${EVIL_FORGEJO_SECURITY_INTERNAL_TOKEN}"
606605 POSTGRES_PASSWORD_EVIL_FORGEJO: "${POSTGRES_PASSWORD_EVIL_FORGEJO:?}"
606 HOME_INFRA_HOST_DIR: "${HOME_INFRA_HOST_DIR:?}"
607607 restart: unless-stopped
608608 volumes:
609609 - "${APP_ROOT}/evil-infra/forgejo:/data:rw"
config/evil-infra/forgejo/init/app.ini+3-3
......@@ -31,13 +31,13 @@ DOMAIN=git.evil.inc
3131SSH_DOMAIN=git.evil.inc
3232HTTP_PORT=3000
3333ROOT_URL=https://git.evil.inc
34DISABLE_SSH=false
35SSH_PORT=22
36SSH_LISTEN_PORT=22
3734LFS_JWT_SECRET=$FORGEJO_SERVER_LFS_JWT_SECRET
3835LFS_START_SERVER=true
3936OFFLINE_MODE=true
4037LANDING_PAGE=/evil
38START_SSH_SERVER=false
39SSH_CREATE_AUTHORIZED_KEYS_FILE=false
40SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE=$HOME_INFRA_HOST_DIR/config/forgejo/ssh/route.sh {{.Key.ID}}
4141
4242[database]
4343DB_TYPE=postgres
config/forgejo/init/app.ini+3-3
......@@ -32,13 +32,13 @@ DOMAIN=git.$HOME_DOMAIN
3232SSH_DOMAIN=git.$HOME_DOMAIN
3333HTTP_PORT=3000
3434ROOT_URL=https://git.$HOME_DOMAIN/
35DISABLE_SSH=false
36SSH_PORT=22
37SSH_LISTEN_PORT=22
3835LFS_JWT_SECRET=$FORGEJO_SERVER_LFS_JWT_SECRET
3936LFS_START_SERVER=true
4037OFFLINE_MODE=true
4138LANDING_PAGE=/clo
39START_SSH_SERVER=false
40SSH_CREATE_AUTHORIZED_KEYS_FILE=false
41SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE=$HOME_INFRA_HOST_DIR/config/forgejo/ssh/route.sh {{.Key.ID}}
4242
4343[database]
4444DB_TYPE=postgres
config/forgejo/ssh/config created+3
......@@ -0,0 +1,3 @@
1Match User git
2 AuthorizedKeysCommand /bin/python /mnt/storage1/apps/home-infra/config/forgejo/ssh/keys.py %u %t %k
3 AuthorizedKeysCommandUser git
config/forgejo/ssh/keys.py created+57
......@@ -0,0 +1,57 @@
1#!/usr/bin/env python3
2import sys
3import subprocess
4import re
5
6user, key_type, key = sys.argv[1:4]
7
8
9def get_key_id(container: str):
10 try:
11 result = subprocess.run(
12 [
13 "sudo",
14 "/usr/bin/docker",
15 "exec",
16 "-u",
17 "3000:3000",
18 container,
19 "/usr/local/bin/forgejo",
20 "keys",
21 "-c",
22 "/custom/conf/app.ini",
23 "-e",
24 "git",
25 "-u",
26 user,
27 "-t",
28 key_type,
29 "-k",
30 key,
31 ],
32 capture_output=True,
33 text=True,
34 check=False,
35 )
36 match = re.search(r"route\.sh (\d+)", result.stdout)
37 return match.group(1) if match else None
38 except:
39 return None
40
41
42clover_id = get_key_id("forgejo")
43evil_id = get_key_id("evil-forgejo")
44
45if not clover_id and not evil_id:
46 sys.exit(0)
47
48args: list[str] = []
49if clover_id:
50 args.extend(["--clover", clover_id])
51if evil_id:
52 args.extend(["--evil", evil_id])
53
54cmd = "/mnt/storage1/apps/home-infra/config/forgejo/ssh/route.sh " + " ".join(args)
55print(
56 f'command="{cmd}",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict {key_type} {key}'
57)
config/forgejo/ssh/route.sh created+48
......@@ -0,0 +1,48 @@
1#!/bin/sh
2clover_id=""
3evil_id=""
4
5while [ $# -gt 0 ]; do
6 case "$1" in
7 --clover)
8 clover_id="$2"
9 shift 2
10 ;;
11 --evil)
12 evil_id="$2"
13 shift 2
14 ;;
15 *) shift ;;
16 esac
17done
18
19repo="${SSH_ORIGINAL_COMMAND#* }"
20repo="${repo%\'*}"
21repo="${repo#\'}"
22repo="${repo#/}"
23
24case "$repo" in
25*[!a-zA-Z0-9/_.-]*)
26 echo "invalid repo name: $repo" >&2
27 exit 1
28 ;;
29*..*)
30 echo "invalid repo name: $repo" >&2
31 exit 1
32 ;;
33/*)
34 echo "invalid repo name: $repo" >&2
35 exit 1
36 ;;
37esac
38
39if [ -d "/mnt/storage1/apps/forgejo/git/repositories/${repo}" ]; then
40 [ -z "$clover_id" ] && echo "ssh key is not configured for repository on git.paperclover.net" >&2 && exit 1
41 exec sudo docker exec -i -u git forgejo /usr/bin/env SSH_ORIGINAL_COMMAND="$SSH_ORIGINAL_COMMAND" /usr/local/bin/forgejo --config=/custom/conf/app.ini serv key-"$clover_id"
42elif [ -d "/mnt/storage1/apps/evil-infra/forgejo/git/repositories/${repo}" ]; then
43 [ -z "$evil_id" ] && echo "ssh key is not configured for repository on git.evil.inc" >&2 && exit 1
44 exec sudo docker exec -i -u git evil-forgejo /usr/bin/env SSH_ORIGINAL_COMMAND="$SSH_ORIGINAL_COMMAND" /usr/local/bin/forgejo --config=/custom/conf/app.ini serv key-"$evil_id"
45else
46 echo "repo not found" >&2
47 exit 1
48fi
docker.sh created+2
......@@ -0,0 +1,2 @@
1#!/bin/sh
2ssh clo@192.168.0.1 -- cd /mnt/storage1/apps/home-infra \&\& sudo docker "$@"
generate-env.sh+1
......@@ -10,6 +10,7 @@ fi
1010printf "generating...\n"
1111
1212template() {
13 add "HOME_INFRA_HOST_DIR" "$PWD"
1314 add "HOME_DOMAIN" "local.test"
1415 add "ADMIN_EMAIL" ""
1516 add "USER_ID" "$(id -u)"
readme.md-20
......@@ -3,26 +3,6 @@
33this codebase is used for all docker services on my home nas, including personal
44customization.
55
6## usage
7
8```
9git clone https://git.paperclover.net/clo/home-infra.git
10cd home-infra
11
12# setup repo
13rm config/font/*
14git clone https://git.paperclover.net/clo/sitegen.git
15cp .env.example .env
16
17# fill secrets
18vi .env
19vi secrets/openvpn-credentials.txt
20
21docker compose up -d
22```
23
24## license
25
266this repository is for example usage only. you may not re-host it in full,
277privately or publicly, but you may learn from my usage. please note that some
288components here are under licenses which do not permit redistribution.