From c72b30aa6a30186eed46999cc1c5b35e0b58c934 Mon Sep 17 00:00:00 2001 From: julian Date: Sun, 15 Oct 2023 21:44:25 +0200 Subject: [PATCH] Make AirPlay 2 work for Shairport Sync on Audio host - use nqptp - use Shairport Sync and nqptp versions, which work - disable IPv6, since Shairport Sync doesn't work with it for some reason - configure firewall for AirPlay 2 - use correct subnet --- config/hosts/audio/networking.nix | 26 ++++++------ config/hosts/audio/shairport-sync.nix | 57 +++++++++++++++++++++++++-- flake.nix | 34 ++++++++++++++-- 3 files changed, 98 insertions(+), 19 deletions(-) diff --git a/config/hosts/audio/networking.nix b/config/hosts/audio/networking.nix index a109ecf..b666cd4 100644 --- a/config/hosts/audio/networking.nix +++ b/config/hosts/audio/networking.nix @@ -1,19 +1,21 @@ { config, pkgs, ... }: { - networking.interfaces.net0 = { - ipv4.addresses = [ - { - address = "10.31.210.10"; - prefixLength = 25; - } - ]; - ipv6.addresses = [ - { - address = "2a07:c480:0:1d2:0000:0000:1000:000a"; - prefixLength = 64; - } + networking = { + interfaces.net0 = { + ipv4.addresses = [ + { + address = "10.31.210.10"; + prefixLength = 23; + } + ]; + }; + defaultGateway = "10.31.210.1"; + nameservers = [ + "10.31.210.1" ]; + # Disable IPv6, since Shairport-Sync doesn't work with IPv6. Unclear why. + enableIPv6 = false; }; systemd.network.links."10-net0" = { matchConfig.MACAddress = "1E:EF:2D:92:81:DA"; diff --git a/config/hosts/audio/shairport-sync.nix b/config/hosts/audio/shairport-sync.nix index 0407f79..5c323e0 100644 --- a/config/hosts/audio/shairport-sync.nix +++ b/config/hosts/audio/shairport-sync.nix @@ -1,9 +1,14 @@ -{ ... }: +# Sources for this configuration: +# - https://github.com/mikebrady/shairport-sync/blob/f5c4b51da827a7f8d9a72a1b6f986807aba47bfc/AIRPLAY2.md +# - https://github.com/mikebrady/nqptp +# - https://github.com/mikebrady/nqptp/blob/050a8c2de9f3e1f4859abf9b36d2f18afd4c34d7/nqptp.service.in + +{ pkgs, lib, ... }: + { services.shairport-sync = { enable = true; - openFirewall = true; - arguments = "-v -o alsa -- -d plughw:1,0 -r 48000"; + arguments = "-o alsa -- -d plughw:1,0 -r 48000"; }; environment.etc.shairport-sync-config = { @@ -11,4 +16,50 @@ source = ./shairport-sync.conf; target = "shairport-sync.conf"; }; + + users.users.nqptp = { + isSystemUser = true; + group = "nqptp"; + }; + users.groups.nqptp = { }; + + systemd.services.nqptp = { + enable = true; + description = "NQPTP -- Not Quite PTP"; + unitConfig = { + Wants = [ "network-online.target" ]; + After = [ "network.target" "network-online.target" ]; + Before = [ "shairport-sync.service" ]; + }; + serviceConfig = { + ExecStart = "${pkgs.nqptp}/bin/nqptp"; + User = "nqptp"; + Group = "nqptp"; + AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; + }; + wantedBy = [ "multi-user.target" ]; + }; + + # See here for docs: + # https://github.com/mikebrady/shairport-sync/blob/4ca5a15de2d53c69e6c3c23b0440c27978bb91df/TROUBLESHOOTING.md#ufw-firewall-blocking-ports-commonly-includes-raspberry-pi + # These docs seem like they also include the ports for AirPlay 1. Since we're + # doing just AirPlay 2, we can have a more restrictive firewall than + # documented there. + # This more restritive firewall also matches with a packet capture I did. + networking.firewall = { + allowedTCPPorts = [ 7000 ]; + allowedUDPPorts = [ 319 320 5353 ]; + allowedTCPPortRanges = [ + { + from = 32768; + to = 60999; + } + ]; + allowedUDPPortRanges = [ + { + from = 32768; + to = 60999; + } + ]; + }; } diff --git a/flake.nix b/flake.nix index 07c792f..f604f3a 100644 --- a/flake.nix +++ b/flake.nix @@ -18,12 +18,38 @@ outputs = { nixpkgs, nixpkgs-unstable, nixos-generators, ... }: let - # Shairport Sync with metadata and MQTT support. - shairportSyncExtendedNixpkgsUnstableOverlay = final: prev: { - shairport-sync = (prev.shairport-sync.override { enableMetadata = true; }).overrideAttrs (finalAttr: previousAttr: { + # Shairport Sync 4.3.1 (with nqptp 1.2.4) with metadata, MQTT and AirPlay 2 support. + shairportSync431ExtendedNixpkgsUnstableOverlay = final: prev: { + shairport-sync = (prev.shairport-sync.override { enableMetadata = true; enableAirplay2 = true; }).overrideAttrs (finalAttr: previousAttr: { # See: https://github.com/mikebrady/shairport-sync/blob/e78a88b64adfe7b5f88fd6faedf55c57445bb240/CONFIGURATION%20FLAGS.md configureFlags = previousAttr.configureFlags ++ [ "--with-mqtt-client" ]; buildInputs = previousAttr.buildInputs ++ [ final.mosquitto ]; + # Use specific Shairport Sync and nqptp versions, since with those the + # following error doesn't happen: + # fatal error: The nqptp service on this system, which is required for + # Shairport Sync to operate, does not seem to be initialised. + src = final.fetchFromGitHub { + owner = "mikebrady"; + repo = finalAttr.pname; + rev = "4.3.1"; + hash = "sha256-Yj0SKMKACj2B/ADPkUzO4EvaYZX39erKmjaTsr5UN0s="; + }; + }); + nqptp = prev.nqptp.overrideAttrs (finalAttr: previousAttr: { + # See Shairport Sync version note. + src = final.fetchFromGitHub { + owner = "mikebrady"; + repo = finalAttr.pname; + rev = "1.2.4"; + hash = "sha256-roTNcr3v2kzE6vQ5plAVtlw1+2yJplltOYsGGibtoZo="; + }; + # Custom install phase to avoid setcap. + # See: + # https://github.com/mikebrady/nqptp/blob/1.2.4/Makefile.am#L23 + installPhase = '' + mkdir -p $out/bin + cp nqptp $out/bin/ + ''; }); }; in { @@ -31,7 +57,7 @@ meta = { nixpkgs = nixpkgs.legacyPackages."x86_64-linux"; nodeNixpkgs = { - audio = nixpkgs-unstable.legacyPackages."x86_64-linux".extend shairportSyncExtendedNixpkgsUnstableOverlay; + audio = nixpkgs-unstable.legacyPackages."x86_64-linux".extend shairportSync431ExtendedNixpkgsUnstableOverlay; }; };