diff --git a/README.md b/README.md index e206dc8..5f49e6c 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,9 @@ all of the various config files powering my blog server and other personal services + +## The stack + +everything runs on NixOS installed via [`nixos-bite.sh`]. the system is configured through NixOS' `configuration.nix`, services are run through `docker compose` with `caddy` as our reverse proxy + +[`nixos-bite.sh`]: https://codeberg.org/whitequark/nixos-bite diff --git a/etc/caddy/Caddyfile b/etc/caddy/Caddyfile new file mode 100644 index 0000000..0bf7b65 --- /dev/null +++ b/etc/caddy/Caddyfile @@ -0,0 +1,29 @@ +cosmichorror.dev { + root * /var/www/blog + encode + file_server + tls { + issuer acme { + profile shortlived + } + } +} + +www.cosmichorror.dev { + redir https://cosmichorror.dev{uri} permanent +} + +knot.cosmichorror.dev { + reverse_proxy localhost:5555 +} + +# TODO: normally this would be a `*.pds`, but that takes special DNS +# junk for TLS*, so instead we hardcode our only account +# *: either a special TXT DNS challenge or on-demand TLS +cosmic.pds.cosmichorror.dev pds.cosmichorror.dev { + reverse_proxy localhost:3000 +} + +status.cosmichorror.dev { + reverse_proxy localhost:3002 +} diff --git a/etc/compose.yaml b/etc/compose.yaml new file mode 100644 index 0000000..5fb2c26 --- /dev/null +++ b/etc/compose.yaml @@ -0,0 +1,64 @@ +services: + caddy: + container_name: caddy + image: caddy:2 + restart: unless-stopped + volumes: + - /etc/caddy:/etc/caddy:ro + - /var/www:/var/www:ro + - caddy_data:/data + - caddy_config:/config + # FIXME: the reverse proxies fail to connect when trying to map just the + # http(s) ports + network_mode: host + # ports: + # - "80:80" + # - "443:443" + # - "443:443/udp" + knot: + container_name: knot + image: knot-dev + restart: unless-stopped + volumes: + - /opt/knot/keys:/etc/ssh/keys + - /opt/knot/repos:/home/git/repositories + - /opt/knot/server:/app + - type: bind + source: /opt/knot/motd + target: /home/git/motd + read_only: true + ports: + - "5555:5555" + - "22:22" + env_file: + - /opt/knot/knot.env + pds: + container_name: pds + image: ghcr.io/bluesky-social/pds:0.4 + restart: unless-stopped + volumes: + - /opt/pds:/pds + ports: + - "3000:3000" + # smtp(s) for email + - "465:465" + - "2465:2465" + env_file: + - /opt/pds/pds.env + status: + container_name: status + image: louislam/uptime-kuma:2 + restart: unless-stopped + # avoids failing connections while launching + depends_on: + - caddy + - knot + - pds + volumes: + - /opt/status:/app/data + ports: + - "3002:3001" + +volumes: + caddy_data: + caddy_config: diff --git a/etc/nixos/configuration.nix b/etc/nixos/configuration.nix new file mode 100644 index 0000000..2a1ce76 --- /dev/null +++ b/etc/nixos/configuration.nix @@ -0,0 +1,200 @@ +{ config, pkgs, modulesPath, ... }: { + system.stateVersion = "25.11"; + + # Hardware + imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; + fileSystems."/" = { + device = "/dev/sda1"; + fsType = "ext4"; + }; + boot.loader.grub.device = "/dev/sda"; + boot.loader.timeout = 30; + boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "xen_blkfront" ]; + boot.initrd.kernelModules = [ "nvme" ]; + boot.tmp.cleanOnBoot = true; + + # Setup zswap + zramSwap.enable = true; + + # Networking + networking = { + useNetworkd = true; + usePredictableInterfaceNames = true; + hostName = "blog"; + domain = "vps.ovh.ca"; + firewall = { + enable = true; + allowedTCPPorts = [ + # ssh + 28412 + # http(s) + 80 443 + # knot (git) server ssh + 22 + # smtp(s) for PDS emails + 465 2465 + ]; + allowedUDPPorts = [ + # http3 + 443 + ]; + }; + }; + systemd.network = { + enable = true; + # doesn't work for some reason :( + wait-online.enable = false; + networks."40-wan" = { + matchConfig.Name = "enxfa163e169a96"; + address = [ "2607:5300:205:200::7538/128" "51.161.32.185/32" ]; + routes = [ + { Gateway = "51.161.32.1"; GatewayOnLink = true; } + { Gateway = "2607:5300:205:200::1"; } + ]; + dns = [ "2620:fe::fe" "9.9.9.9" ]; + }; + }; + + # Docker + virtualisation.docker.enable = true; + + # UTC + time.timeZone = "UTC"; + + # User + users = { + defaultUserShell = pkgs.zsh; + users.keeper = { + isNormalUser = true; + description = "A lone keeper in hostile waters"; + extraGroups = [ "docker" "wheel" ]; + initialPassword = "change me!"; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGL669ze5/aUwScu87ERyZtD4OCzgsgXnqbUuJs1CgN7 wintermute@ai" + ]; + packages = with pkgs; [ + bottom + fastfetch + git + nix-tree + ]; + }; + }; + + # Allow unfree packages + nixpkgs.config.allowUnfree = true; + + # We're a headless server, so trim down any of the graphical deps + nixpkgs.config.packageOverrides = pkgs: { + fastfetch = pkgs.fastfetch.override { + vulkanSupport = false; + waylandSupport = false; + x11Support = false; + }; + }; + + # Packages! + environment.systemPackages = with pkgs; [ + atuin + bat + caddy + eza + fd + htop + ripgrep + yazi + zoxide + ]; + + programs = { + neovim = { + enable = true; + defaultEditor = true; + viAlias = true; + vimAlias = true; + }; + starship.enable = true; + zoxide.enable = true; + zsh = { + enable = true; + autosuggestions.enable = true; + syntaxHighlighting.enable = true; + setOptions = [ "AUTO_CD" ]; + interactiveShellInit = '' + eval "$(atuin init zsh)" + # Custom vi escape + bindkey -v 'jj' vi-cmd-mode + + function y() { + local tmp="$(mktemp -t "yazi-cwd.XXXXXX")" cwd + yazi "$@" --cwd-file="$tmp" + if cwd="$(command cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then + builtin cd -- "$cwd" + fi + rm -f -- "$tmp" + } + ''; + }; + }; + + environment = { + sessionVariables = { + # Use `bat` as our `man` pager + PAGER = "bat"; + MANROFFOPT = "-c"; + BAT_PAGER = "less -FRX"; + MANPAGER = "sh -c 'col -bx | bat -l man -p'"; + }; + shellAliases = { + l = "eza -b --icons"; + ls = "eza -b --icons"; + ll = "eza -b --icons -l"; + la = "eza -b --icons -a"; + cl = "clear"; + nv = "nvim"; + }; + }; + + # Services + services = { + atuin.enable = true; + #caddy = { + # enable = true; + # virtualHosts."cosmichorror.dev".extraConfig = '' + # root * /var/www/blog + # encode + # file_server + # tls { + # issuer acme { + # profile shortlived + # } + # } + # ''; + # virtualHosts."www.cosmichorror.dev".extraConfig = '' + # redir https://cosmichorror.dev{uri} permanent + # ''; + # virtualHosts."status.cosmichorror.dev".extraConfig = '' + # reverse_proxy localhost:3002 + # ''; + # # TODO: normally this would be a `*.pds`, but that takes special DNS + # # stuff for TLS*, so instead we hardcode our one and only account + # # *: either a special TXT DNS challenge or on-demand TLS + # virtualHosts."cosmic.pds.cosmichorror.dev pds.cosmichorror.dev".extraConfig = '' + # reverse_proxy localhost:3000 + # ''; + # virtualHosts."knot.cosmichorror.dev".extraConfig = '' + # reverse_proxy localhost:5555 + # ''; + #}; + fail2ban.enable = true; + openssh = { + enable = true; + ports = [ 28412 ]; + settings = { + AllowUsers = [ "keeper" ]; + PasswordAuthentication = false; + PermitRootLogin = "no"; + }; + }; + }; +}