From 6b1cb001e42fe7b580c3ad5af7f2acb1e3d30913 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Am=C3=A9lia=20Coutard-Sander?= Date: Mon, 2 Dec 2024 22:07:38 +0100 Subject: [PATCH] Rewrite using gnunet-vpn --- README.txt | 45 ++++++++++++++++++++++++--------------------- daemon.sh | 26 -------------------------- git-remote-gnunet | 40 +++++++++++++++++++--------------------- handle-request.sh | 29 +++++++++++++++++++++++++++++ run-daemons.sh | 14 -------------- 5 files changed, 72 insertions(+), 82 deletions(-) delete mode 100755 daemon.sh create mode 100755 handle-request.sh delete mode 100755 run-daemons.sh diff --git a/README.txt b/README.txt index c9eadbe..59e784f 100644 --- a/README.txt +++ b/README.txt @@ -1,18 +1,27 @@ -This set of bash scripts allows one to clone/fetch/pull git repositories using gnunet, specifically the GNS and cadet. -In the interest of demonstrating that it works, this repo is available at - `gnunet://git.serv.amelia.gnunet.gns.alt/git-over-gnunet`. - Try cloning it ! (And do tell me if it fails, or if it works.) +This set of bash scripts allows one to clone/fetch/pull git repositories using gnunet, specifically the GNS and the GNUnet VPN. +In the interest of demonstrating that it works, all the repos on my server are available at + `gnunet://git.serv.amelia.gnunet.gns.alt/.git` (see gitweb for the specific URIs). + Try cloning them ! (And do tell me if it fails, or if it works.) Usage: Client: You must add the repo folder to the GIT_EXEC_PATH (for example by prepending `GIT_EXEC_PATH=$GIT_EXEC_PATH:` before every git command that uses remotes). - The URL to give to git is gnunet:///. + The URL to give to git is `gnunet:///`. Server: - - To run the server on a repo, simply copy the two .sh files to its root's parent, and run - `./run-daemons.sh `. - - You should also add a gns TXT record containing the peerid serving your repos to your zone. - - This will open the repo for pulling on the url `gnunet:///`. + - To serve repos, you have to setup a gnunet-vpn exit service. + ``` + [exit] + IMMEDIATE_START = YES + EXIT_IFNAME = % + [.gnunet.] + TCP_REDIRECTS = 9418:169.254.86.1:9418 + ``` + - You should also add a gns VPN record containing the required information to connect to the exit service. + `gnunet-namestore -a -t VPN -e 1d -n -V '1 ' -z -p` + - Next, you need to run the daemon: it needs to run `handle-request.sh` in the parent folder of all the repos every time someone connects to it. + For example, with socat: `socat tcp4-listen:9418,bind=169.254.86.1,fork exec:'/path/to/handle-request.sh /path/to/repos'` + - This will open the repos for pulling on the url `gnunet:///`. Features: - Clone repo ! @@ -20,23 +29,17 @@ Features: - Pull repo ! TODO: - - GNUnet VPN exit service - - Figure out why cadet breaks when pushing, probably linked to the usage of the CLI client, GNUnet VPN rewrite might fix. - - gnunet-cadet exits too early, definitely an issue with the input method. GNUnet VPN will probably fix the issue. - In the meantime, a cat+sleep is a reasonnable enough "fix". - - In which case, password system to push to repos (just add the password to the port ?) + - Pushing, and a gnunet-identity verification system ? - Pulling from anyone using a commit hash ? - DHT to figure out who has what commits ? - Question of trust for pulling from peers ? - - Multiple repos. How it works: Client: - Uses a git remote helper to give git the ability to interpet the gnunet:// scheme. - - Gets the TXT record associated with the GNS name given in gnunet:///. - - This TXT record is a . - - To run a specific git command on repo and get its inputs and outputs, connect via cadet - to the peer id , via the port git.-. - - This is used by the remote helper to run git-upload-pack remotely. + - Gets the VPN record associated with the GNS name given in gnunet:///. + - Connects to the exit service, give it the name of the repo and the command required (e.g. `git-upload-pack`). Server: - - The server just loops on a cadet server with inputs and outputs connected to the specified git-command. + - Reads the path and the command. + - Checks that the command is git-upload-pack and the path isn't malicious. + - Executes the command and connects it to the client. diff --git a/daemon.sh b/daemon.sh deleted file mode 100755 index 0720812..0000000 --- a/daemon.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -mktempfifo() { - fifo_name="$(mktemp -u)" - mkfifo "$fifo_name" || mktempfifo -} - -if test -z $1 || test -z $2 || ! test -z $3; then - echo >&2 "Usage: $0 " - exit 1 -fi - -service="$1" -name="$2" - -echo >&2 "Running $service on port \`git.$name.git-$service'." - -mktempfifo -while < "$fifo_name" gnunet-cadet -o "git.$name.git-$service" | git "$service" "$(pwd)/$name" | (cat; sleep 5) | cat > "$fifo_name"; test 130 != "$?"; do - rm "$fifo_name" - mktempfifo -done - -rm $fifo_name - -echo >&2 "Terminating $service on port \`git.$name.git-$service'." diff --git a/git-remote-gnunet b/git-remote-gnunet index c92e29d..59d1bcd 100755 --- a/git-remote-gnunet +++ b/git-remote-gnunet @@ -1,20 +1,20 @@ #!/usr/bin/env bash +shopt -s lastpipe + if [[ "$2" = gnunet://* ]]; then - addr=${2:9} + echo "${2:9}" else - addr=$2 -fi -gns="$(echo "$addr" | cut -d/ -f1)" -repo="$(echo "$addr" | cut -d/ -f2)" -if test -z "$gns" || test -z "$repo" || ! test -z "$(echo "$addr" | cut -d/ -f3-)"; then + echo "$2" +fi | awk ' +match($0, /(([^@]*)@)?([^/]*)(\/(.*))?/, a) { printf "%s\n%s\n%s\n", a[2], a[3], a[5] } +' | { read -r username && read -r gnsname && read -r path && ! grep '.'; } +if test "$?" != "0"; then echo >&2 "Invalid address: \`$2'." - echo >&2 "Format: \`gnunet://gns-name/repo-name'." - echo >&2 " or \`gnunet::gns-name/repo-name'." + echo >&2 "URI format: \`[gnunet://][user@]gns.name/path/to/repo'." exit 1 fi - while read -r cmd arg; do case "$cmd" in capabilities) @@ -30,18 +30,16 @@ while read -r cmd arg; do esac done -if [[ "$arg" = "git-receive-pack" ]]; then - echo >&2 "Pushing unsupported for now." +echo >&2 "Looking up $gnsname." +vpn="$(gnunet-gns -u "$gnsname" -t VPN -r)" +if test "$(echo "$vpn" | cut -d' ' -f1)" != "1"; then + echo >&2 "VPN record must specify TCP, not UDP." exit 1 fi +peer="$(echo "$vpn" | cut -d' ' -f2)" +service="$(echo "$vpn" | cut -d' ' -f3)" +echo >&2 "Trying service \`$service' on peer \`$peer'." +ip="$(gnunet-vpn -s "$service" -p "$peer" -t)" +echo >&2 "Using tunnel ip \`$ip'." -echo '' -echo >&2 "Looking up $gns." -peer="$(gnunet-gns -u "$gns" -t TXT -r)" -if test -z "$peer"; then - echo >&2 "Address lookup failed: \`$gns'." - exit 1 -fi -port="git.$repo.$arg" -echo >&2 "Attempting to connect to peer $peer, on port \`$port'." -(stdbuf -i0 -o0 -e0 cat; sleep 0.01) | gnunet-cadet "$peer" "$port" +{ printf '%s\n' "$path" "$arg"; cat; } | socat stdio tcp4:"$ip":9418 diff --git a/handle-request.sh b/handle-request.sh new file mode 100755 index 0000000..32785ed --- /dev/null +++ b/handle-request.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +if test -z $1 || ! test -z $2; then + echo >&2 "Usage: $0 /base/path" + exit 1 +fi +base_path="$1/" + +read -r path || { echo "No path specified." | tee /dev/stderr; exit 1; } +read -r comm || { echo "No command specified." | tee /dev/stderr; exit 1; } + +if echo "$path" | grep -q '/'; then + echo "Path \`$path' contains a '/'." | tee /dev/stderr + exit 1 +fi +if test "$path" = '..'; then + echo "Path cannot be \`..'." | tee /dev/stderr + exit 1 +fi + +if test "$comm" != "git-upload-pack"; then + echo "Command \`$comm' has to be git-upload-pack." | tee /dev/stderr + exit 1 +fi + +git -C "$base_path$path" status >/dev/null 2>/dev/null || { echo "Not a git repository." | tee /dev/stderr; exit 1; } + +echo '' +exec "$comm" "$base_path$path" diff --git a/run-daemons.sh b/run-daemons.sh deleted file mode 100755 index db1302d..0000000 --- a/run-daemons.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -if test -z $1 || ! test -z $2; then - echo >&2 "Usage: $0 " - exit 1 -fi - -name="$1" - -echo >&2 "Running pullable repo $name." - -./daemon.sh upload-pack "$name" - -echo >&2 "Terminating pullable repo $name." -- 2.46.0