├── LICENSE.md ├── README.md ├── files ├── git-server.service.in ├── nginx.service.in ├── os-release ├── site.in ├── tor-server.service.in └── torrc.in ├── flake.lock └── flake.nix /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Lucas Ontivero 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Portable Hidden Git Server 2 | 3 | A minimalistic and reproducible, ready to deploy and immutable [git daemon](https://git-scm.com/book/en/v2/Git-on-the-Server-Git-Daemon) service published as a [Tor onion service](https://community.torproject.org/onion-services/). 4 | 5 | ## What is this 6 | 7 | This project contains a [Nix Flake](https://xeiaso.net/blog/series/nix-flakes) that once built generates a [systemd portable service](https://systemd.io/PORTABLE_SERVICES/) 8 | (a single file under the name `personal.raw`) that can be "plug" to any Linux that supports systemd. The `personal.raw` file is just a 9 | compressed read-only filesystem for Linux containing all what is needed (`git`, `tor`, `nginx` and all their dependencies, and the config files too) 10 | 11 | 12 | ## How to build 13 | 14 | ```console 15 | $ nix build .#portable 16 | ``` 17 | 18 | Then you have to "attach" the portable service to the systemd system: 19 | 20 | ```console 21 | # mv result /var/lib/portables/personal.raw 22 | # portablectl attach --enable --now personal 23 | ``` 24 | 25 | And that's it. You can verify `personal.tor-server.service`, `personal.git-server.service` and `personal.nginx.service` are working properly with: 26 | 27 | ```console 28 | systemctl status personal.tor-server.service 29 | systemctl status personal.git-server.service 30 | systemctl status personal.nginx.service 31 | ``` 32 | 33 | **Note:** it is possible to browse the generated filesystem by building a different flake output called `rootfs`: 34 | 35 | ```console 36 | nix build .#rootfs 37 | ``` 38 | 39 | After that the `result` link will contain the directory tree instead of the squashed filesystem. 40 | 41 | ## What next 42 | 43 | * nginx server to provide git over http. 44 | * support https (certbot) 45 | * use systemd private network's to isolate network communication 46 | * map ports to standard ones with iptable 47 | * make all this more configurable (ips, ports, hidden service private keys) 48 | 49 | ---- 50 | 51 | This is heavily "inspired" on the work of [Xe](https://xeiaso.net/)'s [Nix Flakes: Packages and How to Use Them](https://xeiaso.net/blog/nix-flakes-2-2022-02-27) and Дамјан Георгиевски's [Tiny Tiny RSS](https://github.com/gdamjan/tt-rss-service) 52 | -------------------------------------------------------------------------------- /files/git-server.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Git Daemon 3 | Documentation=man:git-daemon(1) 4 | 5 | [Service] 6 | ExecStart=@git@/bin/git daemon --reuseaddr --export-all --informative-errors --verbose --base-path=/var/lib/git /var/lib/git 7 | User=git 8 | Group=git 9 | DynamicUser=yes 10 | StateDirectory=git 11 | 12 | StandardOutput=inherit 13 | StandardError=journal 14 | SyslogIdentifier=git-daemon 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | -------------------------------------------------------------------------------- /files/nginx.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=The NGINX HTTP and reverse proxy server 3 | After=syslog.target network-online.target remote-fs.target nss-lookup.target 4 | Wants=network-online.target 5 | 6 | [Service] 7 | Type=forking 8 | #PIDFile=/run/nginx/nginx.pid 9 | ExecStartPre=@nginx@/bin/nginx -t -c /etc/www-wasabito 10 | ExecStartPost=@coreutils@/bin/sleep 0.5 11 | ExecStart=@nginx@/bin/nginx -c /etc/www-wasabito 12 | ExecReload=@nginx@/bin/nginx -s reload -c /etc/www-wasabito 13 | #ExecStop=@coreutils@/bin/kill -s QUIT $MAINPID 14 | PrivateTmp=true 15 | PrivateNetwork=false 16 | User=nginx 17 | Group=nginx 18 | DynamicUser=yes 19 | LogsDirectory=nginx 20 | CacheDirectory=nginx 21 | RuntimeDirectory=nginx 22 | RuntimeDirectoryPreserve=yes 23 | 24 | [Install] 25 | WantedBy=multi-user.target 26 | 27 | -------------------------------------------------------------------------------- /files/os-release: -------------------------------------------------------------------------------- 1 | PORTABLE_PRETTY_NAME="Personal hidden git server" 2 | PORTABLE_ID=personal-git-server 3 | PRETTY_NAME=NixOS 4 | ID=nixos 5 | BUILD_ID=rolling 6 | -------------------------------------------------------------------------------- /files/site.in: -------------------------------------------------------------------------------- 1 | # /etc/www-wasabito 2 | 3 | worker_processes 1; 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | http 10 | { 11 | server { 12 | listen 8080; 13 | listen [::]:8080; 14 | server_name 127.0.0.1 192.168.100.116; 15 | 16 | root /var/lib/nginx; 17 | charset utf-8; 18 | 19 | location / { 20 | } 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /files/tor-server.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Tor Daemon 3 | 4 | [Service] 5 | ExecStart=@tor@/bin/tor --torrc-file /etc/torrc --datadirectory $STATE_DIRECTORY 6 | StateDirectory=tor 7 | #Restart=always 8 | DynamicUser=yes 9 | User=tor 10 | 11 | SyslogIdentifier=tor-daemon 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | 16 | -------------------------------------------------------------------------------- /files/torrc.in: -------------------------------------------------------------------------------- 1 | HiddenServiceDir /var/lib/tor/onion_service/ 2 | HiddenServicePort 9418 127.0.0.1:9418 3 | HiddenServiceVersion 3 4 | 5 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1661541451, 6 | "narHash": "sha256-LALyT63vpo1sftSmHAbbrj+emfCOo93Jv4xVOIcmnl0=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "38e16b192af13ff6cffc8a35a25f390f1e96b585", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "id": "nixpkgs", 14 | "ref": "nixos-unstable", 15 | "type": "indirect" 16 | } 17 | }, 18 | "root": { 19 | "inputs": { 20 | "nixpkgs": "nixpkgs" 21 | } 22 | } 23 | }, 24 | "root": "root", 25 | "version": 7 26 | } 27 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "My personal server setup"; 3 | 4 | inputs = { 5 | nixpkgs.url = "nixpkgs/nixos-unstable"; 6 | }; 7 | 8 | outputs = { self, nixpkgs }: 9 | let 10 | pkgs = nixpkgs.legacyPackages.x86_64-linux; 11 | squash-compression = "xz -Xdict-size 100%"; 12 | version = builtins.substring 0 8 self.lastModifiedDate; 13 | in { 14 | 15 | rootfs = pkgs.stdenv.mkDerivation { 16 | name = "rootfs"; 17 | tor = pkgs.tor; 18 | git = pkgs.git; 19 | nginx = pkgs.nginx; 20 | certbot = pkgs.certbot; 21 | coreutils = pkgs.coreutils; 22 | buildCommand = '' 23 | # Prepare the portable service file-system layout 24 | mkdir -p $out/etc/systemd/system $out/proc $out/sys $out/dev $out/run $out/tmp $out/var/tmp $out/var/lib 25 | touch $out/etc/resolv.conf $out/etc/machine-id 26 | cp ${./files/os-release} $out/etc/os-release 27 | 28 | # Create the mount-point for the cert store 29 | mkdir -p $out/var/{lib,log,cache}/{nginx,git,tor/onion_service} 30 | ln -sf /var/lib/private/tor $out/var/lib/tor 31 | ln -sf /var/lib/private/git $out/var/lib/git 32 | ln -sf /var/lib/private/nginx $out/var/lib/nginx 33 | ln -sf /var/log/private/nginx $out/var/log/nginx 34 | 35 | mkdir -p $out/run/nginx 36 | 37 | # setup systemd units 38 | substituteAll ${./files/git-server.service.in} $out/etc/systemd/system/personal.git-server.service 39 | substituteAll ${./files/tor-server.service.in} $out/etc/systemd/system/personal.tor-server.service 40 | substituteAll ${./files/nginx.service.in} $out/etc/systemd/system/personal.nginx.service 41 | cp ${./files/torrc.in} $out/etc/torrc 42 | cp ${./files/site.in} $out/etc/www-wasabito 43 | ''; 44 | }; 45 | 46 | default = self.portable; 47 | 48 | portable = 49 | pkgs.stdenv.mkDerivation { 50 | name = "personal.raw"; 51 | nativeBuildInputs = [ pkgs.squashfsTools ]; 52 | 53 | buildCommand = '' 54 | closureInfo=${pkgs.closureInfo { rootPaths = [ self.rootfs ]; }} 55 | mkdir -p nix/store 56 | for i in $(< $closureInfo/store-paths); do 57 | cp -a "$i" "''${i:1}" 58 | done 59 | # archive the nix store 60 | mksquashfs nix ${self.rootfs}/* $out \ 61 | -noappend \ 62 | -keep-as-directory \ 63 | -all-root -root-mode 755 \ 64 | -b 1048576 -comp ${squash-compression} 65 | ''; 66 | }; 67 | 68 | 69 | devShells.default = pkgs.mkShell { 70 | buildInputs = [ self.default ]; 71 | }; 72 | }; 73 | } 74 | --------------------------------------------------------------------------------