summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@cryptonomic.net>2021-10-22 15:22:37 -0400
committerAndrew Cady <d@cryptonomic.net>2021-10-23 11:17:15 -0400
commita19dce5c18f6d6370b58aa3b9dd26550a589fc8c (patch)
treeb8625d617d8ad85e0715b36400411f53d9ad1a75
Minimal Git/SSH Rhizome
Requires Sudo access to configure OpenSSH. It would be possible to run OpenSSH on a non-default port and not require root access. That is not implemented.
-rwxr-xr-xAnonymousAccessCommand126
-rwxr-xr-xAuthorizedKeysCommand15
-rw-r--r--Makefile24
-rw-r--r--README.txt1
-rw-r--r--anonymous-access.conf10
-rw-r--r--tests.makefile34
-rw-r--r--tests.sh66
7 files changed, 276 insertions, 0 deletions
diff --git a/AnonymousAccessCommand b/AnonymousAccessCommand
new file mode 100755
index 0000000..443d25e
--- /dev/null
+++ b/AnonymousAccessCommand
@@ -0,0 +1,126 @@
1#!/bin/sh
2default_msg()
3{
4 sshfpline="$(get_sshfp_authline ${SSH_CLIENT%% *})"
5 cat <<EOF >&2
6
7 You are:
8
9 $authline
10 $sshfpline
11
12EOF
13}
14
15get_sshfp_authline()
16{
17 (
18 r=${1:-.}
19 key=$(mktemp) || exit
20 trap 'rm -rf "$key"' EXIT
21 echo "$authline" > "$key"
22 get_sshfp "$key" "$r"
23 )
24}
25
26get_sshfp()
27{
28 (
29 key="$1"
30 r="${2:-.}"
31 dns=$(mktemp) || exit
32 trap 'rm -rf "$dns"' EXIT
33
34 ssh-keygen -r "$r" -f "$key" > "$dns"
35 exec < "$dns"
36 while read line
37 do
38 set -- $line
39 if [ "$3 $5" = "SSHFP 2" ]
40 then
41 echo "$line"
42 break
43 fi
44 done
45 )
46}
47
48ssh_client_fingerprint_base16()
49{
50 set -- $(get_sshfp_authline)
51 [ "$6" ]
52 echo $6
53}
54
55check_if_self_forge()
56{
57 # TODO: don't use description, but something else.
58 local dir="$1"
59 [ -d "$dir" ] || exit
60 [ -r "$dir"/description ] || exit
61 read description < "$dir"/description
62 if [ "$description" != self-forge ] && [ "$(GIT_DIR=$dir git config core.self-forge)" != true ]
63 then
64 echo 'Error: access denied. The specified directory is not a self-forge.' >&2
65 exit
66 fi
67}
68
69read authtype authline < "$SSH_USER_AUTH" || exit
70[ "$authtype" = publickey ] || exit
71
72cmd=${SSH_ORIGINAL_COMMAND%% *}
73
74case "$cmd" in
75 git-send-pack | git-upload-pack)
76 GIT_NAMESPACE=
77 ;;
78 git-receive-pack)
79 export GIT_NAMESPACE="$(ssh_client_fingerprint_base16)"
80 [ "$GIT_NAMESPACE" ] || exit
81 ;;
82 *)
83 default_msg
84 exit
85 ;;
86esac
87
88arg=${SSH_ORIGINAL_COMMAND#* }
89arg=${arg%\'}
90arg=${arg#\'}
91case "$arg" in
92 *\'*) exit ;;
93 *.git) ;;
94 *) arg=$arg/.git ;;
95esac
96
97dir=$(readlink -e "$arg") || exit
98
99check_if_self_forge "$dir"
100
101with_allowCurrentBranch()
102{
103 local cmd="$1" dir="$2"
104 (
105 set -eC
106 lockfile=$GIT_DIR/index.lock
107 echo $$ > "$lockfile"
108 trap 'rm -f "$lockfile"' EXIT
109
110 # This doesn't seem very secure. Need to patch git probably.
111 for deny in CurrentBranch # DeleteCurrent
112 do git config receive.deny$deny false
113 done
114 "$@"
115 for deny in CurrentBranch # DeleteCurrent
116 do git config receive.deny$deny true
117 done
118 )
119}
120
121if [ "$GIT_NAMESPACE" ]
122then
123 GIT_DIR=$dir with_allowCurrentBranch "$cmd" "$dir"
124else
125 "$cmd" "$dir"
126fi
diff --git a/AuthorizedKeysCommand b/AuthorizedKeysCommand
new file mode 100755
index 0000000..6e13063
--- /dev/null
+++ b/AuthorizedKeysCommand
@@ -0,0 +1,15 @@
1#!/bin/sh
2username=$1
3userhome=$2
4fingerprint=$3
5authline="$4 $5"
6
7case "$userhome" in
8 *'"'*) exit ;;
9esac
10
11usercommand=$userhome/.ssh/AnonymousAccessCommand
12
13[ -x "$usercommand" ] || exit
14
15printf 'command="%s",no-port-forwarding %s\n' "$usercommand $fingerprint" "$authline"
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bd63693
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,24 @@
1ifeq ($(shell id -u),0)
2SUDO =
3else
4SUDO = sudo
5endif
6
7ROOT_INSTALL = $(SUDO) install
8
9USER != echo "$${SUDO_USER:-$$(id -un)}"
10
11SSH_CONFIG_DIR = /etc/ssh
12SSHD_CONFIG_DIR = $(SSH_CONFIG_DIR)/sshd_config.d
13SSH_LIB_DIR = /usr/lib/ssh
14USER_SSH_CONFIG_DIR = ~$(USER)/.ssh
15
16.PHONY: install
17install:
18 install -t $(USER_SSH_CONFIG_DIR) AnonymousAccessCommand
19 $(ROOT_INSTALL) -d "$(SSH_CONFIG_DIR)" "$(SSHD_CONFIG_DIR)" "$(SSH_LIB_DIR)" || true
20 $(ROOT_INSTALL) -m0644 -t "$(SSHD_CONFIG_DIR)" anonymous-access.conf || true
21 $(ROOT_INSTALL) -t "$(SSH_LIB_DIR)" AuthorizedKeysCommand || true
22 [ -e "$(SSH_LIB_DIR)"/AuthorizedKeysCommand ] || $(SUDO) ln -s -t /etc/ssh "$(SSH_LIB_DIR)"/AuthorizedKeysCommand
23
24include tests.makefile
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..a1c0dd8
--- /dev/null
+++ b/README.txt
@@ -0,0 +1 @@
minimal rhizome
diff --git a/anonymous-access.conf b/anonymous-access.conf
new file mode 100644
index 0000000..5cd6b6a
--- /dev/null
+++ b/anonymous-access.conf
@@ -0,0 +1,10 @@
1ExposeAuthInfo=yes
2AuthorizedKeysCommandUser=root
3AuthorizedKeysCommand=/etc/ssh/AuthorizedKeysCommand %u %h %f "%t %k"
4
5# %u The username.
6# %h The home directory of the user.
7# %f The fingerprint of the key or certificate.
8# %t The key or certificate type.
9# %k The base64-encoded key or certificate for authentication.
10
diff --git a/tests.makefile b/tests.makefile
new file mode 100644
index 0000000..b152f01
--- /dev/null
+++ b/tests.makefile
@@ -0,0 +1,34 @@
1testuser = testuser
2
3SU = $(SUDO) su
4
5.PHONY: test useradd cleanuser
6
7useradd:
8 $(SUDO) useradd $(testuser) --shell /bin/bash --create-home
9 $(SU) - $(testuser) -c 'ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""'
10 $(SU) - $(testuser) -c 'git config --global user.name $(testuser)'
11 $(SU) - $(testuser) -c 'git config --global user.email $(testuser)'
12
13test: install $(shell getent passwd $(testuser) >/dev/null || echo useradd)
14 $(ROOT_INSTALL) -t ~$(testuser) tests.sh
15 $(SU) - $(testuser) -c ./tests.sh
16
17ifeq ($(testuser),)
18$(error testuser not defined)
19endif
20cleanuser_command = $(SUDO) rm -I -r ~$(testuser)
21
22cleanuser:
23 : Preparing to run destructive command:
24 :
25 :
26 : $(cleanuser_command)
27 :
28 :
29 : Press ctrl-c to abort.
30 :
31 @for n in 5 4 3 2 1; do printf ' %d\r' "$$n"; sleep 1; done
32 $(cleanuser_command) || true
33 $(SUDO) userdel testuser || true
34
diff --git a/tests.sh b/tests.sh
new file mode 100644
index 0000000..5d221b6
--- /dev/null
+++ b/tests.sh
@@ -0,0 +1,66 @@
1#!/bin/sh
2set -ex
3USER=u
4DIR=src/anonymous-ssh
5HOST=localhost
6SSH_ID=~/.ssh/id_ed25519
7
8get_sshfp()
9{
10 (
11 key="$1"
12 r="${2:-.}"
13 dns=$(mktemp) || exit
14 trap 'rm -rf "$dns"' EXIT
15
16 ssh-keygen -r "$r" -f "$key" > "$dns"
17 exec < "$dns"
18 while read line
19 do
20 set -- $line
21 if [ "$3 $5" = "SSHFP 2" ]
22 then
23 echo "$line"
24 break
25 fi
26 done
27 )
28}
29
30make_test_commit()
31{
32 newfile=newfile.$(date -Ins|tr -d :)
33 touch "$newfile"
34 git add "$newfile"
35 git commit -m "$newfile"
36}
37
38[ -e "$SSH_ID" ] || ssh-keygen -t ed25519 -f "$SSH_ID" -P ''
39
40git_namespace=$(set -- $(get_sshfp "$SSH_ID") && echo $6)
41
42ssh -o NoHostAuthenticationForLocalhost=yes $USER@$HOST -- test || true
43[ ! -e anonymous-ssh ] || rm -rf anonymous-ssh
44export GIT_SSH_COMMAND="ssh -o NoHostAuthenticationForLocalhost=yes -i $SSH_ID"
45git clone -v ${USER}@${HOST}:${DIR}
46cd anonymous-ssh
47
48make
49
50git pull --ff-only
51make_test_commit
52git push -f
53make_test_commit
54git push
55git log -n4
56git pull --ff-only
57git log -n4
58git push
59
60# branch=$(git branch -q --show-current)
61# forkname=origin-myfork
62# ns_branch=refs/namespaces/$git_namespace/refs/heads/$branch
63# git remote add -m "$ns_branch" "$forkname" $(git remote get-url origin)
64# git push "$forkname"
65# git pull "$forkname" --ff-only "$branch"
66exit