From a624a7bfd1ce205185b518f4e037e8c56af9c976 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 29 Apr 2026 17:35:56 +0700 Subject: [PATCH 1/2] checker: join Hdr.Name to apex in preferRRName happyDomain encodes service-embedded record owners relative to the zone apex, so returning the trimmed Hdr.Name as the FQDN owner left relative names like "_465._tcp" leaking into pointer dedup keys and discovery entries. Join with apex unless the name is already suffix-anchored. --- checker/collect.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/checker/collect.go b/checker/collect.go index e8bb96c..ee326e3 100644 --- a/checker/collect.go +++ b/checker/collect.go @@ -159,7 +159,7 @@ func extractPointers(sub, apex string, svc rawService) ([]Pointer, error) { if target == "" { return nil, nil } - ptOwner := preferRRName(b.Record.Hdr.Name, owner) + ptOwner := preferRRName(b.Record.Hdr.Name, owner, apex) return []Pointer{{ Owner: ptOwner, Subdomain: sub, @@ -180,7 +180,7 @@ func extractPointers(sub, apex string, svc rawService) ([]Pointer, error) { continue } out = append(out, Pointer{ - Owner: preferRRName(r.Hdr.Name, owner), + Owner: preferRRName(r.Hdr.Name, owner, apex), Subdomain: sub, Rrtype: "MX", Target: target, @@ -201,7 +201,7 @@ func extractPointers(sub, apex string, svc rawService) ([]Pointer, error) { continue } out = append(out, Pointer{ - Owner: preferRRName(r.Hdr.Name, owner), + Owner: preferRRName(r.Hdr.Name, owner, apex), Subdomain: sub, Rrtype: "SRV", Target: target, @@ -215,7 +215,7 @@ func extractPointers(sub, apex string, svc rawService) ([]Pointer, error) { if err := json.Unmarshal(svc.Service, &b); err != nil { return nil, fmt.Errorf("decode orphan body: %w", err) } - ptOwner := preferRRName(b.Record.Hdr.Name, owner) + ptOwner := preferRRName(b.Record.Hdr.Name, owner, apex) switch b.Record.Hdr.Rrtype { case dns.TypeNS: target := normaliseTarget(b.Record.Ns, ptOwner, apex) @@ -330,13 +330,20 @@ func ownerFQDN(svcDomain, sub, apex string) string { return sub + "." + apex } -// preferRRName returns the RR header Name when present, as it is authoritative over the service-derived owner. -func preferRRName(rrName, fallback string) string { +// preferRRName returns the RR header Name as an FQDN when present. +// happyDomain encodes service-embedded record owners relative to the zone +// apex, so the rrName must be joined with apex unless it already contains +// the apex suffix (services published with absolute owners). +func preferRRName(rrName, fallback, apex string) string { rrName = strings.TrimSuffix(rrName, ".") - if rrName != "" { + if rrName == "" { + return fallback + } + apex = strings.TrimSuffix(apex, ".") + if apex == "" || rrName == apex || strings.HasSuffix(rrName, "."+apex) { return rrName } - return fallback + return rrName + "." + apex } // normaliseTarget converts a target to FQDN form; happyDomain stores in-zone targets relative, external ones absolute. From e85d06d6cded0149aa66fda7b6230ab3a7b62916 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 10 May 2026 19:01:37 +0800 Subject: [PATCH 2/2] Add CI/CD pipeline --- .drone-manifest.yml | 22 ++++++ .drone.yml | 187 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 .drone-manifest.yml create mode 100644 .drone.yml diff --git a/.drone-manifest.yml b/.drone-manifest.yml new file mode 100644 index 0000000..3c50a1c --- /dev/null +++ b/.drone-manifest.yml @@ -0,0 +1,22 @@ +image: happydomain/checker-dangling:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} +{{#if build.tags}} +tags: +{{#each build.tags}} + - {{this}} +{{/each}} +{{/if}} +manifests: + - image: happydomain/checker-dangling:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 + platform: + architecture: amd64 + os: linux + - image: happydomain/checker-dangling:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 + platform: + architecture: arm64 + os: linux + variant: v8 + - image: happydomain/checker-dangling:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm + platform: + architecture: arm + os: linux + variant: v7 diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..e5bf907 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,187 @@ +--- +kind: pipeline +type: docker +name: build-amd64 + +platform: + os: linux + arch: amd64 + +steps: + - name: checker build + image: golang:1-alpine + commands: + - apk add --no-cache git make + - make + environment: + CHECKER_VERSION: "${DRONE_BRANCH}-${DRONE_COMMIT}" + CGO_ENABLED: 0 + when: + event: + exclude: + - tag + + - name: checker build tag + image: golang:1-alpine + commands: + - apk add --no-cache git make + - make + environment: + CHECKER_VERSION: "${DRONE_SEMVER}" + CGO_ENABLED: 0 + when: + event: + - tag + + - name: publish on Docker Hub + image: plugins/docker + settings: + repo: happydomain/checker-dangling + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} + dockerfile: Dockerfile + build_args: + - CHECKER_VERSION=${DRONE_BRANCH}-${DRONE_COMMIT} + username: + from_secret: docker_username + password: + from_secret: docker_password + when: + event: + exclude: + - tag + + - name: publish on Docker Hub (tag) + image: plugins/docker + settings: + repo: happydomain/checker-dangling + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} + dockerfile: Dockerfile + build_args: + - CHECKER_VERSION=${DRONE_SEMVER} + username: + from_secret: docker_username + password: + from_secret: docker_password + when: + event: + - tag + +trigger: + branch: + exclude: + - renovate/* + event: + - cron + - push + - tag + +--- +kind: pipeline +type: docker +name: build-arm64 + +platform: + os: linux + arch: arm64 + +steps: + - name: checker build + image: golang:1-alpine + commands: + - apk add --no-cache git make + - make + environment: + CHECKER_VERSION: "${DRONE_BRANCH}-${DRONE_COMMIT}" + CGO_ENABLED: 0 + when: + event: + exclude: + - tag + + - name: checker build tag + image: golang:1-alpine + commands: + - apk add --no-cache git make + - make + environment: + CHECKER_VERSION: "${DRONE_SEMVER}" + CGO_ENABLED: 0 + when: + event: + - tag + + - name: publish on Docker Hub + image: plugins/docker + settings: + repo: happydomain/checker-dangling + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} + dockerfile: Dockerfile + build_args: + - CHECKER_VERSION=${DRONE_BRANCH}-${DRONE_COMMIT} + username: + from_secret: docker_username + password: + from_secret: docker_password + when: + event: + exclude: + - tag + + - name: publish on Docker Hub (tag) + image: plugins/docker + settings: + repo: happydomain/checker-dangling + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} + dockerfile: Dockerfile + build_args: + - CHECKER_VERSION=${DRONE_SEMVER} + username: + from_secret: docker_username + password: + from_secret: docker_password + when: + event: + - tag + +trigger: + event: + - cron + - push + - tag + +--- +kind: pipeline +name: docker-manifest + +platform: + os: linux + arch: arm64 + +steps: + - name: publish on Docker Hub + image: plugins/manifest + settings: + auto_tag: true + ignore_missing: true + spec: .drone-manifest.yml + username: + from_secret: docker_username + password: + from_secret: docker_password + +trigger: + branch: + exclude: + - renovate/* + event: + - cron + - push + - tag + +depends_on: + - build-amd64 + - build-arm64