diff --git a/amanuensis.nix b/amanuensis.nix new file mode 100644 index 0000000..1b44eeb --- /dev/null +++ b/amanuensis.nix @@ -0,0 +1,50 @@ +# Configuration for the Amanuensis service + +{ config, pkgs, ... }: + +# Set up python +with pkgs; +let amanuensis-requires = python-packages: with python-packages; [ + flask flask_login flask_wtf gunicorn +]; +python3-with-amanuensis-requires = python3.withPackages amanuensis-requires; +in +{ + # Create a user for the server process to run under + users.users.amanuensis = { + isNormalUser = true; + group = "amanuensis"; + description = "Amanuensis server user"; + packages = [ python3-with-amanuensis-requires ]; + }; + users.groups.amanuensis = {}; + + # Create the server process systemd unit + systemd.services.amanuensis = { + enable = true; + description = "Amanuensis Lexicon server"; + path = [ python3-with-amanuensis-requires ]; + serviceConfig = { + Type = "simple"; + ExecStart = "/home/amanuensis/Amanuensis/run.sh"; + Restart = "on-failure"; + User = "amanuensis"; + WorkingDirectory = "/home/amanuensis"; + }; + wantedBy = [ "multi-user.target" ]; + }; + + # Configure nginx to forward to the server at the lexicon subdomain + services.nginx.virtualHosts."lexicon.alogoulogoi.com" = { + enableACME = true; + forceSSL = true; + extraConfig = '' + access_log /var/log/nginx/access.lexicon.log; + ''; + locations."/".extraConfig = '' + proxy_buffering off; + proxy_pass http://localhost:8000/; + ''; + }; +} + diff --git a/catacomb.nix b/catacomb.nix new file mode 100644 index 0000000..407788e --- /dev/null +++ b/catacomb.nix @@ -0,0 +1,33 @@ +# Configuration for the catacomb forwarder + +{ config, pkgs, ... }: + +{ + # Configure nginx to forward to the server on catacomb + services.nginx.virtualHosts."catacomb.alogoulogoi.com" = { + enableACME = true; + forceSSL = true; + extraConfig = '' + access_log /var/log/nginx/access.catacomb.log; + ''; + locations = { + # Forwards to the index server + "/browse/".proxyPass = "http://10.7.3.16:7472/browse/"; + # Forwards to nginx via catacomb auth server + "/".extraConfig = '' + auth_request /auth; + proxy_buffering off; + proxy_pass http://10.7.3.16:7470/; + ''; + "= /auth".extraConfig = '' + internal; + proxy_buffering off; + proxy_pass_request_body off; + proxy_pass http://10.7.3.16:7471/; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-URI $request_uri; + ''; + }; + }; +} + diff --git a/configuration.nix b/configuration.nix new file mode 100644 index 0000000..b2407ab --- /dev/null +++ b/configuration.nix @@ -0,0 +1,178 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page +# and in the NixOS manual (accessible by running ‘nixos-help’). + +{ config, pkgs, ... }: + +{ + imports = + [ # Include the results of the hardware scan. + ./hardware-configuration.nix + ./amanuensis.nix + ./redstring.nix + ./catacomb.nix + ./gitea.nix + ./inquisitor.nix + ]; + + # Use the GRUB 2 boot loader. + boot.loader.grub = { + enable = true; + version = 2; + device = "/dev/xvda"; + extraConfig = "serial --unit=0 --speed=115200 ; terminal_input serial console ; terminal_output serial console"; + }; + boot.kernelParams = ["console=ttyS0"]; + + nix = { + package = pkgs.nixFlakes; + maxJobs = 2; + extraOptions = '' + experimental-features = nix-command flakes + ''; + }; + + swapDevices = [ { device = "/swap"; size = 1024; } ]; + + networking.hostName = "empyrean"; + + # The global useDHCP flag is deprecated, therefore explicitly set to false here. + # Per-interface useDHCP will be mandatory in the future, so this generated config + # replicates the default behaviour. + networking.useDHCP = false; + networking.interfaces.eth0.useDHCP = true; + + # Configure network proxy if necessary + # networking.proxy.default = "http://user:password@proxy:port/"; + # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; + + # Select internationalisation properties. + i18n.defaultLocale = "en_US.UTF-8"; + console = { + font = "Lat2-Terminus16"; + keyMap = "us"; + }; + + # Set your time zone. + time.timeZone = "UTC"; + + # List packages installed in system profile. To search, run: + # $ nix search wget + environment.systemPackages = with pkgs; [ + vim htop git tinc_pre python3 + gitea + ]; + environment.variables.EDITOR = "vim"; + + services.nginx = { + enable = true; + recommendedProxySettings = true; + virtualHosts = { + # Static pages + "www.ktvb.site" = { + enableACME = true; + forceSSL = true; + root = "/srv/wedding/"; + extraConfig = '' + access_log /var/log/nginx/access.ktvb.log; + index index.html; + ''; + }; + "www.alogoulogoi.com" = { + enableACME = true; + forceSSL = true; + root = "/srv/www/"; + extraConfig = '' + access_log /var/log/nginx/access.www.log; + index index.html; + ''; + }; + # Deny all other subdomains + "alogoulogoi.com" = { + default = true; + locations."/".return = "444"; + }; + }; + }; + security.acme = { + email = "tim.vanbaak+alogoulogoi@gmail.com"; + acceptTerms = true; + }; + + services.gitolite = { + enable = true; + adminPubkey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDkSvOcY0eFuYqewc73MNHGP/Owzfnt+BZDSOCwr4h+gJenOBmXol695sRKdIw8phgshb6LsNyWeEAr3YISwzjdOvWNKpSsdyH/79VFIffC+/RBhPFhIPHn1zpHIwgthXji8FMmnO6B1bhJvbJxD5bUqhdBLnUeMhNgUkDj4Qi42S8yK1VZkgWRuPOTGqlzkODEdzG6OST7ndL58jEQaK8R2KRC2cZfrjingmPL+ORyf1/lGwyF6MEAmbQuE6UdspMs8FWt2e8jQJS+ZQ8dl+NDFrC1QfPRFpBJWnQceBcfAVZTtDaJpRhc4ClZstBy/bVRjiKeQiNv5NasjKRvvIvot4+LXmBKrXJs81enExtMSHMPuqPRlyVZLMMCVmdLDP/HUYOASDzlUhV/v5Wp0jjY4Wy0IWC7nm7P8EKsp1ZofwU6rJ9XPLpQJt7UUURX71h1FMaqi+lylW6xkD3LqD8oT5Bdp+Vs0bUbPQVRw1Fenjc6G1URU94GOAggyNgsWms= root@empyrean"; + }; + + services.ntp = { + enable = true; + servers = ["time.nist.gov"]; + }; + + services.openssh = { + enable = true; + passwordAuthentication = false; + permitRootLogin = "prohibit-password"; + }; + + services.tinc.networks.beatific = { + listenAddress = "0.0.0.0"; + chroot = false; + }; + + services.nebula.networks.beatific = { + enable = true; + + # Network certificate and host credentials + ca = "/etc/nebula/beatific/beatific.crt"; + cert = "/etc/nebula/beatific/empyrean.crt"; + key = "/etc/nebula/beatific/empyrean.key"; + + # This host has a well-known IP at its VPS host, so it can function as a lighthouse/entry node + isLighthouse = true; + + # Listen to connection requests from the public Internet + listen.port = 4242; + listen.host = "vpn.alogoulogoi.com"; + + # Don't filter anything at the VPN level + firewall.outbound = [ { port = "any"; proto = "any"; host = "any"; } ]; + firewall.inbound = [ { port = "any"; proto = "any"; host = "any"; } ]; + + settings = { + # Enable UDP holepunching both ways, which allows nodes to establish more direct connections with each other + punchy = { punch = true; response = true; }; + }; + }; + + networking.firewall = { + enable = true; + allowPing = true; + allowedTCPPorts = [ + 22 # ssh + 80 # http + 443 # https + 655 # tinc + ]; + allowedUDPPorts = [ + 655 # tinc + ]; + }; + + users.users.tvb = { + isNormalUser = true; + group = "tvb"; + extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user. + }; + users.groups.tvb = {}; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It‘s perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "20.03"; # Did you read the comment? + +} + diff --git a/gitea.nix b/gitea.nix new file mode 100644 index 0000000..6eaf7ff --- /dev/null +++ b/gitea.nix @@ -0,0 +1,68 @@ +# Configuration for Gitea instance + +{ config, pkgs, ... }: + +{ + # Gitea configuration + services.gitea = { + # Enable Gitea and configure for reverse proxy + enable = true; + httpAddress = "127.0.0.1"; + httpPort = 3300; + domain = "git.alogoulogoi.com"; + rootUrl = "https://git.alogoulogoi.com/"; + + # Private server + disableRegistration = true; + #useWizard = true; # Needed for first-time building + + # Settings + appName = "Horse Codes"; + lfs.enable = true; + dump = { + enable = true; + interval = "weekly"; + }; + log.level = "Info"; + settings = { + "repository" = { + DEFAULT_PRIVATE = true; + }; + "ui" = { + DEFAULT_THEME = "arc-green"; + SHOW_USER_EMAIL = false; + }; + "ui.meta" = { + AUTHOR = "Horse Codes"; + DESCRIPTION = "Alogoulogoi Gitea instance"; + KEYWORDS = ""; + }; + "security" = { + INSTALL_LOCK = true; + }; + "picture" = { + DISABLE_GRAVATAR = true; + }; + "cron.archive_cleanup".ENABLED = false; + "cron.sync_external_users".ENABLED = false; + }; + }; + + users.users.gitea.openssh.authorizedKeys.keys = [ + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCYr3Y4waQA4Qb9Vv29APxqkAE6E8KSoTcK1L+NSKOEAb3IxlqitMMnDFfWENXuQlEkkxkqszGA3oe2uchN89UckBFIkm8oEBNE2ZQ0SnuVv+ETHRYMmGvhfOnsnEzpD/j6qSk/0/ea2eJpzfUazMVNTDP7aX6pI0F0n6lXFty0vVan/gN6lM41aNatlQPGxY2XDJQ/e2IJJeOubb2YwH/Vj7/t25yuKiQ5AmaX9fVheM4xA1xfNTs42UfoHzU7Pk3gT6D6L1DGHjsbO0FD4lKPe030XYcPVvpqSiEKGTAYvcWnPH/RDXuz6cEQpN3kMajEtvKUcu0FM/3NPJhvUuxEX0wJnvPPRuY30tcD2WuYemQjm5OCGewdIr1a7mMJ/5zEAzRq4AttEdw7PtTjoj8O+0S6pFrFnv6Dp5TOrg9jyRLICEv7SPb76OhPWWr2uf3TllfXJcQMdsEd3gnTxaUUgJRmD3hfAQO5fOR0MFuVw+bVgleeYctBCW5UjbWZqE1lzEU8xwVYKB05HnWI5tgeh/pkdjg9AfdWnuVU7EljJ8nFEevNTJEe3kjZ67l+wL/dLiyyQuMIq1oBpcOCq+ew0jWZMfPq3o5r13qsdPkUuqdwWOXhCQtqOHHYXVgFEvEGLWacdgHSIFlP7IdfW1M4k1yFPBUlJUU9Bo+VGSZxSw== tvb@catacomb" + ]; + + # Configure nginx to forward to the server at the git subdomain + services.nginx.virtualHosts."git.alogoulogoi.com" = { + enableACME = true; + forceSSL = true; + extraConfig = '' + access_log /var/log/nginx/access.git.log; + ''; + locations."/".extraConfig = '' + proxy_buffering off; + proxy_pass http://localhost:3300/; + ''; + }; +} + diff --git a/hardware-configuration.nix b/hardware-configuration.nix new file mode 100644 index 0000000..66d6ba0 --- /dev/null +++ b/hardware-configuration.nix @@ -0,0 +1,22 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, pkgs, ... }: + +{ + imports = [ ]; + + boot.initrd.availableKernelModules = [ "ata_piix" "sr_mod" "xen_blkfront" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/60aa6f09-f77f-472e-beb1-3441423a5d6d"; + fsType = "ext4"; + }; + + swapDevices = [ ]; + + nix.maxJobs = lib.mkDefault 1; +} diff --git a/inquisitor.nix b/inquisitor.nix new file mode 100644 index 0000000..a266c4f --- /dev/null +++ b/inquisitor.nix @@ -0,0 +1,138 @@ +{pkgs, ...}: + +let + # Import the inquisitor package and build it + inquisitorSource = pkgs.fetchFromGitHub { + owner = "Jaculabilis"; + repo = "Inquisitor"; + rev = "a6d961aba948d3a682dbde12dbaa8805eadbbd84"; + sha256 = "10n6c5zvi27f92b7am0rrdizxz0mlp3rw1y1jyd44b57ykk7x6fr"; + }; + inquisitor = pkgs.callPackage inquisitorSource {}; + + # Define the inquisitor data directory + inquisiDir = "/var/lib/inquisitor"; + + # Define an scp helper for executing in cron jobs + scp-helper = pkgs.writeShellScriptBin "scp-helper" '' + ${pkgs.openssh}/bin/scp -i ${inquisiDir}/inquisitor.key -oStrictHostKeyChecking=no "$@" + ''; + + # Define the inquisitor service user + inquisitorUser = { + name = "inquisitor"; + group = "inquisitor"; + description = "Inquisitor service user"; + isSystemUser = true; + shell = pkgs.bashInteractive; + packages = [ inquisitor pkgs.cron ]; + }; + + # Create the inquisitor config file in the nix store, pointing to /var/lib/ + inquisitorConfig = pkgs.writeTextFile { + name = "inquisitor.conf"; + text = '' + DataPath = ${inquisiDir}/data/ + SourcePath = ${inquisiDir}/sources/ + CachePath = ${inquisiDir}/cache/ + Verbose = false + LogFile = ${inquisiDir}/inquisitor.log + ''; + }; + + # Create a setup script to ensure the service directory state + inquisitorSetup = pkgs.writeShellScriptBin "inquisitor-setup.sh" '' + # Ensure the service directory and the default source directory + ${pkgs.coreutils}/bin/mkdir -p ${inquisiDir}/data/inquisitor/ + ${pkgs.coreutils}/bin/mkdir -p ${inquisiDir}/sources/ + ${pkgs.coreutils}/bin/mkdir -p ${inquisiDir}/cache/ + if [ ! -f ${inquisiDir}/data/inquisitor/state ]; then + ${pkgs.coreutils}/bin/echo "{}" > ${inquisiDir}/data/inquisitor/state + fi + + # Ensure the service owns the folders + chown -R ${inquisitorUser.name} ${inquisiDir} + + # Ensure the scp helper is present + if [ -f ${inquisiDir}/scp-helper ]; then + rm ${inquisiDir}/scp-helper + fi + ln -s -t ${inquisiDir} ${scp-helper}/bin/scp-helper + ''; + + # Create a run script for the server + inquisitorRun = pkgs.writeShellScriptBin "inquisitor-run.sh" '' + cd ${inquisiDir} + ${inquisitor}/bin/gunicorn \ + --bind=localhost:24133 \ + --workers=4 \ + --timeout 120 \ + --log-level debug \ + "inquisitor.app:wsgi()" + ''; + + # Create a wrapper to execute the cli as the service user + inquisitorWrapper = pkgs.writeShellScriptBin "inq" '' + sudo --user=inquisitor ${inquisitor}/bin/inquisitor "$@" + ''; +in +{ + users.users.inquisitor = inquisitorUser; + users.groups.inquisitor = {}; + + # Link the config in /etc to avoid envvar shenanigans + environment.etc."inquisitor.conf".source = "${inquisitorConfig}"; + + # Give all users the inq wrapper + environment.systemPackages = [ inquisitorWrapper ]; + + # Allow the sudo in the cli wrapper without password + security.sudo.extraRules = [{ + commands = [{ + command = "${inquisitor}/bin/inquisitor"; + options = [ "NOPASSWD" ]; + }]; + runAs = "${inquisitorUser.name}"; + groups = [ "users" ]; + }]; + + # Run the setup script on activation + system.activationScripts.inquisitorSetup = "${inquisitorSetup}/bin/inquisitor-setup.sh"; + + # Set up the inquisitor service + systemd.services.inquisitor = + { + description = "Inquisitor server"; + script = "${inquisitorRun}/bin/inquisitor-run.sh"; + serviceConfig = { + User = "${inquisitorUser.name}"; + Type = "simple"; + }; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + enable = true; + }; + + # Set up nginx to reverse proxy from the beatific url to the inq server + services.nginx.enable = true; + services.nginx.virtualHosts.inquisitorHost = { + listen = [ { addr = "10.7.3.1"; port = 80; } ]; + locations."/".extraConfig = '' + access_log /var/log/nginx/access.inquisitor.log; + proxy_buffering off; + proxy_pass http://localhost:24133/; + ''; + }; + + # Allow nginx through the firewall + networking.firewall = { + allowedTCPPorts = [ + 80 # http + 443 # https + ]; + }; + + # Enable cron, but don't set up any system cron jobs + # Inquisitor updates will be managed manually + services.cron.enable = true; +} diff --git a/redstring.nix b/redstring.nix new file mode 100644 index 0000000..5af765d --- /dev/null +++ b/redstring.nix @@ -0,0 +1,120 @@ +# redstring server module +{ pkgs, ... }: + +let + # Import package + redstringSource = builtins.fetchGit { + url = "https://git.alogoulogoi.com/Jaculabilis/redstring.git"; + ref = "master"; + rev = "91dd353ad1d48118452a949b15e100b3035bf297"; + }; + redstring = pkgs.callPackage redstringSource {}; + + # Define the data directory + redstringDir = "/var/lib/redstring/"; + redstringData = "${redstringDir}docs/"; + + # Define the service user + redstringUser = { + name = "redstring"; + description = "redstring service user"; + group = "redstring"; + isSystemUser = true; + }; + + # Create the public server config file in the nix store + publicConfigAttrs = { + root = redstringData; + edit = false; + }; + publicConfig = pkgs.writeTextFile { name = "redstring-config-external.json"; text = (builtins.toJSON publicConfigAttrs); }; + + # Create the private server config file in the nix store + privateConfig = pkgs.writeTextFile { + name = "redstring-config-internal.json"; + text = (builtins.toJSON { + root = redstringData; + edit = true; + }); + }; + + # Create a setup script to ensure the data directory exists + redstringSetup = pkgs.writeShellScriptBin "redstring-setup.sh" '' + # Ensure the service directory + ${pkgs.coreutils}/bin/mkdir -p ${redstringData} + + # Ensure ownership + chown -R ${redstringUser.name} ${redstringDir} + chmod 700 ${redstringDir} + ''; + + # Create a run script for the public server + publicRun = pkgs.writeShellScriptBin "redstring-run-external.sh" '' + cd ${redstringDir} + ${redstring}/bin/gunicorn \ + --bind=localhost:24144 \ + --workers=3 \ + --log-level debug \ + --env REDSTRING_CONFIG=${publicConfig} \ + "redstring.server:wsgi()" + ''; + + # Create a run script for the private server + privateRun = pkgs.writeShellScriptBin "redstring-run-internal.sh" '' + cd ${redstringDir}; + ${redstring}/bin/gunicorn \ + --bind=10.7.3.1:24145 \ + --workers=3 \ + --log-level debug \ + --env REDSTRING_CONFIG=${privateConfig} \ + "redstring.server:wsgi()" + ''; +in +{ + users.users.redstring = redstringUser; + users.groups.redstring = {}; + + # Run the setup script on activation + system.activationScripts.redstringSetup = "${redstringSetup}/bin/redstring-setup.sh"; + + # Set up the public redstring service + systemd.services."redstring-public" = + { + description = "redstring public read-only server"; + script = "${publicRun}/bin/redstring-run-external.sh"; + serviceConfig = { + User = "${redstringUser.name}"; + Type = "simple"; + }; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + enable = true; + }; + + # Set up the private redstring service + systemd.services."redstring-private" = + { + description = "redstring private editable server"; + script = "${privateRun}/bin/redstring-run-internal.sh"; + serviceConfig = { + User = redstringUser.name; + Type = "simple"; + }; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + enable = true; + }; + + # Configure nginx to forward to the public server at the docs subdomain + services.nginx.virtualHosts."docs.alogoulogoi.com" = { + enableACME = true; + forceSSL = true; + extraConfig = '' + access_log /var/log/nginx/access.docs.log; + ''; + locations."/".proxyPass = "http://localhost:24144"; + }; + + # Open the firewall to the private server's port + networking.firewall.allowedTCPPorts = [ 24145 ]; +}