Initial commit

This commit is contained in:
nemunaire 2026-04-23 18:30:01 +07:00
commit c1020c8be7
16 changed files with 118 additions and 209 deletions

4
.gitignore vendored
View file

@ -1,2 +1,2 @@
checker-openpgpkey checker-email-keys
checker-openpgpkey.so checker-email-keys.so

View file

@ -6,9 +6,9 @@ WORKDIR /src
COPY go.mod go.sum ./ COPY go.mod go.sum ./
RUN go mod download RUN go mod download
COPY . . COPY . .
RUN CGO_ENABLED=0 go build -ldflags "-X main.Version=${CHECKER_VERSION}" -o /checker-openpgpkey . RUN CGO_ENABLED=0 go build -ldflags "-X main.Version=${CHECKER_VERSION}" -o /checker-email-keys .
FROM scratch FROM scratch
COPY --from=builder /checker-openpgpkey /checker-openpgpkey COPY --from=builder /checker-email-keys /checker-email-keys
EXPOSE 8080 EXPOSE 8080
ENTRYPOINT ["/checker-openpgpkey"] ENTRYPOINT ["/checker-email-keys"]

View file

@ -1,4 +1,4 @@
CHECKER_NAME := checker-openpgpkey CHECKER_NAME := checker-email-keys
CHECKER_IMAGE := happydomain/$(CHECKER_NAME) CHECKER_IMAGE := happydomain/$(CHECKER_NAME)
CHECKER_VERSION ?= custom-build CHECKER_VERSION ?= custom-build

2
NOTICE
View file

@ -1,4 +1,4 @@
checker-openpgpkey checker-email-keys
Copyright (c) 2026 The happyDomain Authors Copyright (c) 2026 The happyDomain Authors
This product is licensed under the MIT License (see LICENSE). This product is licensed under the MIT License (see LICENSE).

View file

@ -1,4 +1,4 @@
# checker-openpgpkey # checker-email-keys
DANE-Email posture checker for happyDomain. DANE-Email posture checker for happyDomain.
@ -66,28 +66,6 @@ rule engine can fold them into a single `CheckState`.
| `smimea_self_signed` | info | Self-signed certificate paired with PKIX-EE usage. | | `smimea_self_signed` | info | Self-signed certificate paired with PKIX-EE usage. |
| `smimea_hash_only` | info | Matching-type 1/2 only carries a digest; certificate can't be inspected. | | `smimea_hash_only` | info | Matching-type 1/2 only carries a digest; certificate can't be inspected. |
## Why a bespoke checker instead of a third-party testsuite?
There is no canonical "OPENPGPKEY / SMIMEA testsuite" in Go or as a
self-hostable online service:
- `ldns-dane` (NLnet Labs) validates DANE-TLSA and handles SMIMEA only
shallowly (it parses the record without deep certificate checks).
- `hokey` (Paul Wouters) queries OPENPGPKEY but does not validate the
key material.
- Online DANE validators (e.g. `dane.sys4.de`, `has-tls-rpt.com`) focus
on SMTP DANE-TLSA, not email-identity records.
The heavy lifting here is standard Go parsing:
- `github.com/ProtonMail/go-crypto/openpgp` (maintained fork of the
deprecated `golang.org/x/crypto/openpgp`) for OpenPGP packet parsing,
UIDs, subkeys, revocations, key-lifetime self-signatures.
- `crypto/x509` for SMIMEA certificate parsing, validity window, EKU,
key-usage, signature-algorithm and key-size checks.
- `github.com/miekg/dns` for the DNS+EDNS0+DO query and the `AD` flag
read-back used as the DNSSEC-validation signal.
## Options ## Options
| Id | Type | Default | Description | | Id | Type | Default | Description |
@ -107,22 +85,5 @@ Auto-filled by the host: `domain_name`, `subdomain`, `service`,
make plugin make plugin
# Standalone HTTP server # Standalone HTTP server
make && ./checker-openpgpkey -listen :8080 make && ./checker-email-keys -listen :8080
``` ```
## HTML report
The report renders as a self-contained HTML document intended for
embedding in an `<iframe>` (the same contract as the other happyDomain
checkers). It is organised as:
1. **Header**: status badge, queried owner name, resolver used, DNSSEC
flag.
2. **Most common issues (fix these first)**: remediation cards shown
*only* when a matching finding was emitted. Each card carries the
concrete shell commands / zone-file snippets the user needs.
3. **OpenPGP key / SMIMEA record**: structured details for the
parsed material (fingerprint, UIDs, subkeys, cert subject/issuer,
EKU/KU flags, …).
4. **Findings**: the full table of per-finding code / severity /
message / fix hints, sorted by severity.

View file

@ -1,9 +1,3 @@
// This file is part of the happyDomain (R) project.
// Copyright (c) 2026 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// Licensed under the MIT License (see LICENSE).
package checker package checker
import ( import (
@ -123,13 +117,9 @@ func (p *emailKeyProvider) Collect(ctx context.Context, opts sdk.CheckerOptions)
data.RecordCount = len(ans.Records) data.RecordCount = len(ans.Records)
if ans.Rcode == dns.RcodeNameError || len(ans.Records) == 0 { if ans.Rcode == dns.RcodeNameError || len(ans.Records) == 0 {
sev := SeverityCrit
if ans.Rcode == dns.RcodeNameError {
sev = SeverityCrit
}
data.Findings = append(data.Findings, Finding{ data.Findings = append(data.Findings, Finding{
Code: CodeDNSNoRecord, Code: CodeDNSNoRecord,
Severity: sev, Severity: SeverityCrit,
Message: fmt.Sprintf("Authoritative DNS returned no %s record at %s.", dns.TypeToString[qtype], data.QueriedOwner), Message: fmt.Sprintf("Authoritative DNS returned no %s record at %s.", dns.TypeToString[qtype], data.QueriedOwner),
Fix: "Ensure the record is present in the zone and that the zone has been loaded by the authoritative servers.", Fix: "Ensure the record is present in the zone and that the zone has been loaded by the authoritative servers.",
}) })

View file

@ -1,9 +1,3 @@
// This file is part of the happyDomain (R) project.
// Copyright (c) 2026 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// Licensed under the MIT License (see LICENSE).
package checker package checker
import ( import (

View file

@ -1,9 +1,3 @@
// This file is part of the happyDomain (R) project.
// Copyright (c) 2026 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// Licensed under the MIT License (see LICENSE).
package checker package checker
import ( import (

View file

@ -1,9 +1,3 @@
// This file is part of the happyDomain (R) project.
// Copyright (c) 2026 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// Licensed under the MIT License (see LICENSE).
package checker package checker
import ( import (

View file

@ -1,9 +1,3 @@
// This file is part of the happyDomain (R) project.
// Copyright (c) 2026 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// Licensed under the MIT License (see LICENSE).
package checker package checker
import ( import (

View file

@ -1,9 +1,3 @@
// This file is part of the happyDomain (R) project.
// Copyright (c) 2026 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// Licensed under the MIT License (see LICENSE).
package checker package checker
import ( import (

View file

@ -1,9 +1,3 @@
// This file is part of the happyDomain (R) project.
// Copyright (c) 2026 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// Licensed under the MIT License (see LICENSE).
package checker package checker
import ( import (

View file

@ -1,9 +1,3 @@
// This file is part of the happyDomain (R) project.
// Copyright (c) 2026 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// Licensed under the MIT License (see LICENSE).
// Package checker implements the OPENPGPKEY/SMIMEA DANE checker for // Package checker implements the OPENPGPKEY/SMIMEA DANE checker for
// happyDomain. It runs a comprehensive testsuite on the DNS-published // happyDomain. It runs a comprehensive testsuite on the DNS-published
// OpenPGP key (RFC 7929) or S/MIME certificate (RFC 8162) corresponding // OpenPGP key (RFC 7929) or S/MIME certificate (RFC 8162) corresponding

2
go.mod
View file

@ -1,4 +1,4 @@
module git.happydns.org/checker-openpgpkey module git.happydns.org/checker-email-keys
go 1.25.0 go 1.25.0

10
main.go
View file

@ -4,23 +4,23 @@ import (
"flag" "flag"
"log" "log"
openpgpkey "git.happydns.org/checker-openpgpkey/checker" emailkeys "git.happydns.org/checker-email-keys/checker"
sdk "git.happydns.org/checker-sdk-go/checker" sdk "git.happydns.org/checker-sdk-go/checker"
) )
var listenAddr = flag.String("listen", ":8080", "HTTP listen address")
// Version is the standalone binary's version. Override with: // Version is the standalone binary's version. Override with:
// //
// go build -ldflags "-X main.Version=1.2.3" . // go build -ldflags "-X main.Version=1.2.3" .
var Version = "custom-build" var Version = "custom-build"
var listenAddr = flag.String("listen", ":8080", "HTTP listen address")
func main() { func main() {
flag.Parse() flag.Parse()
openpgpkey.Version = Version emailkeys.Version = Version
server := sdk.NewServer(openpgpkey.Provider()) server := sdk.NewServer(emailkeys.Provider())
if err := server.ListenAndServe(*listenAddr); err != nil { if err := server.ListenAndServe(*listenAddr); err != nil {
log.Fatalf("server error: %v", err) log.Fatalf("server error: %v", err)
} }

View file

@ -4,18 +4,18 @@
package main package main
import ( import (
openpgpkey "git.happydns.org/checker-openpgpkey/checker" emailkeys "git.happydns.org/checker-email-keys/checker"
sdk "git.happydns.org/checker-sdk-go/checker" sdk "git.happydns.org/checker-sdk-go/checker"
) )
// Version is the plugin's version, meant to be overridden by CI: // Version is the plugin's version, meant to be overridden by CI:
// //
// go build -buildmode=plugin -ldflags "-X main.Version=1.2.3" -o checker-openpgpkey.so ./plugin // go build -buildmode=plugin -ldflags "-X main.Version=1.2.3" -o checker-email-keys.so ./plugin
var Version = "custom-build" var Version = "custom-build"
// NewCheckerPlugin is the symbol resolved by happyDomain when loading // NewCheckerPlugin is the symbol resolved by happyDomain when loading
// the .so file. // the .so file.
func NewCheckerPlugin() (*sdk.CheckerDefinition, sdk.ObservationProvider, error) { func NewCheckerPlugin() (*sdk.CheckerDefinition, sdk.ObservationProvider, error) {
openpgpkey.Version = Version emailkeys.Version = Version
return openpgpkey.Definition(), openpgpkey.Provider(), nil return emailkeys.Definition(), emailkeys.Provider(), nil
} }