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:
94
hosts/fw/modules/allywatch.nix
Normal file
94
hosts/fw/modules/allywatch.nix
Normal 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"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
};
|
||||
};
|
||||
|
||||
94
hosts/fw/modules/web/allywatch.nix
Normal file
94
hosts/fw/modules/web/allywatch.nix
Normal 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"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -58,7 +58,6 @@ in {
|
||||
./zammad.nix
|
||||
./proxies.nix
|
||||
./matrix.nix
|
||||
./tinder-api.nix
|
||||
];
|
||||
|
||||
networkPrefix = config.networkPrefix;
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
publicKey = "HE4eX4IMKG8eRDzcriy6XdIPV71uBY5VTqjKzfHPsFI=";
|
||||
allowedIPs = [ "${config.networkPrefix}.98.203/32" ];
|
||||
}
|
||||
{
|
||||
publicKey = "yv0AWQl4LFebVa7SvwdxpEmB3PPglwjoKy6A3og93WI=";
|
||||
allowedIPs = [ "${config.networkPrefix}.98.204/32" ];
|
||||
}
|
||||
];
|
||||
};
|
||||
wg_epicenter = {
|
||||
|
||||
Reference in New Issue
Block a user