Migrate Haven's services from K3s to Nix
This commit is contained in:
parent
af7bd3618a
commit
4d4bc1988d
17
README.md
17
README.md
|
@ -55,7 +55,14 @@ There are a few different actions for handling the update:
|
|||
|
||||
#### Using Remote builds
|
||||
|
||||
You can build any Nix or NixOS expression on a remote system before copying it over, as long as the root user on the local system has SSH access to the build target.
|
||||
Nix can create builds for or on remote systems, and transfer them via SSH.
|
||||
|
||||
##### Generating a build on a remote system
|
||||
|
||||
You can run a build on a remote server, then pull it down to the local system. This is called a `distributedBuild`.
|
||||
|
||||
> [!NOTE]
|
||||
> For distributed builds, the root user on the local system needs SSH access to the build target. This is done automatically.
|
||||
|
||||
To enable root builds on a host, add this to its config:
|
||||
|
||||
|
@ -65,6 +72,14 @@ nix.distributedBuilds = true;
|
|||
|
||||
For hosts where `nix.distributedBuilds` is true, this repo automatically gives the local root user SSH access to an unprivileged user on the build systems. This is configured in `nix-secrets`, but the build systems are defined in [`modules/base/nix.nix`](https://github.com/8bitbuddhist/nix-configuration/blob/b816d821636f9d30be905af80fe578c25ce74b92/modules/base/nix.nix#L41).
|
||||
|
||||
##### Pushing a build to a remote system
|
||||
|
||||
Conversely, you can run a build on the local host, then push it to a remote system.
|
||||
|
||||
```sh
|
||||
NIX_SSHOPTS="-o RequestTTY=force" nixos-rebuild --target-host user@example.com --use-remote-sudo switch
|
||||
```
|
||||
|
||||
### Testing without modifying the system
|
||||
|
||||
If you want to test without doing a whole build, or without modifying the current system, there are a couple additional tools to try.
|
||||
|
|
14
flake.lock
14
flake.lock
|
@ -250,11 +250,11 @@
|
|||
"nix-secrets": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1715904475,
|
||||
"narHash": "sha256-5PyOjPdOhzX5qHq3ywwSsYCQT5OmWv870DlSYyuJBh4=",
|
||||
"lastModified": 1716069971,
|
||||
"narHash": "sha256-0YWdnb+RiMHW8vQ4siDIqYEo5OUdWvYq7xzQw7blBwE=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "0bc545bf36759ca1ab67e2718bc5771eca72d02f",
|
||||
"revCount": 23,
|
||||
"rev": "b5c77dd4718a64ce7c8aef3752beed1144ea2693",
|
||||
"revCount": 27,
|
||||
"type": "git",
|
||||
"url": "file:///home/aires/Development/nix-configuration/nix-secrets"
|
||||
},
|
||||
|
@ -313,11 +313,11 @@
|
|||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1715787315,
|
||||
"narHash": "sha256-cYApT0NXJfqBkKcci7D9Kr4CBYZKOQKDYA23q8XNuWg=",
|
||||
"lastModified": 1715961556,
|
||||
"narHash": "sha256-+NpbZRCRisUHKQJZF3CT+xn14ZZQO+KjxIIanH3Pvn4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "33d1e753c82ffc557b4a585c77de43d4c922ebb5",
|
||||
"rev": "4a6b83b05df1a8bd7d99095ec4b4d271f2956b64",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -6,7 +6,25 @@
|
|||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.forgejo;
|
||||
forgejo-cli = pkgs.writeScriptBin "forgejo-cli" ''
|
||||
#!${pkgs.runtimeShell}
|
||||
cd ${cfg.stateDir}
|
||||
sudo=exec
|
||||
if [[ "$USER" != forgejo ]]; then
|
||||
sudo='exec /run/wrappers/bin/sudo -u ${cfg.user} -g ${cfg.group} --preserve-env=GITEA_WORK_DIR --preserve-env=GITEA_CUSTOM'
|
||||
fi
|
||||
# Note that these variable names will change
|
||||
export GITEA_WORK_DIR=${cfg.stateDir}
|
||||
export GITEA_CUSTOM=${cfg.customDir}
|
||||
$sudo ${lib.getExe cfg.package} "$@"
|
||||
'';
|
||||
start-haven = pkgs.writeShellScriptBin "start-haven" (builtins.readFile ./start-haven.sh);
|
||||
|
||||
subdomains = map (subdomain: subdomain + ".${config.secrets.networking.primaryDomain}") [
|
||||
"code"
|
||||
"music"
|
||||
];
|
||||
in
|
||||
{
|
||||
imports = [ ./hardware-configuration.nix ];
|
||||
|
@ -24,10 +42,6 @@ in
|
|||
autostart = false;
|
||||
environment = "${config.users.users.aires.home}";
|
||||
};
|
||||
k3s = {
|
||||
enable = true;
|
||||
role = "server";
|
||||
};
|
||||
msmtp.enable = true;
|
||||
};
|
||||
users = {
|
||||
|
@ -44,33 +58,191 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
# Enable BOINC (distributed research computing)
|
||||
services.boinc = {
|
||||
enable = true;
|
||||
dataDir = "/var/lib/boinc";
|
||||
# TLS certificate renewal via Let's Encrypt
|
||||
security.acme = {
|
||||
acceptTerms = true;
|
||||
defaults = {
|
||||
email = "${config.secrets.users.aires.email}";
|
||||
};
|
||||
|
||||
certs."${config.secrets.networking.primaryDomain}" = {
|
||||
dnsProvider = "namecheap";
|
||||
extraDomainNames = subdomains;
|
||||
webroot = null; # Prevents an assertion error
|
||||
credentialFiles = {
|
||||
"NAMECHEAP_API_USER_FILE" = "${pkgs.writeText "namecheap-api-user" ''
|
||||
${config.secrets.networking.namecheap.api.user}
|
||||
''}";
|
||||
"NAMECHEAP_API_KEY_FILE" = "${pkgs.writeText "namecheap-api-key" ''
|
||||
${config.secrets.networking.namecheap.api.key}
|
||||
''}";
|
||||
};
|
||||
};
|
||||
};
|
||||
# /var/lib/acme/.challenges must be writable by the ACME user
|
||||
# and readable by the Nginx user. The easiest way to achieve
|
||||
# this is to add the Nginx user to the ACME group.
|
||||
users.users.nginx.extraGroups = [ "acme" ];
|
||||
users.users.airsonic.extraGroups = [ "media" ];
|
||||
|
||||
services = {
|
||||
nginx = {
|
||||
enable = true;
|
||||
|
||||
# Use recommended settings per https://nixos.wiki/wiki/Nginx#Hardened_setup_with_TLS_and_HSTS_preloading
|
||||
recommendedGzipSettings = true;
|
||||
recommendedOptimisation = true;
|
||||
#recommendedProxySettings = true; # Recommended settings break Airsonic
|
||||
recommendedTlsSettings = true;
|
||||
|
||||
virtualHosts = {
|
||||
# Base URL: make sure we've got Let's Encrypt running challenges here, and all other requests going to HTTPS
|
||||
"${config.secrets.networking.primaryDomain}" = {
|
||||
# Catchall vhost, will redirect users to HTTPS for all vhosts
|
||||
default = true;
|
||||
enableACME = true;
|
||||
#serverAliases = subdomains;
|
||||
locations."/" = {
|
||||
return = "301 https://$host$request_uri";
|
||||
};
|
||||
};
|
||||
|
||||
# Forgejo
|
||||
"code.${config.secrets.networking.primaryDomain}" = {
|
||||
useACMEHost = "${config.secrets.networking.primaryDomain}";
|
||||
forceSSL = true;
|
||||
listen = [
|
||||
{
|
||||
port = 443;
|
||||
addr = "0.0.0.0";
|
||||
ssl = true;
|
||||
}
|
||||
];
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:3000";
|
||||
proxyWebsockets = true; # needed if you need to use WebSocket
|
||||
extraConfig =
|
||||
# required when the target is also TLS server with multiple hosts
|
||||
"proxy_ssl_server_name on;";
|
||||
};
|
||||
};
|
||||
|
||||
# Airsonic
|
||||
"music.${config.secrets.networking.primaryDomain}" = {
|
||||
useACMEHost = "${config.secrets.networking.primaryDomain}";
|
||||
forceSSL = true;
|
||||
listen = [
|
||||
{
|
||||
port = 443;
|
||||
addr = "0.0.0.0";
|
||||
ssl = true;
|
||||
}
|
||||
];
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:4040";
|
||||
proxyWebsockets = true; # needed if you need to use WebSocket
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Enable Airsonic-Advanced (music streaming)
|
||||
airsonic = {
|
||||
enable = true;
|
||||
war = "${
|
||||
(pkgs.callPackage ../../packages/airsonic-advanced.nix { inherit lib; })
|
||||
}/webapps/airsonic-advanced.war";
|
||||
port = 4040;
|
||||
jre = pkgs.jdk17_headless;
|
||||
jvmOptions = [
|
||||
"-Dserver.use-forward-headers=true"
|
||||
"-Xmx4G"
|
||||
];
|
||||
home = "/storage/services/airsonic-advanced";
|
||||
};
|
||||
|
||||
# Enable BOINC (distributed research computing)
|
||||
boinc = {
|
||||
enable = true;
|
||||
package = pkgs.boinc-headless;
|
||||
dataDir = "/var/lib/boinc";
|
||||
extraEnvPackages = [ pkgs.ocl-icd ];
|
||||
};
|
||||
|
||||
# Enable Forgejo / Gitea (code repository)
|
||||
forgejo = {
|
||||
enable = true;
|
||||
stateDir = "/storage/services/forgejo";
|
||||
# Enable support for Git Large File Storage
|
||||
lfs.enable = true;
|
||||
settings = {
|
||||
server = {
|
||||
DOMAIN = "${config.secrets.networking.primaryDomain}";
|
||||
ROOT_URL = "https://code.${config.secrets.networking.primaryDomain}/";
|
||||
HTTP_PORT = 3000;
|
||||
SSH_PORT = config.secrets.services.forgejo.sshPort;
|
||||
};
|
||||
};
|
||||
useWizard = true;
|
||||
};
|
||||
|
||||
# Enable SSH
|
||||
openssh = {
|
||||
enable = true;
|
||||
ports = [ config.secrets.hosts.haven.ssh.port ];
|
||||
|
||||
settings = {
|
||||
# require public key authentication for better security
|
||||
PasswordAuthentication = false;
|
||||
KbdInteractiveAuthentication = false;
|
||||
PubkeyAuthentication = true;
|
||||
|
||||
PermitRootLogin = "without-password";
|
||||
};
|
||||
};
|
||||
|
||||
# TODO: VPN (Check out Wireguard)
|
||||
};
|
||||
|
||||
# Enable SSH
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
ports = [ 33105 ];
|
||||
# Configure services
|
||||
systemd.services = {
|
||||
# Airsonic: Disable autostart and set environment variables. Started via start-haven script
|
||||
airsonic = {
|
||||
wantedBy = lib.mkForce [ ];
|
||||
};
|
||||
|
||||
settings = {
|
||||
# require public key authentication for better security
|
||||
PasswordAuthentication = false;
|
||||
KbdInteractiveAuthentication = false;
|
||||
PubkeyAuthentication = true;
|
||||
# Foregejo: Disable autostart. Started via start-haven script
|
||||
forgejo = {
|
||||
wantedBy = lib.mkForce [ ];
|
||||
};
|
||||
|
||||
PermitRootLogin = "without-password";
|
||||
# Nginx: Disable autostart. Started via start-haven script
|
||||
nginx = {
|
||||
wantedBy = lib.mkForce [ ];
|
||||
wants = [
|
||||
"airsonic.service"
|
||||
"forgejo.service"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# Open ports
|
||||
networking.firewall = {
|
||||
enable = true;
|
||||
allowedTCPPorts = [
|
||||
80
|
||||
443
|
||||
];
|
||||
};
|
||||
|
||||
# Add extra packages:
|
||||
# 1: forgejo CLI tool
|
||||
# 2: Haven's startup script
|
||||
environment.systemPackages = [
|
||||
forgejo-cli
|
||||
start-haven
|
||||
];
|
||||
|
||||
# Allow Haven to be a build target for other architectures (mainly ARM64)
|
||||
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
|
||||
|
||||
# Open port for OpenVPN
|
||||
networking.firewall.allowedUDPPorts = [ 1194 ];
|
||||
|
||||
# Add script for booting Haven
|
||||
environment.systemPackages = [ start-haven ];
|
||||
}
|
||||
|
|
|
@ -11,25 +11,20 @@ fi
|
|||
set -e
|
||||
|
||||
# Unlock and mount storage directory if we haven't already
|
||||
if [ ! -f /dev/mapper/storage ]; then
|
||||
echo "Unlocking storage partition:"
|
||||
if [ -e "/dev/mapper/storage" ]; then
|
||||
echo "Storage partition already mounted."
|
||||
else
|
||||
echo "Unlocking storage partition..."
|
||||
cryptsetup luksOpen /dev/md/Sapana storage
|
||||
mount /dev/mapper/storage /storage
|
||||
echo "Storage partition mounted."
|
||||
fi
|
||||
|
||||
#echo "Unlocking backup partition:"
|
||||
# 4 TB HDD, partition #2
|
||||
#cryptsetup luksOpen /dev/disk/by-uuid/8dc60329-d27c-4a4a-b76a-861b1e28400e backups --key-file /storage/backups_partition.key
|
||||
#mount /dev/mapper/backups /backups
|
||||
#echo "Storage and backup partitions mounted."
|
||||
|
||||
echo "Starting Duplicacy:"
|
||||
systemctl start duplicacy-web.service
|
||||
echo "Duplicacy started."
|
||||
|
||||
echo "Starting SyncThing:"
|
||||
echo "Starting services..."
|
||||
systemctl restart duplicacy-web.service
|
||||
systemctl restart airsonic.service forgejo.service
|
||||
systemctl --machine aires@.host --user start syncthing.service
|
||||
echo "SyncThing started."
|
||||
systemctl restart nginx.service
|
||||
echo "Services started. Haven is ready to go!"
|
||||
|
||||
exit 0
|
||||
|
|
|
@ -63,6 +63,9 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
# Install additional packages
|
||||
environment.systemPackages = [ pkgs.boinc ];
|
||||
|
||||
# Move files into target system
|
||||
systemd.tmpfiles.rules = [
|
||||
# Use gremlin user's monitor config for GDM (defined above)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 0bc545bf36759ca1ab67e2718bc5771eca72d02f
|
||||
Subproject commit 9b1b7422848beee7137e74a18bf4f9551a1cf044
|
34
packages/airsonic-advanced.nix
Normal file
34
packages/airsonic-advanced.nix
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
lib,
|
||||
stdenv,
|
||||
fetchurl,
|
||||
nixosTests,
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "airsonic-advanced";
|
||||
version = "11.1.4-SNAPSHOT.20240518150716";
|
||||
|
||||
src = fetchurl {
|
||||
url = "https://github.com/kagemomiji/airsonic-advanced/releases/download/${version}/airsonic.war";
|
||||
sha256 = "f4274fadd0acfe7f21d04e34ebb158238d8aaac06c0c76f6a4bf3d2d5bb41156";
|
||||
};
|
||||
|
||||
buildCommand = ''
|
||||
mkdir -p "$out/webapps"
|
||||
cp "$src" "$out/webapps/airsonic-advanced.war"
|
||||
'';
|
||||
|
||||
passthru.tests = {
|
||||
airsonic-starts = nixosTests.airsonic;
|
||||
};
|
||||
|
||||
meta = with lib; {
|
||||
description = "Personal media streamer";
|
||||
homepage = "https://airsonic.github.io";
|
||||
sourceProvenance = with sourceTypes; [ binaryBytecode ];
|
||||
license = lib.licenses.gpl3;
|
||||
platforms = platforms.all;
|
||||
maintainers = with maintainers; [ disassembler ];
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue