From 9b43b89f005aba27afa7040bc35716004643b34b Mon Sep 17 00:00:00 2001 From: "Mikkel T. Hoegh" Date: Thu, 14 May 2026 18:12:44 +0200 Subject: [PATCH 1/4] Add flake.nix for installation on NixOS --- flake.lock | 61 +++++++++++++++++++ flake.nix | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..c6d2e2b --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1778672786, + "narHash": "sha256-Blg88K1jwG+P0Mr27+rKMFCufdrWkV3wWh9AdYtz0FQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "eef00dfd8a712b34af845f9350bac681b1228bd1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..cc03525 --- /dev/null +++ b/flake.nix @@ -0,0 +1,171 @@ +{ + description = "Local Docker Development DNS"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { + self, + nixpkgs, + flake-utils, + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + packages = { + ldddns = pkgs.buildGo126Module { + pname = "ldddns"; + version = self.shortRev or self.dirtyShortRev or "dev"; + + src = self; + + # Relax the go directive to match the Go version available in nixpkgs. + prePatch = '' + substituteInPlace go.mod --replace-fail "go 1.26.3" "go 1.26" + ''; + + vendorHash = "sha256-FI7kSIn1QNDcEqDECiGNVLZ5z7LWPxxunK6eKH3D46Y="; + + # Tests require /etc/protocols which is unavailable in the Nix sandbox. + doCheck = false; + + env.CGO_ENABLED = 0; + + ldflags = [ + "-s" + "-w" + "-X main.version=${self.shortRev or self.dirtyShortRev or "dev"}" + ]; + + buildFlags = [ "-trimpath" ]; + + postInstall = '' + mv $out/bin/ldddns.arnested.dk $out/bin/ldddns + ''; + + meta = { + description = "Local Docker Development DNS"; + homepage = "https://ldddns.arnested.dk"; + license = pkgs.lib.licenses.mit; + maintainers = [ ]; + platforms = pkgs.lib.platforms.linux; + }; + }; + + default = self.packages.${system}.ldddns; + }; + + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + go_1_26 + gopls + goreleaser + ]; + }; + } + ) + // { + nixosModules.default = + { + config, + lib, + pkgs, + ... + }: + let + cfg = config.services.ldddns; + in + { + options.services.ldddns = { + enable = lib.mkEnableOption "ldddns - Local Docker Development DNS"; + + package = lib.mkOption { + type = lib.types.package; + default = self.packages.${pkgs.stdenv.hostPlatform.system}.ldddns; + defaultText = lib.literalExpression "ldddns.packages.\${pkgs.stdenv.hostPlatform.system}.ldddns"; + description = "The ldddns package to use."; + }; + + hostnameLookup = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ + "env:VIRTUAL_HOST" + "containerName" + ]; + description = "Methods for looking up hostnames for containers."; + }; + + ignoreDockerComposeOneoff = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to ignore docker-compose oneoff containers."; + }; + + gops = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to enable the Google gops diagnostics agent."; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.ldddns = { + description = "Local Docker Development DNS"; + documentation = [ "https://ldddns.arnested.dk" ]; + bindsTo = [ + "docker.service" + "avahi-daemon.service" + ]; + after = [ + "docker.service" + "avahi-daemon.service" + ]; + wantedBy = [ "docker.service" ]; + + environment = { + LDDDNS_HOSTNAME_LOOKUP = lib.concatStringsSep "," cfg.hostnameLookup; + LDDDNS_IGNORE_DOCKER_COMPOSE_ONEOFF = + if cfg.ignoreDockerComposeOneoff then "true" else "false"; + LDDDNS_GOPS = if cfg.gops then "true" else "false"; + }; + + serviceConfig = { + Type = "notify"; + ExecStart = "${cfg.package}/bin/ldddns start"; + Restart = "on-failure"; + SupplementaryGroups = [ "docker" ]; + CapabilityBoundingSet = ""; + DevicePolicy = "closed"; + IPAddressDeny = "any"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateNetwork = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = [ "AF_UNIX" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ "@system-service" ]; + UMask = "0777"; + }; + }; + }; + }; + }; +} From 9947db57994a3c0331b485ed071107e4957d7138 Mon Sep 17 00:00:00 2001 From: "Mikkel T. Hoegh" Date: Fri, 5 Jun 2026 11:01:07 +0200 Subject: [PATCH 2/4] Add action for automatic update of Nix flake vendorHash --- .github/workflows/nix-flake-vendor-hash.yml | 84 +++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 .github/workflows/nix-flake-vendor-hash.yml diff --git a/.github/workflows/nix-flake-vendor-hash.yml b/.github/workflows/nix-flake-vendor-hash.yml new file mode 100644 index 0000000..20b1234 --- /dev/null +++ b/.github/workflows/nix-flake-vendor-hash.yml @@ -0,0 +1,84 @@ +--- +name: Update vendorHash in Nix Flake +on: + push: + branches: + - main + paths: + - go.mod + - go.sum + pull_request_target: + paths: + - go.mod + - go.sum + +permissions: + contents: read + +jobs: + vendor-hash: + name: Update vendorHash + runs-on: ubuntu-24.04 + steps: + # Decide whether we trust this run *before* checking out any code. + # pull_request_target runs trusted base-repo workflow code with a + # writable token, so we must gate it: only Dependabot, or an actor with + # write access to the repo, may reach the checkout/build steps. Pushes + # to main are implicitly trusted (you cannot push there without access). + - name: Check authorization + id: auth + env: + GH_TOKEN: ${{ secrets.PAT_TOKEN }} + run: | + if [ "${{ github.event_name }}" = "push" ]; then + echo "authorized=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + actor='${{ github.actor }}' + if [ "$actor" = "dependabot[bot]" ]; then + echo "authorized=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + perm="$(gh api "repos/${{ github.repository }}/collaborators/$actor/permission" --jq '.permission' 2>/dev/null || echo none)" + case "$perm" in + admin | write | maintain) + echo "authorized=true" >> "$GITHUB_OUTPUT" + ;; + *) + echo "::notice::skipping vendorHash update for untrusted actor $actor ($perm)" + echo "authorized=false" >> "$GITHUB_OUTPUT" + ;; + esac + - uses: actions/checkout@v6 + if: steps.auth.outputs.authorized == 'true' + with: + # Push: the branch that was pushed (main). PR: the PR's head branch, + # so the fixup commit lands on the PR. Both are checked out by branch + # name (not detached) so we can commit and push back. + ref: >- + ${{ github.event_name == 'pull_request_target' + && github.event.pull_request.head.ref + || github.ref_name }} + token: ${{ secrets.PAT_TOKEN }} + - uses: DeterminateSystems/nix-installer-action@main + if: steps.auth.outputs.authorized == 'true' + - name: Recompute vendorHash + if: steps.auth.outputs.authorized == 'true' + run: | + # Force a mismatch so Nix prints the correct hash, then write it back. + sed -i 's|vendorHash = "[^"]*"|vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="|' flake.nix + hash="$(nix build .#ldddns 2>&1 | sed -n 's/.*got:[[:space:]]*//p')" + if [ -z "$hash" ]; then + echo "::error::could not determine vendorHash from nix build output" >&2 + exit 1 + fi + sed -i "s|vendorHash = \"sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\"|vendorHash = \"$hash\"|" flake.nix + - name: Commit if changed + if: steps.auth.outputs.authorized == 'true' + run: | + if ! git diff --quiet -- flake.nix; then + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + git commit -am 'Update Nix Flake vendorHash for Go module changes' + git push + fi From 693b2eacceb34b0211e78d4140b9a83aabf4a38d Mon Sep 17 00:00:00 2001 From: "Mikkel T. Hoegh" Date: Wed, 29 Jul 2026 14:46:29 +0200 Subject: [PATCH 3/4] Use FlakeHub nixpkgs-weekly and unpin Go versions in flake The flake no longer built due to version drift. The prePatch used substituteInPlace --replace-fail with the literal "go 1.26.3", but go-version.yml runs `go mod edit -go ` on an hourly cron, so every merged Go bump made that string stop matching and --replace-fail turns a non-match into a hard failure. Strip the patch component with a version agnostic sed instead. Switch nixpkgs to https://flakehub.com/f/DeterminateSystems/nixpkgs-weekly/0.1 and drop the versioned Go attributes: buildGo126Module -> buildGoModule and go_1_26 -> go in the devShell. Both versioned attributes still exist in the current nixpkgs, but they are the same class of pin that breaks once nixpkgs drops an old Go version. vendorHash is unchanged; Go 1.26.2 -> 1.26.4 did not affect vendoring. Co-Authored-By: Claude Opus 5 (1M context) --- flake.lock | 18 ++++++++---------- flake.nix | 14 +++++++++----- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/flake.lock b/flake.lock index c6d2e2b..cc4382e 100644 --- a/flake.lock +++ b/flake.lock @@ -20,18 +20,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1778672786, - "narHash": "sha256-Blg88K1jwG+P0Mr27+rKMFCufdrWkV3wWh9AdYtz0FQ=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "eef00dfd8a712b34af845f9350bac681b1228bd1", - "type": "github" + "lastModified": 1784497964, + "narHash": "sha256-vlHUuqAcbcH2RKmHbPiuQzbv1pnzzavXnI62RD0bqCU=", + "rev": "241313f4e8e508cb9b13278c2b0fa25b9ca27163", + "revCount": 1037713, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nixpkgs-weekly/0.1.1037713%2Brev-241313f4e8e508cb9b13278c2b0fa25b9ca27163/019fa760-2880-7491-9ad9-7ba4669f6a84/source.tar.gz" }, "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" + "type": "tarball", + "url": "https://flakehub.com/f/DeterminateSystems/nixpkgs-weekly/0.1" } }, "root": { diff --git a/flake.nix b/flake.nix index cc03525..b9f706b 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "Local Docker Development DNS"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + nixpkgs.url = "https://flakehub.com/f/DeterminateSystems/nixpkgs-weekly/0.1"; flake-utils.url = "github:numtide/flake-utils"; }; @@ -19,15 +19,19 @@ in { packages = { - ldddns = pkgs.buildGo126Module { + ldddns = pkgs.buildGoModule { pname = "ldddns"; version = self.shortRev or self.dirtyShortRev or "dev"; src = self; - # Relax the go directive to match the Go version available in nixpkgs. + # Drop the patch component from the go directive so the build never + # demands a newer Go toolchain than the one nixpkgs ships. Written + # version agnostically on purpose: the go-version workflow bumps + # go.mod to every new Go patch release, so matching a literal + # version here breaks the build on each bump. prePatch = '' - substituteInPlace go.mod --replace-fail "go 1.26.3" "go 1.26" + sed -i -E 's/^(go [0-9]+\.[0-9]+).*$/\1/' go.mod ''; vendorHash = "sha256-FI7kSIn1QNDcEqDECiGNVLZ5z7LWPxxunK6eKH3D46Y="; @@ -63,7 +67,7 @@ devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ - go_1_26 + go gopls goreleaser ]; From 7e7f5eb0f72f1739da40d2166c8e3c505a8c8183 Mon Sep 17 00:00:00 2001 From: "Mikkel T. Hoegh" Date: Wed, 29 Jul 2026 14:47:36 +0200 Subject: [PATCH 4/4] Update Nix Flake vendorHash for Go module changes Co-Authored-By: Claude Opus 5 (1M context) --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index b9f706b..5d0b480 100644 --- a/flake.nix +++ b/flake.nix @@ -34,7 +34,7 @@ sed -i -E 's/^(go [0-9]+\.[0-9]+).*$/\1/' go.mod ''; - vendorHash = "sha256-FI7kSIn1QNDcEqDECiGNVLZ5z7LWPxxunK6eKH3D46Y="; + vendorHash = "sha256-wPqALHdMyb/Ic5LaJhMJr1ceFvPQfdldRf+m2NVQNYQ="; # Tests require /etc/protocols which is unavailable in the Nix sandbox. doCheck = false;