Add a11ywatch and related configurations for Podman and Nginx

- Introduced a new module for a11ywatch with Podman support, creating a bridge network and defining backend and frontend containers.
- Configured Nginx to serve the a11ywatch application with SSL and ACME support.
- Added user and group configurations for a11ywatch.
- Created a systemd service to ensure the Podman network exists on boot.

Implement Firefox Container Controller extension and host

- Added a module for the Firefox Container Controller extension, allowing installation via Nix.
- Created a native messaging host for the extension to communicate with the container controller.
- Included CLI helpers to enqueue commands for showing and hiding containers.

Enable fingerprint authentication in PAM

- Configured fingerprint authentication for login, sudo, and swaylock services.

Setup Raspberry Pi OS image creation script

- Developed a script to create a read-only Raspberry Pi OS Lite image with Snapcast client.
- Included configuration for Wi-Fi, hostname, and Snapcast server.
- Implemented user and group setup for Snapcast client and ensured necessary services are enabled.

Document Raspberry Pi Zero W setup instructions

- Added detailed instructions for configuring Raspberry Pi OS on Zero W, including disabling unused services and setting up Snapcast client.

Create test configuration script for NixOS

- Implemented a script to perform dry-builds for NixOS configurations, allowing for easy validation of host configurations.
This commit is contained in:
2025-05-29 00:10:07 +02:00
parent 8e52274edd
commit 53d73142ae
32 changed files with 1280 additions and 104 deletions

View File

@@ -0,0 +1,94 @@
{ config, pkgs, lib, ... }:
let
domain = "a11ywatch.cloonar.com";
confDir = "/var/lib/a11ywatch";
json = pkgs.formats.json { };
in {
# 1) Enable Podman (daemonless, drop-in for docker)
virtualisation.podman.enable = true; # :contentReference[oaicite:0]{index=0}
virtualisation.podman.dockerCompat = true; # :contentReference[oaicite:1]{index=1}
virtualisation.podman.defaultNetwork.settings.dns_enabled = true;# :contentReference[oaicite:2]{index=2}
services.nginx.virtualHosts."${domain}" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:3000/";
};
};
environment.etc."containers/networks/a11ywatch-net.json" = {
source = json.generate "a11ywatch-net.json" ({
name = "a11ywatch-net";
id = "ccb4b7fb90d2df26db27ef0995765b04f52d318db752c9474b470c5ef4d7978d";
driver = "bridge";
network_interface = "podman1";
subnets = [
{
subnet = "10.89.0.0/24";
gateway = "10.89.0.1";
}
];
ipv6_enabled = false;
internal = false;
dns_enabled = true;
ipam_options = {
driver = "host-local";
};
});
};
users.users.a11ywatch = {
isSystemUser = true;
group = "a11ywatch";
home = "/var/lib/a11ywatch";
createHome = true;
};
users.groups.a11ywatch = { };
users.groups.docker.members = [ "a11ywatch" ];
# 2) Create the bridge network on boot via a oneshot systemd service
systemd.services.a11ywatch-net = {
description = "Ensure a11ywatch-net Podman network exists";
wants = [ "podman.service" ];
after = [ "podman.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = ''
${pkgs.podman}/bin/podman network inspect a11ywatch-net >/dev/null 2>&1 \
|| ${pkgs.podman}/bin/podman network create a11ywatch-net
'';
RemainAfterExit = true;
};
wantedBy = [
"multi-user.target"
];
};
# 3) Declare your two containers using the podman backend
virtualisation.oci-containers = {
backend = "podman"; # :contentReference[oaicite:3]{index=3}
containers = {
a11ywatch-backend = {
image = "docker.io/a11ywatch/a11ywatch:latest";
autoStart = true;
ports = [ "3280:3280" ];
volumes = [ "${confDir}:/a11ywatch/conf" ];
environment = { SUPER_MODE = "true"; };
extraOptions = [ "--network=a11ywatch-net" ];
};
a11ywatch-frontend = {
image = "docker.io/a11ywatch/web:latest";
autoStart = true;
ports = [ "3000:3000" ];
volumes = [ "${confDir}:/a11ywatch/conf" ];
environment = { SUPER_MODE = "true"; };
extraOptions = [
"--network=a11ywatch-net"
];
};
};
};
}

View File

@@ -95,8 +95,11 @@
"/home-assistant.cloonar.com/${config.networkPrefix}.97.20"
"/mopidy.cloonar.com/${config.networkPrefix}.97.21"
"/snapcast.cloonar.com/${config.networkPrefix}.97.21"
"/lms.cloonar.com/${config.networkPrefix}.97.21"
"/git.cloonar.com/${config.networkPrefix}.97.50"
"/feeds.cloonar.com/188.34.191.144"
"/nukibridge1a753f72.cloonar.smart/${config.networkPrefix}.100.112"
"/allywatch.cloonar.com/${config.networkPrefix}.97.5"
"/stage.wsw.at/10.254.235.22"
"/prod.wsw.at/10.254.217.23"
@@ -147,6 +150,8 @@
"/bath-bulb-0.cloonar.smart/${config.networkPrefix}.100.41"
"/bath-bulb-0.cloonar.smart/${config.networkPrefix}.100.42"
"/paraclub.at/188.34.191.144"
];
};
};

View File

@@ -1,5 +1,8 @@
{
services.home-assistant.extraComponents = [ "squeezebox" ];
services.home-assistant.extraComponents = [
"squeezebox"
"slimproto"
];
services.home-assistant.config = {
"automation toilet music" = {
alias = "toilet music";
@@ -11,7 +14,7 @@
{
service = "media_player.volume_mute";
target = {
entity_id = "media_player.music_toilet_snapcast_client";
entity_id = "media_player.music_toilet";
};
data = {
is_volume_muted = "{{ trigger.to_state.state != 'on' }}";
@@ -29,7 +32,7 @@
{
service = "media_player.volume_mute";
target = {
entity_id = "media_player.music_bathroom_snapcast_client";
entity_id = "media_player.music_bathroom";
};
data = {
is_volume_muted = "{{ trigger.to_state.state != 'on' }}";
@@ -41,7 +44,7 @@
alias = "piano";
trigger = {
platform = "state";
entity_id = "media_player.music_piano_snapcast_client";
entity_id = "media_player.music_piano";
attribute = "is_volume_muted";
};
condition = [

View File

@@ -26,7 +26,11 @@ in
{
services.mopidy = {
enable = true;
extensionPackages = [ pkgs.mopidy-iris pkgs.mopidy-tunein mopidy-autoplay ];
extensionPackages = [
pkgs.mopidy-iris
pkgs.mopidy-tunein
mopidy-autoplay
];
configuration = ''
[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! filesink location=/run/snapserver/mopidy

View File

@@ -1,28 +1,18 @@
{ pkgs, config, python3Packages, ... }:
{ pkgs, config, lib, python3Packages, ... }:
let
domain = "snapcast.cloonar.com";
mopidyDomain = "mopidy.cloonar.com";
networkPrefix = config.networkPrefix;
snapweb = pkgs.stdenv.mkDerivation {
pname = "snapweb";
version = "0.8";
src = pkgs.fetchzip {
url = "https://github.com/badaix/snapweb/releases/download/v0.8.0/snapweb.zip";
sha256 = "sha256-IpT1pcuzcM8kqWJUX3xxpRQHlfPNsrwhemLmY0PyzjI=";
stripRoot = false;
};
installPhase = ''
mkdir -p $out
cp -r $src/* $out/
'';
};
in
{
security.acme.certs."${domain}" = {
group = "nginx";
};
security.acme.certs."${mopidyDomain}" = {
group = "nginx";
};
sops.secrets.mopidy-spotify = { };
containers.snapcast = {
autoStart = true;
@@ -39,6 +29,13 @@ in
hostPath = "${config.security.acme.certs.${domain}.directory}";
isReadOnly = true;
};
"/var/lib/acme/mopidy/" = {
hostPath = "${config.security.acme.certs.${mopidyDomain}.directory}";
isReadOnly = true;
};
"/run/secrets/mopidy-spotify" = {
hostPath = "${config.sops.secrets.mopidy-spotify.path}";
};
};
config = { lib, config, pkgs, python3Packages, ... }:
let
@@ -51,18 +48,59 @@ in
"--with-metadata"
];
});
snapweb = pkgs.stdenv.mkDerivation {
pname = "snapweb";
version = "0.8";
src = pkgs.fetchzip {
url = "https://github.com/badaix/snapweb/releases/download/v0.8.0/snapweb.zip";
sha256 = "sha256-IpT1pcuzcM8kqWJUX3xxpRQHlfPNsrwhemLmY0PyzjI=";
stripRoot = false;
};
installPhase = ''
mkdir -p $out
cp -r $src/* $out/
'';
};
mopidy-autoplay = pkgs.python3Packages.buildPythonApplication rec {
pname = "Mopidy-Autoplay";
version = "0.2.3";
src = pkgs.python3Packages.fetchPypi {
inherit pname version;
sha256 = "sha256-E2Q+Cn2LWSbfoT/gFzUfChwl67Mv17uKmX2woFz/3YM=";
};
propagatedBuildInputs = [
pkgs.mopidy
] ++ (with pkgs.python3Packages; [
configobj
]);
# no tests implemented
doCheck = false;
meta = with lib; {
homepage = "https://codeberg.org/sph/mopidy-autoplay";
};
};
in
{
networking = {
hostName = "snapcast";
useHostResolvConf = false;
defaultGateway = {
address = "${networkPrefix}.96.1";
address = "${networkPrefix}.97.1";
interface = "eth0";
};
nameservers = [ "${networkPrefix}.97.1" ];
firewall.enable = false;
};
environment.systemPackages = with pkgs; [
# shanocast
];
environment.etc = {
# Creates /etc/nanorc
shairport = {
@@ -83,36 +121,79 @@ in
};
};
systemd.tmpfiles.rules = [
"p /run/snapserver/mopidyfifo 0660 mopidy snapserver -"
];
services.mopidy = {
enable = true;
extensionPackages = [
pkgs.mopidy-iris
pkgs.mopidy-tunein
pkgs.mopidy-spotify
mopidy-autoplay
];
configuration = ''
[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! filesink location=/run/snapserver/mopidyfifo
[file]
enabled = false
[autoplay]
enabled = true
'';
extraConfigFiles = [
"/run/secrets/mopidy-spotify"
];
};
services.snapserver = {
enable = true;
codec = "flac";
http.enable = true;
http.docRoot = "${snapweb}/";
streams.mopidy = {
type = "pipe";
location = "/run/snapserver/mopidy";
};
buffer = 1000;
streamBuffer = 1000;
streams.airplay = {
type = "airplay";
location = "${shairport-sync}/bin/shairport-sync";
query = {
devicename = "Multi Room New";
devicename = "Multi Room";
port = "5000";
params = "--mdns=avahi";
};
sampleFormat = "44100:16:2";
codec = "pcm";
};
streams.mopidy = {
type = "pipe";
location = "/run/snapserver/mopidyfifo";
};
streams.mixed = {
type = "meta";
location = "/airplay/mopidy";
type = "meta";
location = "meta:///airplay/mopidy?name=Mixed&sampleformat=44100:16:2";
codec = "opus";
};
};
# run after tmpfiles-setup
systemd.services.snapserver = {
after = [ "systemd-tmpfiles-setup.service" ];
requires = [ "systemd-tmpfiles-setup.service" ];
};
systemd.services.mopidy = {
after = [ "systemd-tmpfiles-setup.service" ];
requires = [ "systemd-tmpfiles-setup.service" ];
};
services.avahi.enable = true;
services.avahi.publish.enable = true;
services.avahi.publish.userServices = true;
services.nginx.enable = true;
services.nginx.virtualHosts."snapcast.cloonar.com" = {
services.nginx.virtualHosts."${domain}" = {
sslCertificate = "/var/lib/acme/snapcast/fullchain.pem";
sslCertificateKey = "/var/lib/acme/snapcast/key.pem";
sslTrustedCertificate = "/var/lib/acme/snapcast/chain.pem";
@@ -131,6 +212,26 @@ in
'';
};
services.nginx.virtualHosts."${mopidyDomain}" = {
sslCertificate = "/var/lib/acme/mopidy/fullchain.pem";
sslCertificateKey = "/var/lib/acme/mopidy/key.pem";
sslTrustedCertificate = "/var/lib/acme/mopidy/chain.pem";
forceSSL = true;
extraConfig = ''
proxy_buffering off;
'';
locations."/".extraConfig = ''
proxy_pass http://127.0.0.1:6680;
proxy_set_header Host $host;
proxy_redirect http:// https://;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
'';
};
system.stateVersion = "23.05";
};
};

View File

@@ -0,0 +1,94 @@
{ config, pkgs, lib, ... }:
let
domain = "a11ywatch.cloonar.com";
confDir = "/var/lib/a11ywatch";
json = pkgs.formats.json { };
in {
# 1) Enable Podman (daemonless, drop-in for docker)
virtualisation.podman.enable = true; # :contentReference[oaicite:0]{index=0}
virtualisation.podman.dockerCompat = true; # :contentReference[oaicite:1]{index=1}
virtualisation.podman.defaultNetwork.settings.dns_enabled = true;# :contentReference[oaicite:2]{index=2}
services.nginx.virtualHosts."${domain}" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:3000/";
};
};
environment.etc."containers/networks/a11ywatch-net.json" = {
source = json.generate "a11ywatch-net.json" ({
name = "a11ywatch-net";
id = "ccb4b7fb90d2df26db27ef0995765b04f52d318db752c9474b470c5ef4d7978d";
driver = "bridge";
network_interface = "podman1";
subnets = [
{
subnet = "10.89.0.0/24";
gateway = "10.89.0.1";
}
];
ipv6_enabled = false;
internal = false;
dns_enabled = true;
ipam_options = {
driver = "host-local";
};
});
};
users.users.a11ywatch = {
isSystemUser = true;
group = "a11ywatch";
home = "/var/lib/a11ywatch";
createHome = true;
};
users.groups.a11ywatch = { };
users.groups.docker.members = [ "a11ywatch" ];
# 2) Create the bridge network on boot via a oneshot systemd service
systemd.services.a11ywatch-net = {
description = "Ensure a11ywatch-net Podman network exists";
wants = [ "podman.service" ];
after = [ "podman.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = ''
${pkgs.podman}/bin/podman network inspect a11ywatch-net >/dev/null 2>&1 \
|| ${pkgs.podman}/bin/podman network create a11ywatch-net
'';
RemainAfterExit = true;
};
wantedBy = [
"multi-user.target"
];
};
# 3) Declare your two containers using the podman backend
virtualisation.oci-containers = {
backend = "podman"; # :contentReference[oaicite:3]{index=3}
containers = {
a11ywatch-backend = {
image = "docker.io/a11ywatch/a11ywatch:latest";
autoStart = true;
ports = [ "3280:3280" ];
volumes = [ "${confDir}:/a11ywatch/conf" ];
environment = { SUPER_MODE = "true"; };
extraOptions = [ "--network=a11ywatch-net" ];
};
a11ywatch-frontend = {
image = "docker.io/a11ywatch/web:latest";
autoStart = true;
ports = [ "3000:3000" ];
volumes = [ "${confDir}:/a11ywatch/conf" ];
environment = { SUPER_MODE = "true"; };
extraOptions = [
"--network=a11ywatch-net"
];
};
};
};
}

View File

@@ -58,7 +58,6 @@ in {
./zammad.nix
./proxies.nix
./matrix.nix
./tinder-api.nix
];
networkPrefix = config.networkPrefix;

View File

@@ -25,6 +25,10 @@
publicKey = "HE4eX4IMKG8eRDzcriy6XdIPV71uBY5VTqjKzfHPsFI=";
allowedIPs = [ "${config.networkPrefix}.98.203/32" ];
}
{
publicKey = "yv0AWQl4LFebVa7SvwdxpEmB3PPglwjoKy6A3og93WI=";
allowedIPs = [ "${config.networkPrefix}.98.204/32" ];
}
];
};
wg_epicenter = {

View File

@@ -18,6 +18,8 @@ palworld: ENC[AES256_GCM,data:rdqChPt4gSJHS1D60+HJ+4m5mg35JbC+pOmevK21Y95QyAIeyB
ark: ENC[AES256_GCM,data:YYGyzoVIKI9Ac1zGOr0BEpd3fgBsvp1hSwAvfO07/EQdg8ufMWUkNvqNHDKN62ZK5A1NnY3JTA1p4gyZ4ryQeAOsbwqU1GSk2YKHFyPeEnpLz/Ml82KMsv7XPGXuKRXZ4v3UcLu0R8k1Q0gQsMWo4FjCs3FF5mVtJG/YWxxbCYHoBLJ/di5p0DgjuFgJBQknYBpuLzr+yIoeqEyN7XcGYAJO53trEJuOOxLILULifkqISHjZ66i5F1fHW0iUdRbmeWV4aOAeOrsQqXYv,iv:gJwV5ip84zHqpU0l0uESfWWOtcgihMvEEdLaeI+twcU=,tag:sy8udVQsKxV/jOqwhJmWAg==,type:str]
firefox-sync: ENC[AES256_GCM,data:uAJAdyKAuXRuqCFl8742vIejU5RnAPpUxUFCC0s0QeXZR5oH2YOrDh+3vKUmckW4V1cIhSHoe+4+I4HuU5E73DDrJThfIzBEw+spo4HXwZf5KBtu3ujgX6/fSTlPWV7pEsDDsZ0y6ziKPADBDym8yEk0bU9nRedvTBUhVryo3aolzF/c+gJvdeDvKUYa8+8=,iv:yuvE4KG7z7Rp9ZNlLiJ2rh0keed3DuvrELzsfJu4+bs=,tag:HFo1A53Eva31NJ8fRE7TlA==,type:str]
knot-tsig-key: ENC[AES256_GCM,data:H2jEkRSVSIJl1dSolAXj9uUmzD6eEh9zPpoajZLxfuuFt7/LJF8aCEHyk+Q=,iv:9aqywuaILYtejuZGd+Cy8oErrHIoL2XhL1g9HtcUn/o=,tag:K3SnVEXGC/NhlchU7OyA6Q==,type:str]
mopidy-spotify: ENC[AES256_GCM,data:O3s6UvTP8z5KZPCq10GaaEQntWAEoxGFMnTkeUz9AfobrpsGZJcQgyazFX2u4DgAaIjNb34032MISotmuVQDJ14mi8xI5vC9w/Vf16v3TFu/dSKGZNb5ZPQwTUQ+iMJf7chgwOV9guThhutVJokb6pLxzt7fSht7,iv:j8+X1AmuWzIJdafzgrE7WBIlZ7coNNi0/Zn6JObR6rw=,tag:fiw6M2/6nfEPqEgV2YOWLg==,type:str]
lms-spotify: ENC[AES256_GCM,data:gh5kx/MDSefNLbZsnovRc3rNWxp/RTrJ4A2WIs1QMi4JVGFj9SppdsErMXW4y/IFj/YxH1X7JtwvhptO/p3P2CFK0XL2I1vFVqPuj7LavDHJK7GXPAV6+x17ldvPXgym5NqHjzHi4gtj7U/bMJlz0NxrFsrrjMcY9nmNX2vVwKlINUFqWb1JRvQsJ8ujSutjJbGtAY/bVQI8OFtU29QGKw1CU3RH/bgXIzxGiLQsUd68w7N17oKYj8MiTpGVcovMCRKwwUbd9w==,iv:4aVy+r//s1Cs9q4GasR3vSAb8b/VB/8Mx5E1jWAUA+E=,tag:TgTSLLH1OG9ySi2tZ+hK1Q==,type:str]
sops:
kms: []
gcp_kms: []
@@ -60,8 +62,8 @@ sops:
WXJpUUxadERyYUExRFMzNzBXaUVET3cKG9ZwWy5YvTr/BAw/i+ZJos5trwRvaW5j
eV/SHiEteZZtCuCVFAp3iolE/mJyu97nA2yFwWaLN86h+/xkOJsdqA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-05-01T20:36:09Z"
mac: ENC[AES256_GCM,data:ZtXJcuwDpDlBl2xdRtMF1PwwqbW00Eps2ZZG5x4C2djAq+meXJCxKS9sNazQhMYFOqphQXe3JEhChykLxnJyWivY/Er1ig2sU6Ke1uVcfSP85B1/rpzhe/7QI+GBDWrkCk1O0xGKKj8fWt+Yv2MV8gw2XctdtJ9Md4imUhcK7zo=,iv:5NFH+7Z0alBiq/b94T40XJSCar2+BGaFB20z0Kc59fU=,tag:18n0tt17RNMyyE0eECH2kQ==,type:str]
lastmodified: "2025-05-23T19:49:55Z"
mac: ENC[AES256_GCM,data:+03w76nM2yhAipvIYgbrdxDT9EiRzqhWuOtngiJprp+zRYNf8uRJaMNSfVNkmIQ/PUikQpDLoz98zKJNGFsdT6O6JC1mq2/e5MaFMhk7GiV/T93YEhGpU8/CSzKtI+uIQaCO7jCfPFOtsiOBcsscqfAYlWlyCecKrg9zMPmNOaE=,iv:nmy5ATUrGXLZpSvZCSyDnoxHtRyNmXiEqbw62anH7LI=,tag:5nr6/cCFlrwqH9kNGp25og==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.9.4

View File

@@ -24,12 +24,15 @@ in {
./utils/modules/sops.nix
./utils/modules/nur.nix
./modules/appimage.nix
./modules/desktop
./modules/sway/sway.nix
# ./modules/printer.nix
# ./modules/cyberghost.nix
./utils/modules/autoupgrade.nix
./modules/puppeteer.nix
# ./modules/development
./cachix.nix
./users
@@ -38,6 +41,7 @@ in {
./modules/coding.nix
# ./modules/steam.nix
./modules/fingerprint.nix
./hardware-configuration.nix
@@ -57,6 +61,7 @@ in {
open-sans
nix-prefetch
jq
mkcert
oh-my-zsh
zsh-autosuggestions
zsh-completions

View File

@@ -0,0 +1,4 @@
{ pkgs, ... }: {
imports = [
];
}

View File

@@ -0,0 +1,67 @@
# firefox-container-controller-extension.nix
# Import this file in your configuration.nix to build and install the Container Controller extension.
# Usage in configuration.nix:
#
# let
# containerControllerXpi = import ./firefox-container-controller-extension.nix { inherit pkgs; };
# in {
# programs.firefox = {
# enable = true;
# profiles.default = {
# extensions = [ containerControllerXpi ];
# };
# };
# }
{ pkgs }:
pkgs.runCommand "firefox-containercontroller-xpi" {
nativeBuildInputs = [ pkgs.zip ];
} ''
# Create temp dir for packaging
TMPDIR=$(mktemp -d)
cd "$TMPDIR"
# Write manifest.json without leading spaces
cat > manifest.json << 'EOF'
{
"manifest_version": 2,
"name": "Container Controller",
"version": "1.0",
"applications": { "gecko": { "id": "containercontroller@cloonar.com" } },
"permissions": ["containers", "nativeMessaging"],
"background": { "scripts": ["background.js"] }
}
EOF
# Write background.js without indentation
cat > background.js << 'EOF'
async function poll() {
const resp = await browser.runtime.sendNativeMessage(
"com.firefox.containercontroller", {}
);
if (resp.userContextId && resp.action) {
try {
if (resp.action === "hide") {
await browser.containers.hideContainer({ userContextId: resp.userContextId });
} else if (resp.action === "show") {
await browser.containers.showContainer({ userContextId: resp.userContextId });
}
} catch (e) {}
}
}
// Poll every second
setInterval(poll, 1000);
EOF
# Ensure the Firefox extensions directory exists in the output
mkdir -p "$out/share/firefox/extensions"
# Create ZIP archive at root of package
# and use the updated extension id for the filename
zip -r "$out/share/firefox/extensions/containercontroller@cloonar.com.xpi" manifest.json background.js
# Clean up
rm -rf "$TMPDIR"
''

View File

@@ -0,0 +1,59 @@
{ pkgs, lib, ... }:
let
# 1) Nativemessaging host: reads and clears the queued JSON command
containerControllerHost = pkgs.writeScriptBin "firefox-containercontroller-host" ''
#!/usr/bin/env bash
CMD_FILE="$HOME/.cache/firefox-container-command.json"
if [ -f "$CMD_FILE" ]; then
cat "$CMD_FILE"
rm "$CMD_FILE"
else
echo '{}'
fi
'';
# 2) CLI helper to enqueue a “hide” command
hideContainer = pkgs.writeScriptBin "hide-container" ''
#!/usr/bin/env bash
if [ -z "$1" ]; then
echo "Usage: $0 <userContextId>" >&2
exit 1
fi
ID="$1"
mkdir -p "$HOME/.cache"
printf '{"userContextId": %s, "action": "hide"}' "$ID" \
> "$HOME/.cache/firefox-container-command.json"
'';
# 3) CLI helper to enqueue a “show” command
showContainer = pkgs.writeScriptBin "show-container" ''
#!/usr/bin/env bash
if [ -z "$1" ]; then
echo "Usage: $0 <userContextId>" >&2
exit 1
fi
ID="$1"
mkdir -p "$HOME/.cache"
printf '{"userContextId": %s, "action": "show"}' "$ID" \
> "$HOME/.cache/firefox-container-command.json"
'';
in
{
# Install host + helpers
environment.systemPackages = [
containerControllerHost
hideContainer
showContainer
];
# Register the nativemessaging host for our extension
environment.etc."mozilla/native-messaging-hosts/com.firefox.containercontroller.json".text =
builtins.toJSON {
name = "com.firefox.containercontroller";
description = "Native messaging host for Container Controller";
path = containerControllerHost;
type = "stdio";
allowed_extensions = [ "containercontroller@cloonar.com" ];
};
}

View File

@@ -1,11 +1,13 @@
{ config, pkgs, lib, ... }:
let
pkgs = import (builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/refs/heads/nixos-unstable.tar.gz") { };
mcp-servers = import (builtins.fetchTarball "https://github.com/natsukium/mcp-servers-nix/archive/refs/heads/main.tar.gz") { inherit pkgs; };
in {
nixpkgs.overlays = [
(import (builtins.fetchTarball "https://github.com/natsukium/mcp-servers-nix/archive/main.tar.gz")).overlays.default
];
environment.systemPackages = with pkgs; [
mcp-server-fetch
];
mcp-servers.lib.mkConfig pkgs {
programs = {
fetch.enable = true;
memory.enable = true;
};
};
}

View File

@@ -0,0 +1,9 @@
{ config, pkgs, ... }:
{
services.fprintd.enable = true;
security.pam.services.login.fprintAuth = true;
security.pam.services.sudo.fprintAuth = true;
security.pam.services.swaylock.fprintAuth = true;
}

View File

@@ -5,7 +5,10 @@ let
name = "social";
desktopName = "Firefox browser with social profile";
exec = "firefox -P social";
# exec= "firefox -P social --marionette --remote-debugging-port 2828 --no-remote";
};
in {
environment.systemPackages = [ socialDesktopItem ];
environment.systemPackages = with pkgs; [
socialDesktopItem
];
}

View File

@@ -6,6 +6,7 @@
./modules/mysql.nix
./modules/postfix.nix
./utils/modules/nginx.nix
./modules/bitwarden
./modules/authelia
@@ -25,6 +26,8 @@
./hardware-configuration.nix
# ./modules/a11ywatch.nix
./modules/web/typo3.nix
./modules/web/stack.nix
@@ -38,6 +41,10 @@
./sites/stage.cloonar-technologies.at.nix
./sites/cloonar.dev.nix
./sites/paraclub.at.nix
./sites/api.paraclub.at.nix
./sites/module.paraclub.at.nix
./sites/tandem.paraclub.at.nix
./sites/paraclub.cloonar.dev.nix
./sites/api.paraclub.cloonar.dev.nix
./sites/tandem.paraclub.cloonar.dev.nix

View File

@@ -0,0 +1,24 @@
{ config, ... }:
{
#Collabora Containers
virtualisation.oci-containers.containers.pally = {
image = "docker.io/croox/pa11y-dashboard:latest";
ports = [ "4000:4000/tcp" ];
extraOptions = [
"--pull=newer"
];
};
services.nginx.virtualHosts."allywatch.cloonar.com" = {
enableACME = true;
forceSSL = true;
extraConfig = ''
# static files
location ^~ / {
proxy_pass http://127.0.0.1:4000;
proxy_set_header Host $host;
}
'';
};
}

View File

@@ -0,0 +1,24 @@
{ config, ... }:
{
#Collabora Containers
virtualisation.oci-containers.containers.pally = {
image = "docker.io/croox/pa11y-dashboard:latest";
ports = [ "4000:4000/tcp" ];
extraOptions = [
"--pull=newer"
];
};
services.nginx.virtualHosts."allywatch.cloonar.com" = {
enableACME = true;
forceSSL = true;
extraConfig = ''
# static files
location ^~ / {
proxy_pass http://127.0.0.1:4000;
proxy_set_header Host $host;
}
'';
};
}

View File

@@ -4,7 +4,7 @@
enableDefaultLocations = false;
enableMysql = true;
authorizedKeys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCmLPJoHwL+d7dnc3aFLbRCDshxRSQ0dtAVv/LYBn2/PBlZcIyVO9drjr702GL9QuS5DQyjtoZjSOvv1ykBKedUwY3XDyyZgtqjleojKIFMXkdXtD5iG+RUraUfzcFCZU12BYXSeAXK1HmIjSDUtDOlp6lVVWxNpz1vWSRtA/+PULhP+n5Cj7232Wf372+EPfQPntOlcMbyrDLFtj7cUz+E6BH0qdX0l3QtIVnK/C1iagPAwLcwPJd9Sfs8lj5C4g8T9uBJa6OX+87lE4ySYY+Cik9BN59S0ctjXvWCFsPO3udQSC1mf33XdDenc2mbi+lZWTfrN8S2K5CsbxRsVBlbapFBRwufEpN4iQnaTu1QmzDrmktBFAPJ2jvjBJPIx6W3KOy3kUwh9WNhzd/ubf9dFTHzkTzgluo/Zk6/S8fTJiA4rbYKSkLw9Y265bvtR1kfUBLKSa/Axe5dkKysX1RNKfTJEwbh2TfIS3apQPZZc5kIEWfeK/6kbQX7WJZFtTs="
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKcfDiAqwP8FnH0Nl/joMtaRWwiNXbDBYk0wq1gnC5G8"
];
extraConfig = ''
add_header X-Frame-Options "SAMEORIGIN";
@@ -31,4 +31,6 @@
phpPackage = pkgs.php82.withExtensions ({ enabled, all }:
enabled ++ [ all.imagick ]);
};
services.nginx.virtualHosts."api.paraclub.at".acmeRoot = lib.mkForce "/var/lib/acme/acme-challenge";
}

View File

@@ -6,7 +6,8 @@ in {
services.nginx.virtualHosts."${domain}" = {
forceSSL = true;
enableACME = true;
acmeRoot = null;
# acmeRoot = null;
acmeRoot = "/var/lib/acme/acme-challenge";
root = "${dataDir}";
locations."/favicon.ico".extraConfig = ''
@@ -37,7 +38,7 @@ in {
#home = "/home/${domain}";
group = "nginx";
openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCwgjsgjxOSX4ZeLuhSq+JumnEa1bKS3fwlA8LDuxvOWXs2Zn4Hwa04ZuM59jzqifwGGMJOFxErm8+5oH2QQFa0wgg8zEG+2U1AzjMNk5+mxrhnLPGAMlnqXmkGi0Jj2nFwKaEM9kcO5UUqRP71BFdGtP74wRcaVpT4TTPzCQl1HTdwzmAOT+3yQ364kyAHXTwQOAjiFcSAlNfZ5C2eeNC642bv6Dfi6mMWi55tdNV6HUn7y2cbq8wscDG7gla8bN3xivuO6POWqyCpHtLxDhppLYJ28ZwqpcynRAXDnVYlT3DmPw1bDs/eBlkjauGR/oM8phka3No3cREBYpSWK7mJeqIIWSV0Z4dvFLeWh6MM4AVhX3HOW7jcxf2tUmpzre6S10HjXj3lLES7oJO4uOYoJWxaGcqFiUc9BOxqLN9FqECXuzfC0apCr0OYm5T2NsSmzlkBPzCa2EqBBI0u5XGcDKgpBA4gD8kuD+8Cj5DxPzXP+IdX1jhHRVsI5nucTvM="
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEJQLKQ5skQyBRYe8S5Sb72YLE9QFnrHesEWtcf+0D4c"
];
};
users.groups.${domain} = {};

View File

@@ -8,6 +8,13 @@ in {
enableACME = true;
acmeRoot = null;
root = "${dataDir}";
serverAliases = [ "www.${domain}" ];
extraConfig = ''
if ($host != '${domain}') {
return 301 $scheme://${domain}$request_uri;
}
'';
locations."/favicon.ico".extraConfig = ''
log_not_found off;
@@ -37,7 +44,7 @@ in {
#home = "/home/${domain}";
group = "nginx";
openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDbSqS0TrJnmihjuIwLY74jKmuErF5jarQeVEQbnl7k8DDfVXP6DKybK2wVRIrAMN2VQzgXWWyRj2wNZrvq1whZon6CrEDxDVN/VDGS99pazczbrypmycVnPsevtS3wrEhiQrwCplkPxoZGlSAPGtx3SOzql+iG7xrhJfuPDCgwIboKf8Tir170aflH7ZfXqUX+V5QMbOn+roT8Tj7vUd/za3o3okJQrW3NUHT6/0TDkGsn+lJp30e94GF5RDLUJgM8pBf45WM94dv1uEfRI7+AQJZRta3X2VNSbb8I2dPNLmgxYQaW1VtwGP/RfxoFESdQubN74p+VxNeP7z5AFiZfhEYb0yiAwXiavN7fStXX/MKXxMicS2fdGzieXLWpLol70xx19492kOnlzoiPKJRosNw8N60R+AkbPYdwl5z5uKDn1ve79YaWB3KWS5Pcr9IT1wZAc48UePL6QtcDppHe8tUflPP5h/LCKOmAioWG59YF5pKfYNLSXJzmiudzzrs="
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINkaoMNNBDqjvKrQg2YvXUBlJSZwvlKe3wS5cIDdR3pd"
];
};
users.groups.${domain} = {};

View File

@@ -7,7 +7,9 @@ in {
services.nginx.virtualHosts."${domain}" = {
forceSSL = true;
enableACME = true;
acmeRoot = null;
# acmeRoot = null;
acmeRoot = "/var/lib/acme/acme-challenge";
root = "${dataDir}";
locations."/favicon.ico".extraConfig = ''
@@ -38,7 +40,7 @@ in {
#home = "/home/${domain}";
group = "nginx";
openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDpezoJfaqSlQKhbzIRxQysmSmU5tih0SGFh4Eiy3YjfxiJSCRCuVTBCUmnhDCPsJZK+5xEDGarO8UfiqxZfxEyEL5d7IcRQJ/uRSFhYzByGbkziLM760KYqBzaE2Siu+zk625KOm6BN9qWGZdirejwf1Ay9EYmUdNiCMBBFLkPaQkZ8IEuMavf1wHEiZLas25eK7oJWHYKltcluH05QEF+5ODu88nlSpFlz2FjxJSbLDf7qeUba/L2OL124dTU5NIDNzwZLCKjpp8aTYzTaoox7KXUVRmy1X4Or61WhSxw9+LGyrAZLsW+l0a4FgY17V5HnF5/jf8eOpkuVdwtd29KCheJ4BdUfomV8vEt6S0hUP66VqJn6MliuL+10KM6TjLnjg0McPp1LPuSFRoLzO0YetTZzeVc0oBIr9Z3vjm6jt1dYcUtaydn/fc+FgoqpIOLz6EOGCz/CmyaV4rLk2BFKqtx5GP1wbP36hVkyWpREbEMILpFKDOyp21fC67mb0M="
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPTsI0YyToIokBIcSf6j70iI68pKd4fPkRpqByFkZLRB"
];
};
users.groups.${user} = {};