summaryrefslogtreecommitdiff
path: root/hosts
diff options
context:
space:
mode:
authorLinnnus <[email protected]>2024-02-20 19:00:53 +0100
committerLinnnus <[email protected]>2024-02-20 19:01:44 +0100
commit274e08f50faffe1b8e4a760811b0a12450eae719 (patch)
tree393449e81f21b6f6b1ea7a701cef0f740cc3b757 /hosts
parent1bbdd3f63a9d8c46b1772cbf2ad9fd83d7ef213b (diff)
Merge 'reorg' into 'main'
This patch moves in the reorganizational work done on the reorg branch, mainly: * Move host-specific modules into hosts/<host>/<module> * Break up HM config See the reorg branch for the individual commits.
Diffstat (limited to 'hosts')
-rw-r--r--hosts/ahmed/cloudflare-proxy/default.nix88
-rw-r--r--hosts/ahmed/configuration.nix43
-rw-r--r--hosts/ahmed/disable-screen/default.nix61
-rw-r--r--hosts/ahmed/duksebot/default.nix61
-rw-r--r--hosts/ahmed/forsvarsarper/default.nix58
-rw-r--r--hosts/ahmed/forsvarsarper/script.py28
-rw-r--r--hosts/ahmed/git.linus.onl/about.html5
-rw-r--r--hosts/ahmed/git.linus.onl/default.nix84
-rw-r--r--hosts/ahmed/hellohtml.linus.onl/default.nix51
-rw-r--r--hosts/ahmed/home/default.nix9
-rw-r--r--hosts/ahmed/linus.onl/default.nix93
-rw-r--r--hosts/ahmed/nofitications.linus.onl/default.nix24
-rw-r--r--hosts/ahmed/ssh/default.nix (renamed from hosts/ahmed/ssh.nix)0
-rw-r--r--hosts/muhammed/configuration.nix4
-rw-r--r--hosts/muhammed/home/default.nix10
-rw-r--r--hosts/muhammed/home/iterm2/default.nix26
-rw-r--r--hosts/muhammed/home/neovim/conjure.nix71
-rw-r--r--hosts/muhammed/home/neovim/default.nix8
-rw-r--r--hosts/muhammed/home/neovim/lsp.nix107
-rw-r--r--hosts/muhammed/home/noweb/default.nix12
20 files changed, 813 insertions, 30 deletions
diff --git a/hosts/ahmed/cloudflare-proxy/default.nix b/hosts/ahmed/cloudflare-proxy/default.nix
new file mode 100644
index 0000000..45ccaa6
--- /dev/null
+++ b/hosts/ahmed/cloudflare-proxy/default.nix
@@ -0,0 +1,88 @@
+# This module adds some extra configuration useful when running behid a Cloudflare Proxy.
+# Mainly, it blocks all incomming conncections on relevant ports that aren't
+# coming from an official CloudFlare domain.
+{
+ config,
+ lib,
+ pkgs,
+ metadata,
+ ...
+}: let
+ # TODO: What happens when these get out of date??? Huh??? You little pissbaby
+ fileToList = x: lib.strings.splitString "\n" (builtins.readFile x);
+ cfipv4 = fileToList (pkgs.fetchurl {
+ url = "https://www.cloudflare.com/ips-v4";
+ hash = "sha256-8Cxtg7wBqwroV3Fg4DbXAMdFU1m84FTfiE5dfZ5Onns=";
+ });
+ cfipv6 = fileToList (pkgs.fetchurl {
+ url = "https://www.cloudflare.com/ips-v6";
+ hash = "sha256-np054+g7rQDE3sr9U8Y/piAp89ldto3pN9K+KCNMoKk=";
+ });
+
+ IPv4Whitelist = [metadata.hosts.muhammed.ipAddress];
+ IPv6Whitelist = [];
+in {
+ config = {
+ # Teach NGINX how to extract the proxied IP from proxied requests.
+ #
+ # See: https://nixos.wiki/wiki/Nginx#Using_realIP_when_behind_CloudFlare_or_other_CDN
+ services.nginx.commonHttpConfig = let
+ realIpsFromList = lib.strings.concatMapStringsSep "\n" (x: "set_real_ip_from ${x};");
+ in ''
+ ${realIpsFromList cfipv4}
+ ${realIpsFromList cfipv6}
+ real_ip_header CF-Connecting-IP;
+ '';
+
+ # Block non-Cloudflare IP addresses.
+ networking.firewall = let
+ chain = "cloudflare-whitelist";
+ in {
+ extraCommands = let
+ allow-interface = lib.strings.concatMapStringsSep "\n" (i: ''ip46tables --append ${chain} --in-interface ${i} --jump RETURN'');
+ allow-ip = cmd: lib.strings.concatMapStringsSep "\n" (r: ''${cmd} --append ${chain} --source ${r} --jump RETURN'');
+ in ''
+ # Flush the old firewall rules. This behavior mirrors the default firewall service.
+ # See: https://github.com/NixOS/nixpkgs/blob/ac911bf685eecc17c2df5b21bdf32678b9f88c92/nixos/modules/services/networking/firewall-iptables.nix#L59-L66
+ ip46tables --delete INPUT --protocol tcp --destination-port 80 --syn --jump ${chain} 2>/dev/null || true
+ ip46tables --delete INPUT --protocol tcp --destination-port 443 --syn --jump ${chain} 2>/dev/null || true
+ ip46tables --flush ${chain} || true
+ ip46tables --delete-chain ${chain} || true
+
+ # Create a chain that only allows whitelisted IPs through.
+ ip46tables --new-chain ${chain}
+
+ # Allow trusted interfaces through.
+ ${allow-interface config.networking.firewall.trustedInterfaces}
+
+ # Allow local whitelisted IPs through
+ ${allow-ip "iptables" IPv4Whitelist}
+ ${allow-ip "ip6tables" IPv6Whitelist}
+
+ # Allow Cloudflare's IP ranges through.
+ ${allow-ip "iptables" cfipv4}
+ ${allow-ip "ip6tables" cfipv6}
+
+ # Everything else is dropped.
+ #
+ # TODO: I would like to use `nixos-fw-log-refuse` here, but I keep
+ # running into weird issues when reloading the firewall.
+ # Something about the table not being deleted properly.
+ ip46tables --append ${chain} --jump DROP
+
+ # Inject our chain as the first check in INPUT (before nixos-fw).
+ # We want to capture any new incomming TCP connections.
+ ip46tables --insert INPUT 1 --protocol tcp --destination-port 80 --syn --jump ${chain}
+ ip46tables --insert INPUT 1 --protocol tcp --destination-port 443 --syn --jump ${chain}
+ '';
+ extraStopCommands = ''
+ # Clean up added rulesets (${chain}). This mirrors the behavior of the
+ # default firewall at the time of writing.
+ #
+ # See: https://github.com/NixOS/nixpkgs/blob/ac911bf685eecc17c2df5b21bdf32678b9f88c92/nixos/modules/services/networking/firewall-iptables.nix#L218-L219
+ ip46tables --delete INPUT --protocol tcp --destination-port 80 --syn --jump ${chain} 2>/dev/null || true
+ ip46tables --delete INPUT --protocol tcp --destination-port 443 --syn --jump ${chain} 2>/dev/null || true
+ '';
+ };
+ };
+}
diff --git a/hosts/ahmed/configuration.nix b/hosts/ahmed/configuration.nix
index e0eb202..27c35eb 100644
--- a/hosts/ahmed/configuration.nix
+++ b/hosts/ahmed/configuration.nix
@@ -3,12 +3,21 @@
{
config,
pkgs,
- metadata,
...
}: {
imports = [
./hardware-configuration.nix
- ./ssh.nix
+
+ ./cloudflare-proxy
+ ./disable-screen
+ ./duksebot
+ ./forsvarsarper
+ ./git.linus.onl
+ ./hellohtml.linus.onl
+ ./linus.onl
+ ./nofitications.linus.onl
+ ./ssh
+ ./home
];
# Create the main user.
@@ -47,31 +56,11 @@
};
services.cloudflare-dyndns.domains = ["minecraft.linus.onl"];
- # Set up dukse server. Det er satme hårdt at være overduksepåmindelsesansvarlig.
- services.duksebot.enable = true;
-
# Virtual hosts.
+ # Each module for a HTTP service will register a virtual host.
services.nginx.enable = true;
- modules."linus.onl" = {
- enable = true;
- useACME = true;
- };
- modules."notifications.linus.onl" = {
- enable = true;
- useACME = true;
- };
- modules."git.linus.onl" = {
- enable = true;
- useACME = true;
- };
- modules."hellohtml.linus.onl" = {
- enable = true;
- useACME = true;
- };
-
- services.forsvarsarper.enable = true;
- # Configure ACME for various HTTPS services.
+ # Configure ACME. This is used by various HTTP services through the NGINX virtual hosts.
security.acme = {
acceptTerms = true;
defaults.email = "linusvejlo+${config.networking.hostName}[email protected]";
@@ -94,12 +83,6 @@
# Listen for HTTP connections.
networking.firewall.allowedTCPPorts = [80 443];
- # We are running behind CF proxy.
- modules.cloudflare-proxy = {
- enable = true;
- firewall.IPv4Whitelist = [metadata.hosts.muhammed.ipAddress];
- };
-
# 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
diff --git a/hosts/ahmed/disable-screen/default.nix b/hosts/ahmed/disable-screen/default.nix
new file mode 100644
index 0000000..638437a
--- /dev/null
+++ b/hosts/ahmed/disable-screen/default.nix
@@ -0,0 +1,61 @@
+# This file defines some configuration options which disable the screen. This
+# is only relevant because this host is an old laptop running as a server.
+{
+ lib,
+ config,
+ ...
+}: let
+ inherit (lib) mkEnableOption mkOption mkIf types;
+
+ cfg = config.services.disable-screen;
+in {
+ options.services.disable-screen = {
+ enable = mkEnableOption "disable screen";
+
+ device-path = mkOption {
+ description = "Path to the device in the `/sys` file system.";
+ type = types.str;
+ example = "/sys/class/backlight/intel_backlight";
+ };
+
+ device-unit = mkOption {
+ description = "The systemd device unit that corresponds to the device speciefied in `device-path`.";
+ type = types.str;
+ example = "sys-devices-pci...-intel_backligt.device";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ # Disable sleep on lid close.
+ services.logind = let
+ lidSwitchAction = "ignore";
+ in {
+ lidSwitchExternalPower = lidSwitchAction;
+ lidSwitchDocked = lidSwitchAction;
+ lidSwitch = lidSwitchAction;
+ };
+
+ # Don't store screen brightness between boots. We always want to turn off the
+ # screen.
+ #
+ # See: https://wiki.archlinux.org/title/backlight#Save_and_restore_functionality
+ # See: https://github.com/NixOS/nixpkgs/blob/990398921f677615c0732d704857484b84c6c888/nixos/modules/system/boot/systemd.nix#L97-L101
+ systemd.suppressedSystemUnits = ["[email protected]"];
+
+ # FIXME: Figure out how to enable screen when on-device debugging is necessary.
+ # Create a new service which turns off the display on boot.
+ #
+ # See: https://nixos.wiki/wiki/Backlight#.2Fsys.2Fclass.2Fbacklight.2F...
+ # See: https://superuser.com/questions/851846/how-to-write-a-systemd-service-that-depends-on-a-device-being-present
+ systemd.services.disable-screen = {
+ requires = [cfg.device-unit];
+ after = [cfg.device-unit];
+ wantedBy = [cfg.device-unit];
+
+ serviceConfig.Type = "oneshot";
+ script = ''
+ tee ${cfg.device-path}/brightness <<<0
+ '';
+ };
+ };
+}
diff --git a/hosts/ahmed/duksebot/default.nix b/hosts/ahmed/duksebot/default.nix
new file mode 100644
index 0000000..f15b0ff
--- /dev/null
+++ b/hosts/ahmed/duksebot/default.nix
@@ -0,0 +1,61 @@
+# This module defines systemd unit which runs a script that sends Discrord
+# messages. I use it to notify my classmates about who's on cleaning duty. You
+# are probably not interested in this.
+
+{
+ config,
+ pkgs,
+ ...
+}: let
+ # What script to run.
+ package = pkgs.duksebot;
+in {
+ config = {
+ # Create a user to run the server under.
+ users.users.duksebot = {
+ description = "Runs daily dukse reminder";
+ group = "duksebot";
+ isSystemUser = true;
+ home = "/srv/duksebot";
+ createHome = true;
+ };
+ users.groups.duksebot = {};
+
+ age.secrets.duksebot-env = {
+ file = ../../../secrets/duksebot.env.age;
+ owner = config.users.users.duksebot.name;
+ group = config.users.users.duksebot.group;
+ mode = "0440";
+ };
+
+ # Create a service which simply runs script. This will be invoked by our timer.
+ systemd.services.duksebot = {
+ serviceConfig = {
+ # We only want to run this once every time the timer triggers it.
+ Type = "oneshot";
+ # Run as the user we created above.
+ User = "duksebot";
+ Group = "duksebot";
+ WorkingDirectory = config.users.users.duksebot.home;
+ };
+ script = ''
+ # Load the secret environment variables.
+ export $(grep -v '^#' ${config.age.secrets.duksebot-env.path} | xargs)
+ # Kick off.
+ exec "${package}"/bin/duksebot
+ '';
+ };
+
+ # Create a timer to activate our oneshot service.
+ systemd.timers.duksebot = {
+ wantedBy = ["timers.target"];
+ partOf = ["duksebot.service"];
+ after = ["network-online.target"];
+ wants = ["network-online.target"];
+ timerConfig = {
+ OnCalendar = "*-*-* 7:00:00";
+ Unit = "duksebot.service";
+ };
+ };
+ };
+}
diff --git a/hosts/ahmed/forsvarsarper/default.nix b/hosts/ahmed/forsvarsarper/default.nix
new file mode 100644
index 0000000..c1c6163
--- /dev/null
+++ b/hosts/ahmed/forsvarsarper/default.nix
@@ -0,0 +1,58 @@
+# This module defines an on-demand minecraft server service which turns off the
+# server when it's not being used.
+{
+ config,
+ pkgs,
+ ...
+}:{
+ config = {
+ # Create a user to run the server under.
+ users.users.forsvarsarper = {
+ description = "Runs daily scan for tests";
+ group = "forsvarsarper";
+ isSystemUser = true;
+ home = "/srv/forsvarsarper";
+ createHome = true;
+ };
+ users.groups.forsvarsarper = {};
+
+ age.secrets.forsvarsarper-env = {
+ file = ../../../secrets/forsvarsarper.env.age;
+ owner = config.users.users.forsvarsarper.name;
+ group = config.users.users.forsvarsarper.group;
+ mode = "0440";
+ };
+
+ # Create a service which simply runs script. This will be invoked by our timer.
+ systemd.services.forsvarsarper = {
+ serviceConfig = {
+ # We only want to run this once every time the timer triggers it.
+ Type = "oneshot";
+ # Run as the user we created above.
+ User = "forsvarsarper";
+ Group = "forsvarsarper";
+ WorkingDirectory = config.users.users.forsvarsarper.home;
+ };
+ script = let
+ python3' = pkgs.python3.withPackages (ps: [ps.requests]);
+ in ''
+ # Load the secret environment variables.
+ export $(grep -v '^#' ${config.age.secrets.forsvarsarper-env.path} | xargs)
+ # Kick off.
+ exec ${python3'}/bin/python3 ${./script.py}
+ '';
+ };
+
+ # Create a timer to activate our oneshot service.
+ systemd.timers.forsvarsarper = {
+ wantedBy = ["timers.target"];
+ partOf = ["forsvarsarper.service"];
+ after = ["network-online.target"];
+ wants = ["network-online.target"];
+ timerConfig = {
+ OnCalendar = "*-*-* 8:00:00";
+ Unit = "forsvarsarper.service";
+ };
+ };
+ };
+}
diff --git a/hosts/ahmed/forsvarsarper/script.py b/hosts/ahmed/forsvarsarper/script.py
new file mode 100644
index 0000000..7f12508
--- /dev/null
+++ b/hosts/ahmed/forsvarsarper/script.py
@@ -0,0 +1,28 @@
+import requests
+import os
+
+URL = "https://karriere.forsvaret.dk/varnepligt/varnepligten/cybervarnepligt/"
+TARGET_PHRASE = "Der er p&aring; nuv&aelig;rende tidspunkt ikke planlagt nogen afpr&oslash;vninger."
+
+try:
+ response = requests.get(URL);
+ print(f"Forespørgsel til {URL} gav status {response.status_code}")
+except:
+ message = "nejj den er ødelagt"
+else:
+ if TARGET_PHRASE in response.text:
+ message = "der er stadig ikke planlagt nogle afprøvninger"
+ else:
+ message = "noget har ændret sig på siden!!"
+ print(response.text)
+
+token = os.getenv("TOKEN")
+data = {
+ "title": "forsvaret status",
+ "message": message,
+ "url": URL,
+}
+response = requests.post(f"https://notifications.linus.onl/api/send-notification/{token}", json=data)
+print(f"Forespørgsel til at sende notifikation gav status {response.status_code}")
+response.raise_for_status()
+
diff --git a/hosts/ahmed/git.linus.onl/about.html b/hosts/ahmed/git.linus.onl/about.html
new file mode 100644
index 0000000..2d18ca4
--- /dev/null
+++ b/hosts/ahmed/git.linus.onl/about.html
@@ -0,0 +1,5 @@
+<p>Welcome! This is where i keep my public repositories.</p>
+<br>
+<br>
+<p>idk.</p>
+<p>what do i say here?</p>
diff --git a/hosts/ahmed/git.linus.onl/default.nix b/hosts/ahmed/git.linus.onl/default.nix
new file mode 100644
index 0000000..46c74e9
--- /dev/null
+++ b/hosts/ahmed/git.linus.onl/default.nix
@@ -0,0 +1,84 @@
+{
+ config,
+ pkgs,
+ metadata,
+ ...
+}: let
+ git-shell = "${pkgs.gitMinimal}/bin/git-shell";
+
+ # Enables HTTPS stuff.
+ useACME = true;
+
+ # Where repositories will be stored.
+ location = "/srv/git";
+in {
+ config = {
+ # Create a user which
+ # See: https://git-scm.com/book/en/v2/Git-on-the-Server-Setting-Up-the-Server
+ users.users.git = {
+ description = "Git server user";
+ isSystemUser = true;
+ group = "git";
+
+ # FIXME: Is serving the home-directory of a user (indirectly through CGit) a bad idea?
+ home = location;
+ createHome = false;
+
+ # Restrict this user to Git-related activities.
+ # See: https://git-scm.com/docs/git-shell
+ shell = git-shell;
+
+ # List of users who can ssh into this server and write to stuff. We add
+ # some restrictions on what users can do on the server. This works in
+ # tandem with the custom shell.
+ openssh.authorizedKeys.keys =
+ map (key: "no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ${key}")
+ [
+ metadata.hosts.muhammed.sshPubKey
+ ];
+ };
+ users.groups.git = {};
+
+ environment.shells = [git-shell];
+
+ # Create repo directory. It must be readable to NGINX.
+ # NOTE: If location != "/srv/git" you may want to change this!
+ # See: https://git.zx2c4.com/cgit/about/faq#why-doesnt-cgit-findshow-my-repo
+ system.activationScripts.create-cgit-scan-path = ''
+ mkdir -p ${location}
+ chown ${toString config.users.users.git.name} ${location}
+ chgrp ${toString config.users.groups.git.name} ${location}
+ chmod 755 ${location}
+ '';
+
+ # Public git viewer.
+ services.cgit."git.linus.onl" = {
+ enable = true;
+ scanPath = location;
+ settings = {
+ root-title = "Linus' public projects";
+ root-desc = "hello yes this is the git server";
+ root-readme = toString ./about.html;
+ };
+ extraConfig = ''
+ readme=:README.md
+ readme=:README.rst
+ readme=:README.text
+ readme=:README.txt
+ readme=:readme.md
+ readme=:readme.rst
+ readme=:readme.text
+ readme=:readme.txt
+ '';
+ };
+
+ # Register domain name.
+ services.cloudflare-dyndns.domains = ["git.linus.onl"];
+
+ # The CGit service creates the virtual host, but it does not enable ACME.
+ services.nginx.virtualHosts."git.linus.onl" = {
+ enableACME = useACME;
+ forceSSL = useACME;
+ };
+ };
+}
diff --git a/hosts/ahmed/hellohtml.linus.onl/default.nix b/hosts/ahmed/hellohtml.linus.onl/default.nix
new file mode 100644
index 0000000..2d09788
--- /dev/null
+++ b/hosts/ahmed/hellohtml.linus.onl/default.nix
@@ -0,0 +1,51 @@
+# This module defines the HelloHTML web server. It extends the NGINX config
+# with a virtual server that proxies the local HelloHTML service.
+
+{ ... }: let
+ useACME = true;
+in {
+ config = {
+ # Start service listening on socket /tmp/hellohtml.sock
+ services.hellohtml = {
+ enable = true;
+ };
+
+ # Register domain name.
+ services.cloudflare-dyndns.domains = ["hellohtml.linus.onl"];
+
+ # Use NGINX as reverse proxy.
+ services.nginx.virtualHosts."hellohtml.linus.onl" = {
+ enableACME = useACME;
+ forceSSL = useACME;
+ locations."/" = rec {
+ proxyPass = "http://localhost:8538";
+ # Disable settings that might mess with the text/event-stream response of the /listen/:id endpoint.
+ # NOTE: These settings work in tanden with Cloudflare Proxy settings descibed here:
+ # https://blog.devops.dev/implementing-server-sent-events-with-fastapi-nginx-and-cloudflare-10ede1dffc18
+ extraConfig = ''
+ location /listen/ {
+ # Have to duplicate this here, as this directive is not inherited.
+ # See: https://blog.martinfjordvald.com/understanding-the-nginx-configuration-inheritance-model/
+ # See: https://serverfault.com/q/1082562
+ proxy_pass ${proxyPass};
+ # Disable connection header.
+ # See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection
+ # See: https://www.nginx.com/blog/avoiding-top-10-nginx-configuration-mistakes/#no-keepalives
+ proxy_set_header Connection \'\';
+ # Disable buffering. This is crucial for SSE to ensure that
+ # messages are sent immediately without waiting for a buffer to
+ # fill.
+ proxy_buffering off;
+ # Disable caching to ensure that all messages are sent and received
+ # in real-time without being cached by the proxy.
+ proxy_cache off;
+ # Set a long timeout for reading from the proxy to prevent the
+ # connection from timing out. You may need to adjust this value
+ # based on your specific requirements.
+ proxy_read_timeout 86400;
+ }
+ '';
+ };
+ };
+ };
+}
diff --git a/hosts/ahmed/home/default.nix b/hosts/ahmed/home/default.nix
new file mode 100644
index 0000000..a031e96
--- /dev/null
+++ b/hosts/ahmed/home/default.nix
@@ -0,0 +1,9 @@
+# Here we extend the HM user defined in `home/default.nix`. All the global HM
+# stuff is defined in there. The only imports here are specific to this host.
+{...}: {
+ home-manager.users.linus = {
+ imports = [
+ # empty for now
+ ];
+ };
+}
diff --git a/hosts/ahmed/linus.onl/default.nix b/hosts/ahmed/linus.onl/default.nix
new file mode 100644
index 0000000..c31eb73
--- /dev/null
+++ b/hosts/ahmed/linus.onl/default.nix
@@ -0,0 +1,93 @@
+{
+ pkgs,
+ lib,
+ ...
+}: let
+ # The domain to serve. Also kinda embedded in the name of the module??
+ domain = "linus.onl";
+
+ # Enable HTTPS stuff.
+ useACME = true;
+in {
+ config = {
+ # Create a user to run the build script under.
+ users.users."${domain}-builder" = {
+ description = "builds ${domain}";
+ group = "${domain}-builder";
+ isSystemUser = true;
+ };
+ users.groups."${domain}-builder" = {};
+
+ # Create the output directory.
+ system.activationScripts."${domain}-create-www" = lib.stringAfter ["var"] ''
+ mkdir -p /var/www/${domain}
+ chown ${domain}-builder /var/www/${domain}
+ chgrp ${domain}-builder /var/www/${domain}
+ chmod 0755 /var/www/${domain}
+ '';
+
+ # Create a systemd service which rebuild the site regularly.
+ #
+ # This can't be done using Nix because the site relies on the git build and
+ # there are some inherent difficulties with including .git/ in the
+ # inputSource for derivations.
+ #
+ # See: https://github.com/NixOS/nix/issues/6900
+ # See: https://github.com/NixOS/nixpkgs/issues/8567
+ #
+ # TODO: Integrate rebuilding with GitHub webhooks to rebuild on push.
+ systemd.services."${domain}-source" = {
+ description = "generate https://${domain} source";
+
+ serviceConfig = {
+ Type = "oneshot";
+ User = "${domain}-builder";
+ Group = "${domain}-builder";
+ };
+ startAt = "*-*-* *:00/5:00";
+
+ path = with pkgs; [
+ git
+ rsync
+ coreutils-full
+ tcl-8_5
+ gnumake
+ ];
+ environment.TCLLIBPATH = "$TCLLIBPATH ${pkgs.tcl-cmark}/lib/tclcmark1.0";
+ script = ''
+ set -ex
+ tmpdir="$(mktemp -d -t linus.onl-source.XXXXXXXXXXXX)"
+ cd "$tmpdir"
+ trap 'rm -rf $tmpdir' EXIT
+ # TODO: Only do minimal possible cloning
+ git clone https://github.com/linnnus/${domain} .
+ make _build
+ rsync --archive --delete _build/ /var/www/${domain}
+ '';
+
+ # TODO: Harden service
+
+ # Network must be online for us to check.
+ after = ["network-online.target"];
+ requires = ["network-online.target"];
+
+ # We must generate some files for NGINX to serve, so this should be run
+ # before NGINX.
+ before = ["nginx.service"];
+ wantedBy = ["nginx.service"];
+ };
+
+ # Register domain name with ddns.
+ services.cloudflare-dyndns.domains = [domain];
+
+ # Register virtual host.
+ services.nginx = {
+ virtualHosts."${domain}" = {
+ # NOTE: 'forceSSL' will cause an infite loop, if the cloudflare proxy does NOT connect over HTTPS.
+ enableACME = useACME;
+ forceSSL = useACME;
+ root = "/var/www/${domain}";
+ };
+ };
+ };
+}
diff --git a/hosts/ahmed/nofitications.linus.onl/default.nix b/hosts/ahmed/nofitications.linus.onl/default.nix
new file mode 100644
index 0000000..f3ab04f
--- /dev/null
+++ b/hosts/ahmed/nofitications.linus.onl/default.nix
@@ -0,0 +1,24 @@
+{ ... }: let
+ # Enable HTTPS stuff.
+ useACME = true;
+in {
+ config = {
+ # Start the proxied service.
+ services.push-notification-api = {
+ enable = true;
+ };
+
+ # Register domain name.
+ services.cloudflare-dyndns.domains = ["notifications.linus.onl"];
+
+ # Use NGINX as reverse proxy.
+ services.nginx.virtualHosts."notifications.linus.onl" = {
+ enableACME = useACME;
+ forceSSL = useACME;
+ locations."/" = {
+ recommendedProxySettings = true;
+ proxyPass = "http://unix:/run/push-notification-api.sock";
+ };
+ };
+ };
+}
diff --git a/hosts/ahmed/ssh.nix b/hosts/ahmed/ssh/default.nix
index 3c6b7ad..3c6b7ad 100644
--- a/hosts/ahmed/ssh.nix
+++ b/hosts/ahmed/ssh/default.nix
diff --git a/hosts/muhammed/configuration.nix b/hosts/muhammed/configuration.nix
index 9119a55..c629b22 100644
--- a/hosts/muhammed/configuration.nix
+++ b/hosts/muhammed/configuration.nix
@@ -1,5 +1,9 @@
# This file contains the configuration for my Macbook Pro.
{flakeInputs, ...}: {
+ imports = [
+ ./home
+ ];
+
# Specify the location of this configuration file. Very meta.
environment.darwinConfig = flakeInputs.self + "/hosts/muhammed/configuration.nix";
diff --git a/hosts/muhammed/home/default.nix b/hosts/muhammed/home/default.nix
new file mode 100644
index 0000000..b0b126c
--- /dev/null
+++ b/hosts/muhammed/home/default.nix
@@ -0,0 +1,10 @@
+# Here we extend the HM user defined in `home/default.nix`. All the global HM
+# stuff is defined in there.
+{...}: {
+ home-manager.users.linus = {
+ imports = [
+ ./iterm2
+ ./noweb
+ ];
+ };
+}
diff --git a/hosts/muhammed/home/iterm2/default.nix b/hosts/muhammed/home/iterm2/default.nix
new file mode 100644
index 0000000..d868e57
--- /dev/null
+++ b/hosts/muhammed/home/iterm2/default.nix
@@ -0,0 +1,26 @@
+# This file configures iterm2. Note that the actual definition of iTerm2 for
+# home-manager is in `modules/home-manager/iterm2`. *That* file declares
+# `options.programs.iterm2.enable`.
+{
+ pkgs,
+ lib,
+ ...
+}: let
+ inherit (lib) mkIf;
+ inherit (pkgs.stdenv) isDarwin;
+in {
+ config = mkIf isDarwin {
+ home.packages = with pkgs; [imgcat];
+
+ programs.iterm2 = {
+ enable = true;
+ # config = {
+ # # Use the minimal tab style.
+ # # See: https://github.com/gnachman/iTerm2/blob/bd40fba0611fa94684dadf2478625f2a93eb6e47/sources/iTermPreferences.h#L29
+ # TabStyleWithAutomaticOption = 5;
+ # };
+
+ shellIntegration.enableZshIntegration = true;
+ };
+ };
+}
diff --git a/hosts/muhammed/home/neovim/conjure.nix b/hosts/muhammed/home/neovim/conjure.nix
new file mode 100644
index 0000000..0acfadd
--- /dev/null
+++ b/hosts/muhammed/home/neovim/conjure.nix
@@ -0,0 +1,71 @@
+{pkgs, ...}: {
+ programs.neovim.plugins = [
+ {
+ # Add interactive repl-like environment.
+ # See also the addition of cmp-conjure in `completion.nix`.
+ # See also the addition of clojure in `dev-utils/default.nix`.
+ plugin = pkgs.vimPlugins.conjure;
+ type = "lua";
+ config = ''
+ local start_clj_repl = "StartCljRepl";
+ local start_lein_repl = "StartLeinRepl";
+
+ -- Create a command to launch nRepl for Clojure support.
+ -- See: https://github.com/Olical/conjure/wiki/Quick-start:-Clojure
+ vim.api.nvim_create_user_command(start_clj_repl, function()
+ local id = vim.fn.jobstart({
+ "${pkgs.clojure}/bin/clj",
+ "-Sdeps",
+ '{:deps {nrepl/nrepl {:mvn/version "1.0.0"} cider/cider-nrepl {:mvn/version "0.40.0"}}}',
+ "--main",
+ "nrepl.cmdline",
+ "--middleware",
+ '["cider.nrepl/cider-middleware"]',
+ "--interactive",
+ })
+ print("Started nRepl job #" .. id)
+ end, {
+ desc = "Starts an nRepl session in the current directory using clj.",
+ })
+
+ vim.api.nvim_create_user_command(start_lein_repl, function()
+ local id = vim.fn.jobstart({
+ "${pkgs.leiningen}/bin/lein",
+ "repl",
+ })
+ print("Started nRepl job #" .. id)
+ end, {
+ desc = "Starts an nRepl session in the current directory using Lein.",
+ })
+
+ -- Launch nRepl when any clojure file is started.
+ -- vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+ -- pattern = "*.clj",
+ -- command = start_clj_repl,
+ -- });
+
+ -- Use Guile to evaluate scheme buffers.
+ local start_guile_repl = "StartGuileRepl";
+ local sock_path = "/tmp/guile-repl.sock"
+ vim.g["conjure#filetype#scheme"] = "conjure.client.guile.socket"
+ vim.g["conjure#client#guile#socket#pipename"] = sock_path
+ vim.api.nvim_create_user_command(start_guile_repl, function()
+ local id = vim.fn.jobstart({
+ "${pkgs.guile}/bin/guile",
+ "--listen=" .. sock_path,
+ })
+ print("Started Guile job #" .. id)
+ end, {
+ desc = "Starts an Guile repl session listening on " .. sock_path,
+ })
+
+ -- Jump to bottom of log when new evaluation happens
+ -- See: https://github.com/Olical/conjure/blob/58c46d1f4999679659a5918284b574c266a7ac83/doc/conjure.txt#L872
+ vim.cmd [[autocmd User ConjureEval if expand("%:t") =~ "^conjure-log-" | exec "normal G" | endif]]
+ '';
+ }
+
+ # Compe plugin to interact with conjure.
+ pkgs.vimPlugins.cmp-conjure
+ ];
+}
diff --git a/hosts/muhammed/home/neovim/default.nix b/hosts/muhammed/home/neovim/default.nix
new file mode 100644
index 0000000..905896c
--- /dev/null
+++ b/hosts/muhammed/home/neovim/default.nix
@@ -0,0 +1,8 @@
+# Once again we extend the global configuration defined in `home/neovim/` with
+# some stuff specific to this host (mainly development stuff).
+{...}: {
+ imports = [
+ ./lsp.nix
+ ./conjure.nix
+ ];
+}
diff --git a/hosts/muhammed/home/neovim/lsp.nix b/hosts/muhammed/home/neovim/lsp.nix
new file mode 100644
index 0000000..06745da
--- /dev/null
+++ b/hosts/muhammed/home/neovim/lsp.nix
@@ -0,0 +1,107 @@
+# This module sets up LSP server configurations for Neovim.
+{pkgs, ...}: {
+ programs.neovim.plugins = [
+ {
+ plugin = pkgs.vimPlugins.nvim-lspconfig;
+ type = "lua";
+ config = ''
+ local lspconfig = require("lspconfig")
+ local util = require("lspconfig.util")
+
+ -- Mappings.
+ -- See `:help vim.diagnostic.*` for documentation on any of the below functions
+ local opts = { noremap=true, silent=true }
+ vim.keymap.set('n', '<leader>e', vim.diagnostic.open_float, opts)
+ vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts)
+ vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts)
+ vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist, opts)
+
+ -- Use an on_attach function to only map the following keys
+ -- after the language server attaches to the current buffer
+ local on_attach = function(client, bufnr)
+ -- Enable completion triggered by <c-x><c-o>
+ vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
+
+ -- Mappings.
+ -- See `:help vim.lsp.*` for documentation on any of the below functions
+ local bufopts = { noremap=true, silent=true, buffer=bufnr }
+ vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, bufopts)
+ vim.keymap.set('n', 'gd', vim.lsp.buf.definition, bufopts)
+ vim.keymap.set('n', 'K', vim.lsp.buf.hover, bufopts)
+ vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, bufopts)
+ vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, bufopts)
+ vim.keymap.set('n', '<leader>wa', vim.lsp.buf.add_workspace_folder, bufopts)
+ vim.keymap.set('n', '<leader>wr', vim.lsp.buf.remove_workspace_folder, bufopts)
+ vim.keymap.set('n', '<leader>wl', function()
+ print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
+ end, bufopts)
+ vim.keymap.set('n', '<leader>D', vim.lsp.buf.type_definition, bufopts)
+ vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, bufopts)
+ vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, bufopts)
+ vim.keymap.set('n', 'gr', vim.lsp.buf.references, bufopts)
+ vim.keymap.set('n', '<leader>f', function() vim.lsp.buf.format { async = true } end, bufopts)
+ vim.keymap.set('n', '<leader>s', function() vim.cmd[[ClangdSwitchSourceHeader]] end, bufopts)
+ end
+
+ -- Use a loop to conveniently call 'setup' on multiple servers and
+ -- map buffer local keybindings when the language server attaches
+ local servers = {
+ pyright = { cmd = { "${pkgs.pyright}/bin/pyright-langserver", "--stdio" } },
+ nixd = { cmd = { "${pkgs.nixd}/bin/nixd" } },
+ denols = {
+ init_options = {
+ enable = true,
+ unstable = true,
+ lint = true,
+ },
+ cmd = { "${pkgs.unstable.deno}/bin/deno", "lsp", "--unstable" },
+ root_dir = function(startpath)
+ if util.find_package_json_ancestor(startpath) then
+ -- This is a Node project; let tsserver handle this one.
+ return nil
+ else
+ -- Otherwise, we try to find the root or
+ -- default to the current directory.
+ return util.root_pattern("deno.json", "deno.jsonc", ".git")(startpath)
+ or util.path.dirname(startpath)
+ end
+ end,
+ },
+ clangd = {
+ cmd = { "${pkgs.clang-tools}/bin/clangd" },
+ },
+ nimls = {
+ cmd = { "${pkgs.nimlsp}/bin/nimlsp" },
+ },
+ };
+ for server, config in pairs(servers) do
+ -- set common options
+ config.on_attach = on_attach;
+ config.debounce_text_changes = 150;
+
+ lspconfig[server].setup(config)
+ end
+ '';
+ }
+ ];
+}
+# I spent like an hour writing this, only to find it was a pretty bad idea.
+#
+# nixToLua = s:
+# if builtins.isAttrs s then
+# let
+# renderAttr = name: value: "[ [==========[" + name + "]==========] ] = " + (nixToLua value);
+# attrsList = map (name: renderAttr name s.${name}) (lib.attrNames s);
+# attrsListStr = lib.concatStringsSep ", " attrsList;
+# in
+# "{ ${attrsListStr} }"
+# else if builtins.isList s then
+# "{ " + (lib.concatStringsSep ", " (map nixToLua s)) + " }"
+# else if builtins.isString s then
+# # Oh boy I sure hope `s` doesn't contain "]==========]".
+# "[==========[" + s + "]==========]"
+# else if builtins.isInt s || builtins.isFloat s then
+# toString s
+# else
+# throw "Cannot convert ${builtins.typeOf s} to Lua value!";
+
diff --git a/hosts/muhammed/home/noweb/default.nix b/hosts/muhammed/home/noweb/default.nix
new file mode 100644
index 0000000..ef67862
--- /dev/null
+++ b/hosts/muhammed/home/noweb/default.nix
@@ -0,0 +1,12 @@
+{pkgs, ...}: {
+ home.packages = with pkgs; [
+ noweb
+ texliveFull
+ yalafi-shell
+ ];
+
+ # Prepend nowebs STY files to the search path. I chose to do it globally,
+ # rather than using `makeWrapper` because I sometimes want to manually invoke
+ # `pdflatex` and the like on the output of `noweave`.
+ home.sessionVariables.TEXINPUTS = "${pkgs.noweb.tex}/tex/latex/noweb/:$TEXINPUTS";
+}