Compare commits

...

7 commits

15 changed files with 648 additions and 745 deletions

View file

@ -1,18 +1,29 @@
{
config,
pkgs,
lib,
modulesPath,
...
}: {
imports = [
"${modulesPath}/profiles/headless.nix"
(import ../modules)
./services/gitea.nix
./services/nextcloud.nix
./services/webserver.nix
./services/starbound.nix
./ids.nix
./services/postgres.nix
./sops.nix
];
nixpkgs.overlays = [
(final: prev: {
local = import ../pkgs {
pkgs = prev;
};
})
];
nix = {
package = pkgs.nixFlakes;
extraOptions = ''
@ -24,7 +35,7 @@
};
nixpkgs.config.allowUnfreePredicate = pkg:
builtins.elem (lib.getName pkg) ["steam-runtime" "steamcmd"];
builtins.elem (lib.getName pkg) ["steam-original" "steam-runtime" "steamcmd"];
# Optimization for minecraft servers, see:
# https://bugs.mojang.com/browse/MC-183518
@ -37,7 +48,7 @@
useDHCP = false;
interfaces.eth0.useDHCP = true;
firewall.allowedTCPPorts = [80 443 2222 2221 21025];
firewall.allowedTCPPorts = [80 443 2222 21025];
};
time.timeZone = "Europe/London";
@ -66,32 +77,12 @@
recommendedProxySettings = true;
clientMaxBodySize = "10G";
domain = "tlater.net";
virtualHosts = let
proxyPassToPort = port: extra:
lib.recursiveUpdate {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://127.0.0.1:${toString port}";
extraConfig = ''
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
'';
}
extra;
domain = config.services.nginx.domain;
in {
"${domain}" = proxyPassToPort 3002 {serverAliases = ["www.${domain}"];};
"gitea.${domain}" = proxyPassToPort 3000 {};
"nextcloud.${domain}" = proxyPassToPort 3001 {};
};
};
security.acme = {
email = "tm@tlater.net";
defaults.email = "tm@tlater.net";
acceptTerms = true;
};
virtualisation.oci-containers.backend = "podman";
system.stateVersion = "20.09";
}

View file

@ -1,4 +1,8 @@
{
imports = [
./hardware-configuration.nix
];
# Required for the lish console
boot.kernelParams = ["console=ttyS0,19200n8"];

View file

@ -0,0 +1,17 @@
{lib, ...}: {
users.users.tlater.password = "insecure";
# Disable graphical tty so -curses works
boot.kernelParams = ["nomodeset"];
# Sets the base domain for nginx to localhost so that we
# can easily test locally with the VM.
services.nginx.domain = lib.mkOverride 99 "localhost";
# Use the staging secrets
sops.defaultSopsFile = lib.mkOverride 99 ../../keys/staging.yaml;
# # Set up VM settings to match real VPS
# virtualisation.memorySize = 3941;
# virtualisation.cores = 2;
}

View file

@ -1,11 +0,0 @@
{...}: {
ids.uids = {
# System user ids start at 400 (see nixos/modules/programs/shadow.nix)
webserver = 400;
# The limit is 999
};
ids.gids = {
webserver = 400;
};
}

View file

@ -1,167 +0,0 @@
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
upstream php-handler {
server localhost:9000;
}
server {
listen 80;
# HSTS settings
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
#add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
# HTTP response headers borrowed from Nextcloud `.htaccess`
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always;
add_header X-XSS-Protection "1; mode=block" always;
# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;
# Path to the root of your installation
root /var/www/html;
# Specify how to handle directories -- specifying `/index.php$request_uri`
# here as the fallback means that Nginx always exhibits the desired behaviour
# when a client requests a path that corresponds to a directory that exists
# on the server. In particular, if that directory contains an index.php file,
# that file is correctly served; if it doesn't, then the request is passed to
# the front-end controller. This consistent behaviour means that we don't need
# to specify custom rules for certain paths (e.g. images and other assets,
# `/updater`, `/ocm-provider`, `/ocs-provider`), and thus
# `try_files $uri $uri/ /index.php$request_uri`
# always provides the desired behaviour.
index index.php index.html /index.php$request_uri;
# Rule borrowed from `.htaccess` to handle Microsoft DAV clients
location = / {
if ( $http_user_agent ~ ^DavClnt ) {
return 302 /remote.php/webdav/$is_args$args;
}
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Make a regex exception for `/.well-known` so that clients can still
# access it despite the existence of the regex rule
# `location ~ /(\.|autotest|...)` which would otherwise handle requests
# for `/.well-known`.
location ^~ /.well-known {
# The rules in this block are an adaptation of the rules
# in `.htaccess` that concern `/.well-known`.
location = /.well-known/carddav { return 301 /remote.php/dav/; }
location = /.well-known/caldav { return 301 /remote.php/dav/; }
location /.well-known/acme-challenge { try_files $uri $uri/ =404; }
location /.well-known/pki-validation { try_files $uri $uri/ =404; }
# Let Nextcloud's API for `/.well-known` URIs handle all other
# requests by passing them to the front-end controller.
return 301 /index.php$request_uri;
}
# set max upload size
client_max_body_size 10G;
fastcgi_buffers 64 4K;
# Enable gzip but do not remove ETag headers
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
# Pagespeed is not supported by Nextcloud, so if your server is built
# with the `ngx_pagespeed` module, uncomment this line to disable it.
#pagespeed off;
# Rules borrowed from `.htaccess` to hide certain paths from clients
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; }
# Ensure this block, which passes PHP files to the PHP process, is above the blocks
# which handle static assets (as seen below). If this block is not declared first,
# then Nginx will encounter an infinite rewriting loop when it prepends `/index.php`
# to the URI, resulting in a HTTP 500 error response.
location ~ \.php(?:$|/) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
#fastcgi_param HTTPS on;
fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice
fastcgi_param front_controller_active true; # Enable pretty urls
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
}
location ~ \.(?:css|js|svg|gif)$ {
try_files $uri /index.php$request_uri;
expires 6M; # Cache-Control policy borrowed from `.htaccess`
access_log off; # Optional: Don't log access to assets
}
location ~ \.woff2?$ {
try_files $uri /index.php$request_uri;
expires 7d; # Cache-Control policy borrowed from `.htaccess`
access_log off; # Optional: Don't log access to assets
}
# Rule borrowed from `.htaccess`
location /remote {
return 301 /remote.php$request_uri;
}
location / {
try_files $uri $uri/ /index.php$request_uri;
}
}
}

View file

@ -1,48 +1,31 @@
{config, ...}: {
users = {
extraUsers.gitea = {
uid = config.ids.uids.git;
isSystemUser = true;
description = "Gitea Service";
group = config.users.extraGroups.gitea.name;
};
extraGroups.gitea = {gid = config.ids.gids.git;};
{config, ...}: let
domain = "gitea.${config.services.nginx.domain}";
in {
services.gitea = {
inherit domain;
enable = true;
httpAddress = "127.0.0.1";
database.type = "postgres";
ssh.clonePort = 2222;
rootUrl = "https://${domain}/";
cookieSecure = true;
appName = "Gitea: Git with a cup of tea";
disableRegistration = true;
};
virtualisation.pods.gitea = {
hostname = "gitea.tlater.net";
publish = ["3000:3000" "2221:2221"];
network = "slirp4netns";
# Set up SSL
services.nginx.virtualHosts."${domain}" = let
inherit (config.services.gitea) httpAddress httpPort;
in {
forceSSL = true;
enableACME = true;
extraConfig = ''
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
'';
containers = {
gitea = {
image = "gitea/gitea:latest";
volumes = ["gitea:/data:Z" "/etc/localtime:/etc/localtime:ro"];
dependsOn = ["postgres"];
environment = {
DB_TYPE = "postgres";
DB_HOST = "localhost:5432";
DB_NAME = "gitea";
DB_USER = "gitea";
USER_UID = toString config.users.extraUsers.gitea.uid;
USER_GID = toString config.users.extraGroups.gitea.gid;
RUN_MODE = "prod";
DOMAIN = "gitea.tlater.net";
SSH_PORT = "2221";
};
};
postgres = {
image = "postgres:alpine";
environment = {
POSTGRES_DB = "gitea";
POSTGRES_USER = "gitea";
};
volumes = ["gitea-postgres-14:/var/lib/postgresql/data"];
};
};
locations."/".proxyPass = "http://${httpAddress}:${toString httpPort}";
};
}

View file

@ -1,53 +1,99 @@
{config, ...}: {
virtualisation.pods.nextcloud = {
hostname = "nextcloud.tlater.net";
publish = ["3001:80"];
network = "slirp4netns";
{
pkgs,
config,
...
}: let
inherit (pkgs) fetchNextcloudApp;
nextcloud = pkgs.nextcloud24;
hostName = "nextcloud.${config.services.nginx.domain}";
in {
services.nextcloud = {
inherit hostName;
containers = {
nextcloud = {
image = "nextcloud:fpm-alpine";
dependsOn = ["postgres"];
volumes = [
"nextcloud-root:/var/www/html"
"nextcloud-apps:/var/www/html/custom_apps"
"nextcloud-config:/var/www/html/config"
"nextcloud-data:/var/www/html/data"
];
environment = {
POSTGRES_DB = "nextcloud";
POSTGRES_USER = "nextcloud";
POSTGRES_HOST = "localhost";
OVERWRITEPROTOCOL = "https";
TRUSTED_PROXIES = "127.0.0.1";
};
package = nextcloud;
enable = true;
maxUploadSize = "2G";
https = true;
config = {
overwriteProtocol = "https";
dbtype = "pgsql";
dbhost = "/run/postgresql";
adminuser = "tlater";
adminpassFile = config.sops.secrets."nextcloud/tlater".path;
defaultPhoneRegion = "AT";
};
extraApps = {
# TODO(tlater): Seems like this won't work anymore from
# Nextcloud 25 onwards.
#
# Adopt whatever upstream does with this:
# https://github.com/nextcloud/server/issues/4917
apporder = pkgs.fetchNextcloudApp {
name = "apporder";
url = "https://github.com/juliushaertl/apporder/releases/download/v0.15.0/apporder.tar.gz";
version = "0.15.0";
sha256 = "sha256-p3VWxTYDCO2NePq6oLM8tBVqYkvoB7itqxp7IZwGDnE=";
};
cron = {
image = "nextcloud:fpm-alpine";
entrypoint = "/cron.sh";
dependsOn = ["postgres" "nextcloud"];
extraOptions = ["--volumes-from=nextcloud-nextcloud"];
bookmarks = pkgs.fetchNextcloudApp {
name = "bookmarks";
url = "https://github.com/nextcloud/bookmarks/releases/download/v11.0.4/bookmarks-11.0.4.tar.gz";
version = "11.0.4";
sha256 = "sha256-URqtzaCx8FEZHCDP1wSBUFNs+x50jesRtWi+xOU1oXM=";
};
nginx = {
image = "nginx:alpine";
dependsOn = ["nextcloud"];
volumes = [
"nextcloud-root:/var/www/html:ro"
"${./configs/nginx-nextcloud.conf}:/etc/nginx/nginx.conf:ro"
];
extraOptions = ["--volumes-from=nextcloud-nextcloud"];
calendar = pkgs.fetchNextcloudApp {
name = "calendar";
url = "https://github.com/nextcloud-releases/calendar/releases/download/v3.5.0/calendar-v3.5.0.tar.gz";
version = "3.5.0";
sha256 = "sha256-+LRGl9h40AQdWN9SW+NqGwTafAGwV07Af8nVs3pUCm0=";
};
postgres = {
image = "postgres:alpine";
environment = {
POSTGRES_DB = "nextcloud";
POSTGRES_USER = "nextcloud";
};
volumes = ["nextcloud-postgres-14:/var/lib/postgresql/data"];
contacts = pkgs.fetchNextcloudApp {
name = "contacts";
url = "https://github.com/nextcloud-releases/contacts/releases/download/v4.2.2/contacts-v4.2.2.tar.gz";
version = "4.2.2";
sha256 = "sha256-GTiyZsUHBXPgQ17DHAihmt2W/ZnAjDwfgwnujkRwk6A=";
};
cookbook = pkgs.fetchNextcloudApp {
name = "cookbook";
url = "https://github.com/nextcloud/cookbook/releases/download/v0.9.15/Cookbook-0.9.15.tar.gz";
version = "0.9.15";
sha256 = "sha256-v64rLGyMQOdStyivpJsKrNxwumVQvyK3CnHtZ+K+elE=";
};
news = pkgs.fetchNextcloudApp {
name = "news";
url = "https://github.com/nextcloud/news/releases/download/18.2.0/news.tar.gz";
version = "18.2.0";
sha256 = "sha256-eS0cFwJmYfGGJmA02AOWO/OXfqfyI71u2GataDj18DE=";
};
notes = pkgs.fetchNextcloudApp {
name = "notes";
url = "https://github.com/nextcloud/notes/releases/download/v4.5.1/notes.tar.gz";
version = "4.5.1";
sha256 = "sha256-rd3uVkVtARX4enRAWm1ivV468lboYZnYe7/zsqaHYpk=";
};
};
# TODO(tlater): Add redis config. This will be much easier
# starting with 22.11, since this will add an `extraOptions` where
# the necessary redis config can go.
};
# Ensure that this service doesn't start before postgres is ready
systemd.services.nextcloud-setup.after = ["postgresql.service"];
# Set up SSL
services.nginx.virtualHosts."${hostName}" = {
forceSSL = true;
enableACME = true;
};
}

View file

@ -0,0 +1,31 @@
{pkgs, ...}: {
services.postgresql = {
package = pkgs.postgresql_14;
enable = true;
# Only enable connections via the unix socket, and check with the
# OS to make sure the user matches the database name.
#
# See https://www.postgresql.org/docs/current/auth-pg-hba-conf.html
authentication = ''
local sameuser all peer
'';
# Note: The following options with ensure.* are set-only; i.e.,
# when permissions/users/databases are removed from these lists,
# that operation needs to be performed manually on the system as
# well.
ensureUsers = [
{
name = "nextcloud";
ensurePermissions = {
"DATABASE nextcloud" = "ALL PRIVILEGES";
};
}
];
ensureDatabases = [
"nextcloud"
];
};
}

View file

@ -1,47 +1,26 @@
{
config,
pkgs,
...
}: {
users = {
extraUsers.webserver = {
uid = config.ids.uids.webserver;
group = config.users.extraGroups.webserver.name;
isSystemUser = true;
description = "tlater.net web server user";
{config, ...}: let
domain = config.services.nginx.domain;
in {
services.tlaternet-webserver = {
enable = true;
listen = {
addr = "127.0.0.1";
port = 8000;
};
extraGroups.webserver = {gid = config.ids.gids.webserver;};
};
virtualisation.oci-containers.containers.webserver = {
image = "tlaternet/webserver";
# Set up SSL
services.nginx.virtualHosts."${domain}" = let
inherit (config.services.tlaternet-webserver.listen) addr port;
in {
serverAliases = ["www.${domain}"];
imageFile = pkgs.dockerTools.buildImage {
name = "tlaternet/webserver";
tag = "latest";
contents = pkgs.tlaternet-webserver.webserver;
forceSSL = true;
enableACME = true;
extraConfig = ''
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
'';
config = let
uid = toString config.users.extraUsers.webserver.uid;
gid = toString config.users.extraGroups.webserver.gid;
in {
Cmd = ["tlaternet-webserver"];
Volumes = {"/srv/mail" = {};};
Env = [
"ROCKET_PORT=3002"
"ROCKET_TEMPLATE_DIR=${pkgs.tlaternet-templates.templates}/browser/"
];
ExposedPorts = {"3002" = {};};
User = "${uid}:${gid}";
};
};
ports = ["3002:3002"];
volumes = ["tlaternet-mail:/srv/mail"];
extraOptions = [
"--hostname=tlater.net"
# Rocket 0.4 doesn't support SIGTERM anyway, so SIGKILL is the cleanest exit possible.
"--stop-signal=SIGKILL"
];
locations."/".proxyPass = "http://${addr}:${toString port}";
};
}

View file

@ -1,119 +1,377 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"naersk": {
"alejandra": {
"inputs": {
"fenix": "fenix",
"flakeCompat": "flakeCompat",
"nixpkgs": [
"tlaternet-webserver",
"dream2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1632266297,
"narHash": "sha256-J1yeJk6Gud9ef2pEf6aKQemrfg1pVngYDSh+SAY94xk=",
"owner": "nmattia",
"repo": "naersk",
"rev": "ee7edec50b49ab6d69b06d62f1de554efccb1ccd",
"lastModified": 1658427149,
"narHash": "sha256-ToD/1z/q5VHsLMrS2h96vjJoLho59eNRtknOUd19ey8=",
"owner": "kamadorueda",
"repo": "alejandra",
"rev": "f5a22afd2adfb249b4e68e0b33aa1f0fb73fb1be",
"type": "github"
},
"original": {
"owner": "nmattia",
"repo": "naersk",
"owner": "kamadorueda",
"repo": "alejandra",
"type": "github"
}
},
"nixos-hardware": {
"crane": {
"flake": false,
"locked": {
"lastModified": 1650522846,
"narHash": "sha256-SxWHXRI3qJwswyXAtzsi6PKVY3KLNNnb072KaJthII8=",
"owner": "nixos",
"repo": "nixos-hardware",
"rev": "6b4ebea9093c997c5f275c820e679108de4871ab",
"lastModified": 1661875961,
"narHash": "sha256-f1h/2c6Teeu1ofAHWzrS8TwBPcnN+EEu+z1sRVmMQTk=",
"owner": "ipetkov",
"repo": "crane",
"rev": "d9f394e4e20e97c2a60c3ad82c2b6ef99be19e24",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "master",
"repo": "nixos-hardware",
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"deploy-rs": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": "nixpkgs",
"utils": "utils"
},
"locked": {
"lastModified": 1659725433,
"narHash": "sha256-1ZxuK67TL29YLw88vQ18Y2Y6iYg8Jb7I6/HVzmNB6nM=",
"owner": "serokell",
"repo": "deploy-rs",
"rev": "41f15759dd8b638e7b4f299730d94d5aa46ab7eb",
"type": "github"
},
"original": {
"owner": "serokell",
"repo": "deploy-rs",
"type": "github"
}
},
"devshell": {
"flake": false,
"locked": {
"lastModified": 1653917170,
"narHash": "sha256-FyxOnEE/V4PNEcMU62ikY4FfYPo349MOhMM97HS0XEo=",
"owner": "numtide",
"repo": "devshell",
"rev": "fc7a3e3adde9bbcab68af6d1e3c6eb738e296a92",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"dream2nix": {
"inputs": {
"alejandra": "alejandra",
"crane": "crane",
"devshell": "devshell",
"flake-utils-pre-commit": "flake-utils-pre-commit",
"gomod2nix": "gomod2nix",
"mach-nix": "mach-nix",
"nixpkgs": "nixpkgs_3",
"poetry2nix": "poetry2nix",
"pre-commit-hooks": "pre-commit-hooks"
},
"locked": {
"lastModified": 1663323895,
"narHash": "sha256-ZmI9C8HNVz2w3OnB79WR/LIgVEY8tDnR8tEPi3hMiJk=",
"owner": "nix-community",
"repo": "dream2nix",
"rev": "25be741ec92c77b8308ca6a7ab89593fe37b6542",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "dream2nix",
"type": "github"
}
},
"fenix": {
"inputs": {
"nixpkgs": [
"tlaternet-webserver",
"dream2nix",
"alejandra",
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1657607339,
"narHash": "sha256-HaqoAwlbVVZH2n4P3jN2FFPMpVuhxDy1poNOR7kzODc=",
"owner": "nix-community",
"repo": "fenix",
"rev": "b814c83d9e6aa5a28d0cf356ecfdafb2505ad37d",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"fenix_2": {
"inputs": {
"nixpkgs": [
"tlaternet-webserver",
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src_2"
},
"locked": {
"lastModified": 1663396212,
"narHash": "sha256-dlK10QPTDYNpJ/vl2QPKOTrqEbQwAR/v2f4+xsetTkw=",
"owner": "nix-community",
"repo": "fenix",
"rev": "263cd7f991c07a9592a6e825bfc37b23b00eb244",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1648199409,
"narHash": "sha256-JwPKdC2PoVBkG6E+eWw3j6BMR6sL3COpYWfif7RVb8Y=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "64a525ee38886ab9028e6f61790de0832aa3ef03",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils-pre-commit": {
"locked": {
"lastModified": 1644229661,
"narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flakeCompat": {
"flake": false,
"locked": {
"lastModified": 1650374568,
"narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "b4a34015c698c7793d592d66adbab377907a2be8",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"gomod2nix": {
"flake": false,
"locked": {
"lastModified": 1627572165,
"narHash": "sha256-MFpwnkvQpauj799b4QTBJQFEddbD02+Ln5k92QyHOSk=",
"owner": "tweag",
"repo": "gomod2nix",
"rev": "67f22dd738d092c6ba88e420350ada0ed4992ae8",
"type": "github"
},
"original": {
"owner": "tweag",
"repo": "gomod2nix",
"type": "github"
}
},
"mach-nix": {
"flake": false,
"locked": {
"lastModified": 1634711045,
"narHash": "sha256-m5A2Ty88NChLyFhXucECj6+AuiMZPHXNbw+9Kcs7F6Y=",
"owner": "DavHau",
"repo": "mach-nix",
"rev": "4433f74a97b94b596fa6cd9b9c0402104aceef5d",
"type": "github"
},
"original": {
"id": "mach-nix",
"type": "indirect"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1650501692,
"narHash": "sha256-ApKf0/dc0SyB7zZ6yiiOQgcXAhCXxbSDyihHfRDIzx0=",
"lastModified": 1648219316,
"narHash": "sha256-Ctij+dOi0ZZIfX5eMhgwugfvB+WZSrvVNAyAuANOsnQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "30d3d79b7d3607d56546dd2a6b49e156ba0ec634",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-22_05": {
"locked": {
"lastModified": 1665279158,
"narHash": "sha256-TpbWNzoJ5RaZ302dzvjY2o//WxtOJuYT3CnDj5N69Hs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b3783bcfb8ec54e0de26feccfc6cc36b8e202ed5",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-22.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1665466769,
"narHash": "sha256-L+qcHpb4Ac3PipMXJY/Ktbu1+KXy23WCZ8pXWmsf7zY=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "9887f024766aa27704d1f89f623efd1d063da92a",
"rev": "0b20bf89e0035b6d62ad58f9db8fdbc99c2b01e8",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-21.11",
"ref": "nixos-22.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1657638268,
"narHash": "sha256-blBNtQSslAFkg0Gym9fWNJk+bPxGSZib4SOcPrmTPi4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d80993b5f885515254746ba6d1917276ee386149",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"poetry2nix": {
"flake": false,
"locked": {
"lastModified": 1632969109,
"narHash": "sha256-jPDclkkiAy5m2gGLBlKgH+lQtbF7tL4XxBrbSzw+Ioc=",
"owner": "nix-community",
"repo": "poetry2nix",
"rev": "aee8f04296c39d88155e05d25cfc59dfdd41cc77",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "1.21.0",
"repo": "poetry2nix",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-utils": [
"tlaternet-webserver",
"dream2nix",
"flake-utils-pre-commit"
],
"nixpkgs": [
"tlaternet-webserver",
"dream2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1646153636,
"narHash": "sha256-AlWHMzK+xJ1mG267FdT8dCq/HvLCA6jwmx2ZUy5O8tY=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "b6bc0b21e1617e2b07d8205e7fae7224036dfa4b",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs",
"deploy-rs": "deploy-rs",
"nixpkgs": "nixpkgs_2",
"sops-nix": "sops-nix",
"tlaternet-templates": "tlaternet-templates",
"tlaternet-webserver": "tlaternet-webserver"
}
},
"rust-overlay": {
"inputs": {
"flake-utils": [
"tlaternet-webserver",
"flake-utils"
],
"nixpkgs": [
"tlaternet-webserver",
"nixpkgs"
]
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1633400100,
"narHash": "sha256-kHQV7jZ2vVHVI9sfda1mUROVBbQbdfKcbIpKG9WdqGo=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "9c2fc6a62ccbc6f420d71ecac6bf0b84dbbee64f",
"lastModified": 1657557289,
"narHash": "sha256-PRW+nUwuqNTRAEa83SfX+7g+g8nQ+2MMbasQ9nt6+UM=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "caf23f29144b371035b864a1017dbc32573ad56d",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"rust-analyzer-src_2": {
"flake": false,
"locked": {
"lastModified": 1662896065,
"narHash": "sha256-1LkSsXzI1JTAmP/GMTz4fTJd8y/tw8R79l96q+h7mu8=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "2e9f1204ca01c3e20898d4a67c8b84899d394a88",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
@ -121,14 +379,15 @@
"inputs": {
"nixpkgs": [
"nixpkgs"
]
],
"nixpkgs-22_05": "nixpkgs-22_05"
},
"locked": {
"lastModified": 1649756291,
"narHash": "sha256-KTll8bCINAzIUGaaMrbn9wb5nfhkXRLgmFrWGR/Dku0=",
"lastModified": 1665289655,
"narHash": "sha256-j1Q9mNBhbzeJykhObiXwEGres9qvP4vH7gxdJ+ihkLI=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "c2614c4fe61943b3d280ac1892fcebe6e8eaf8c8",
"rev": "0ce0449e6404c4ff9d1b7bd657794ae5ca54deb3",
"type": "github"
},
"original": {
@ -137,42 +396,20 @@
"type": "github"
}
},
"tlaternet-templates": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1633432574,
"narHash": "sha256-IjGaJAQuFIJ1Is9gtHXsryPOnTDE6tlA61PUKuS8dzw=",
"ref": "master",
"rev": "555a2949bdf643c74b535bd0c623d98f99d33628",
"revCount": 61,
"type": "git",
"url": "https://gitea.tlater.net/tlaternet/tlaternet-templates.git"
},
"original": {
"type": "git",
"url": "https://gitea.tlater.net/tlaternet/tlaternet-templates.git"
}
},
"tlaternet-webserver": {
"inputs": {
"flake-utils": "flake-utils_2",
"naersk": "naersk",
"dream2nix": "dream2nix",
"fenix": "fenix_2",
"nixpkgs": [
"nixpkgs"
],
"rust-overlay": "rust-overlay"
]
},
"locked": {
"lastModified": 1633433130,
"narHash": "sha256-jkW+HV8cJvE86gppOEXQl2ke+bDHJ7SAp8eJp8pJ0N8=",
"lastModified": 1665402451,
"narHash": "sha256-zWaEzUJh0WFVoaLQsMoIj2D+QFhUBJw+f+XzoYqw3es=",
"ref": "master",
"rev": "1232950c06ae16bf17fb16ac1f5f2231e971936b",
"revCount": 16,
"rev": "6c29bc5db001e75f5ca06fec726369b87d007a03",
"revCount": 47,
"type": "git",
"url": "https://gitea.tlater.net/tlaternet/tlaternet.git"
},
@ -180,6 +417,21 @@
"type": "git",
"url": "https://gitea.tlater.net/tlaternet/tlaternet.git"
}
},
"utils": {
"locked": {
"lastModified": 1648297722,
"narHash": "sha256-W+qlPsiZd8F3XkzXOzAoR+mpFqzm3ekQkJNa+PIh1BQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "0f8662f1319ad6abf89b3380dd2722369fc51ade",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",

168
flake.nix
View file

@ -2,141 +2,131 @@
description = "tlater.net host configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-21.11";
nixos-hardware.url = "github:nixos/nixos-hardware/master";
nixpkgs.url = "github:nixos/nixpkgs/nixos-22.05";
deploy-rs.url = "github:serokell/deploy-rs";
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
tlaternet-webserver = {
url = "git+https://gitea.tlater.net/tlaternet/tlaternet.git";
inputs.nixpkgs.follows = "nixpkgs";
};
tlaternet-templates = {
url = "git+https://gitea.tlater.net/tlaternet/tlaternet-templates.git";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = {
self,
nixpkgs,
nixos-hardware,
sops-nix,
deploy-rs,
tlaternet-webserver,
tlaternet-templates,
}: let
system = "x86_64-linux";
overlays = [
(final: prev: {
tlaternet-webserver =
tlaternet-webserver.legacyPackages.${prev.system}.packages;
tlaternet-templates =
tlaternet-templates.legacyPackages.${prev.system}.packages;
local = import ./pkgs {
pkgs = prev;
};
})
];
pkgs = import nixpkgs {inherit system overlays;};
sops-pkgs = sops-nix.packages.${system};
in {
nixosConfigurations = {
##################
# Configurations #
##################
nixosConfigurations = let
# Modules that should be generic to all systems
genericModule = {...}: {
imports = [
# Inject flake dependencies
sops-nix.nixosModules.sops
tlaternet-webserver.nixosModules.default
# Import actual configuration
(import ./configuration)
];
};
in {
# The actual system definition
tlaternet = nixpkgs.lib.nixosSystem {
inherit system;
modules = [
({modulesPath, ...}: {
imports = [(modulesPath + "/profiles/headless.nix")];
nixpkgs.overlays = overlays;
})
(import ./modules)
(import ./configuration)
(import ./configuration/linode.nix)
(import ./configuration/hardware-configuration.nix)
sops-nix.nixosModules.sops
genericModule
(import ./configuration/hardware-specific/linode)
];
};
# A qemu VM to test the above with
vm = nixpkgs.lib.nixosSystem {
inherit system;
modules = [
({modulesPath, ...}: {
imports = [(modulesPath + "/profiles/headless.nix")];
nixpkgs.overlays = overlays;
})
(import ./modules)
(import ./configuration)
sops-nix.nixosModules.sops
({lib, ...}: {
users.users.tlater.password = "insecure";
# Disable graphical tty so -curses works
boot.kernelParams = ["nomodeset"];
# Sets the base domain for nginx to localhost so that we
# can easily test locally with the VM.
services.nginx.domain = lib.mkOverride 99 "localhost";
# Use the staging secrets
sops.defaultSopsFile = lib.mkOverride 99 ./keys/staging.yaml;
# # Set up VM settings to match real VPS
# virtualisation.memorySize = 3941;
# virtualisation.cores = 2;
})
genericModule
(import ./configuration/hardware-specific/vm.nix)
];
};
};
############################
# Deployment configuration #
############################
deploy.nodes.tlaternet = {
hostname = "tlater.net";
profiles.system = {
user = "root";
path = deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.tlaternet;
};
sshUser = "tlater";
sshOpts = ["-t" "-p" "2222"];
fastConnection = true;
# Currently broken, see https://github.com/serokell/deploy-rs/issues/78
magicRollback = false;
};
#########
# Tests #
#########
checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib;
####################
# Helper functions #
####################
lib = import ./lib {lib = nixpkgs.lib;};
####################
# VM launch script #
####################
apps.${system}.default = let
inherit (self.nixosConfigurations.vm.config.system.build) vm;
inherit (nixpkgs.legacyPackages.${system}) writeShellScript;
inherit (nixpkgs.lib.attrsets) mapAttrsToList;
inherit (nixpkgs.lib.strings) concatStringsSep;
ports = {
qemuNetOpts = self.lib.makeQemuNetOpts {
"2222" = "2222";
"3080" = "80";
"3443" = "443";
"2221" = "2221";
"21025" = "21025"; # Starbound
};
QEMU_NET_OPTS =
concatStringsSep ","
(mapAttrsToList
(host: vm: "hostfwd=::${host}-:${vm}")
ports);
in {
type = "app";
program = builtins.toString (writeShellScript "run-vm" ''
export QEMU_OPTS="-m 3941 -smp 2 -curses"
export QEMU_NET_OPTS="${QEMU_NET_OPTS}"
export QEMU_OPTS="-m 3941 -smp 2 -display curses"
export QEMU_NET_OPTS="${qemuNetOpts}"
"${vm}/bin/run-tlaternet-vm"
'');
};
devShells.${system}.default = pkgs.mkShell {
sopsPGPKeyDirs = ["./keys/hosts/" "./keys/users/"];
nativeBuildInputs = [
sops-pkgs.sops-import-keys-hook
];
buildInputs = with pkgs; [
nixfmt
git-lfs
sops-pkgs.sops-init-gpg-key
];
###########################
# Development environment #
###########################
devShells.${system}.default = let
inherit (sops-nix.packages.${system}) sops-import-keys-hook sops-init-gpg-key;
deploy-rs-bin = deploy-rs.packages.${system}.default;
pkgs = nixpkgs.legacyPackages.${system};
in
nixpkgs.legacyPackages.${system}.mkShell {
sopsPGPKeyDirs = ["./keys/hosts/" "./keys/users/"];
nativeBuildInputs = [
sops-import-keys-hook
];
shellHook = ''
# Work around sudo requiring a full terminal when deploying to
# a remote host
export NIX_SSHOPTS="-t"
'';
};
packages = with pkgs; [
nixfmt
git-lfs
sops-init-gpg-key
deploy-rs-bin
];
};
};
}

10
lib/default.nix Normal file
View file

@ -0,0 +1,10 @@
{lib}: let
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.strings) concatStringsSep;
in {
makeQemuNetOpts = portMapping:
concatStringsSep ","
(mapAttrsToList
(host: vm: "hostfwd=::${host}-:${vm}")
portMapping);
}

View file

@ -1,222 +0,0 @@
{
lib,
config,
options,
...
}:
with lib; let
cfg = config.virtualisation.pods;
list-to-args = arg: list:
concatStringsSep " " (map (e: "--${arg}=${escapeShellArg e}") list);
possibly-unset-arg = arg: val: (optionalString (val != null) "--${arg}=${escapeShellArg val}");
mkPod = name: pod: rec {
path = [config.virtualisation.podman.package];
wants = ["network.target"];
after = ["network-online.target"];
wantedBy = ["multi-user.target" "default.target"];
environment.PODMAN_SYSTEMD_UNIT = "%n";
preStart = concatStringsSep " " [
"mkdir -p /run/podman/pods/ ;"
"podman pod create"
"--infra-conmon-pidfile=${escapeShellArg "/run/podman/pods/${name}.pid"}"
"--name=${escapeShellArg name}"
"--replace"
(list-to-args "add-host" pod.added-hosts)
(possibly-unset-arg "cgroup-parent" pod.cgroup-parent)
(list-to-args "dns" pod.dns)
(list-to-args "dns-opt" pod.dns-opt)
(list-to-args "dns-search" pod.dns-search)
(possibly-unset-arg "hostname" pod.hostname)
(possibly-unset-arg "infra" pod.infra)
(possibly-unset-arg "infra-command" pod.infra-command)
(possibly-unset-arg "infra-image" pod.infra-image)
(possibly-unset-arg "ip" pod.ip)
(possibly-unset-arg "mac-address" pod.mac-address)
(possibly-unset-arg "network" pod.network)
(possibly-unset-arg "network-alias" pod.network-alias)
(possibly-unset-arg "no-hosts" pod.no-hosts)
(list-to-args "publish" pod.publish)
(list-to-args "share" pod.share)
];
script = "podman pod start ${escapeShellArg name}";
preStop = "podman pod stop ${escapeShellArg name}";
# `podman generate systemd` generates a second stop after the
# first; not sure why but clearly it's recommended.
postStop = preStop;
serviceConfig = rec {
Type = "forking";
TimeoutStopSec = 70;
Restart = "on-failure";
PIDFile = "/run/podman/pods/${name}.pid";
};
};
in {
options.virtualisation.pods = mkOption {
type = with types;
attrsOf (submodule {
options = {
added-hosts = mkOption {
type = listOf str;
default = [];
description = "Additional hosts to add to /etc/hosts for each container.";
example = literalExample ''
[ "database:10.0.0.1" ]
'';
};
cgroup-parent = mkOption {
type = nullOr str;
default = null;
description = "The cgroups path under which the pod cgroup will be created.";
};
dns = mkOption {
type = listOf str;
default = [];
description = "The dns servers to set in /etc/resolv.conf.";
};
dns-opt = mkOption {
type = listOf str;
default = [];
description = "dns options to set in /etc/resolv.conf.";
};
dns-search = mkOption {
type = listOf str;
default = [];
description = "Search domains to set in /etc/resolv.conf.";
};
hostname = mkOption {
type = nullOr str;
default = null;
description = "The pod hostname.";
};
infra = mkOption {
type = nullOr bool;
default = null;
description = "Whether to create the infra container for the pod.";
};
infra-command = mkOption {
type = nullOr str;
default = null;
description = "The command to run in the infra container.";
};
infra-image = mkOption {
type = nullOr str;
default = null;
description = "The image to use for the infra container.";
};
ip = mkOption {
type = nullOr str;
default = null;
description = "A static IP address for the pod network.";
};
# TODO: set up label file stuff.
#
# labels = mkOption {};
mac-address = mkOption {
type = nullOr str;
default = null;
description = "A static mac address for the pod network.";
};
network = mkOption {
type = nullOr str;
default = null;
description = "Network configuration for the pod.";
};
network-alias = mkOption {
type = nullOr str;
default = null;
description = "DNS alias for the pod.";
};
no-hosts = mkOption {
type = nullOr bool;
default = null;
description = "Whether to disable /etc/hosts creation for the pod.";
};
publish = mkOption {
type = listOf str;
default = [];
description = "List of ports to publish from the pod.";
};
share = mkOption {
type = listOf str;
default = [];
description = "List of kernel namespaces to share.";
};
containers = options.virtualisation.oci-containers.containers;
};
});
default = {};
description = "Podman pods to run as systemd services.";
};
config = let
# Merge a list of attribute sets together
#
# TODO: See if there's a generic version for this somewhere in the
# pkgs lib?
mergeAttrs = attrList: foldr (a: b: a // b) {} attrList;
# Create services for all defined pods
pod-services = mapAttrs' (n: v: nameValuePair "pod-${n}" (mkPod n v)) cfg;
# Override the systemd-specific settings of containers defined in
# pods.
#
# I.e., make a systemd unit dependency on the pod service.
pod-container-services = mergeAttrs (mapAttrsToList (pname: pod:
mapAttrs' (cname: container:
nameValuePair "podman-${pname}-${cname}" rec {
after = ["pod-${pname}.service"];
requires = after;
})
pod.containers)
cfg);
# Override the oci-container settings for containers defined in pods.
#
# I.e., set the --pod=podname setting, and update the dependsOn so
# it points to containers in the same pod.
podifyContainer = container: podname:
container
// {
dependsOn =
map (dependency: "${podname}-${dependency}") container.dependsOn;
extraOptions = container.extraOptions ++ ["--pod=${podname}"];
};
in
lib.mkIf (cfg != {}) {
virtualisation.podman.enable = true;
virtualisation.oci-containers.backend = "podman";
systemd.services = pod-services // pod-container-services;
virtualisation.oci-containers.containers = mergeAttrs (mapAttrsToList
(pname: pod:
mapAttrs' (cname: container:
nameValuePair "${pname}-${cname}" (podifyContainer container pname))
pod.containers)
cfg);
};
}

View file

@ -1,5 +1,5 @@
{pkgs, ...}: let
inherit (pkgs.lib) callPackage;
inherit (pkgs) callPackage;
in {
starbound = callPackage ./starbound {};
}