-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/<repo>.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:<path-to-git-over-gnunet-repo>` before every git command that uses remotes).
- The URL to give to git is gnunet://<location of the repo>/<name of the repo>.
+ The URL to give to git is `gnunet://<gns location of the repo>/<name of the repo>`.
Server:
- - To run the server on a repo, simply copy the two .sh files to its root's parent, and run
- `./run-daemons.sh <repo-name>`.
- - 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://<gns-record-name>/<repo-name>`.
+ - To serve repos, you have to setup a gnunet-vpn exit service.
+ ```
+ [exit]
+ IMMEDIATE_START = YES
+ EXIT_IFNAME = %
+ [<gns-name>.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 <gns-name> -V '1 <peer-id> <gns-name>' -z <ego> -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://<gns-record-name>/<repo-name>`.
Features:
- Clone repo !
- 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://<gns-name>/<repo-name>.
- - This TXT record is a <peer-id>.
- - To run a specific git command on repo <repo> and get its inputs and outputs, connect via cadet
- to the peer id <peer-id>, via the port git.<repo-name>-<command>.
- - 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://<gns-name>/<repo-name>.
+ - 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.
#!/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)
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
--- /dev/null
+#!/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"