diff --git a/.gitignore b/.gitignore index d297e8c..41eb9b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -checker-openpgpkey -checker-openpgpkey.so +checker-email-keys +checker-email-keys.so diff --git a/Dockerfile b/Dockerfile index b346539..112385c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,9 +6,9 @@ WORKDIR /src COPY go.mod go.sum ./ RUN go mod download 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 -COPY --from=builder /checker-openpgpkey /checker-openpgpkey +COPY --from=builder /checker-email-keys /checker-email-keys EXPOSE 8080 -ENTRYPOINT ["/checker-openpgpkey"] +ENTRYPOINT ["/checker-email-keys"] diff --git a/Makefile b/Makefile index ddf32ee..b1b1b12 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CHECKER_NAME := checker-openpgpkey +CHECKER_NAME := checker-email-keys CHECKER_IMAGE := happydomain/$(CHECKER_NAME) CHECKER_VERSION ?= custom-build diff --git a/NOTICE b/NOTICE index c463df1..935eb2b 100644 --- a/NOTICE +++ b/NOTICE @@ -1,4 +1,4 @@ -checker-openpgpkey +checker-email-keys Copyright (c) 2026 The happyDomain Authors This product is licensed under the MIT License (see LICENSE). diff --git a/README.md b/README.md index 1efd879..e4d37b6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# checker-openpgpkey +# checker-email-keys DANE-Email posture checker for happyDomain. @@ -107,7 +107,7 @@ Auto-filled by the host: `domain_name`, `subdomain`, `service`, make plugin # Standalone HTTP server -make && ./checker-openpgpkey -listen :8080 +make && ./checker-email-keys -listen :8080 ``` ## HTML report diff --git a/checker/collect.go b/checker/collect.go index ff049c3..2d87b00 100644 --- a/checker/collect.go +++ b/checker/collect.go @@ -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 import ( @@ -32,9 +26,9 @@ import ( // serviceBody is the common envelope for the two services. type serviceBody struct { - Username string `json:"username,omitempty"` - OpenPGP *dns.OPENPGPKEY `json:"openpgpkey,omitempty"` - SMIMEA *dns.SMIMEA `json:"smimea,omitempty"` + Username string `json:"username,omitempty"` + OpenPGP *dns.OPENPGPKEY `json:"openpgpkey,omitempty"` + SMIMEA *dns.SMIMEA `json:"smimea,omitempty"` } // Collect runs the full DANE-email testsuite and returns an *EmailKeyData @@ -123,13 +117,9 @@ func (p *emailKeyProvider) Collect(ctx context.Context, opts sdk.CheckerOptions) data.RecordCount = len(ans.Records) if ans.Rcode == dns.RcodeNameError || len(ans.Records) == 0 { - sev := SeverityCrit - if ans.Rcode == dns.RcodeNameError { - sev = SeverityCrit - } data.Findings = append(data.Findings, Finding{ Code: CodeDNSNoRecord, - Severity: sev, + Severity: SeverityCrit, 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.", }) diff --git a/checker/definition.go b/checker/definition.go index 0eea551..12325c4 100644 --- a/checker/definition.go +++ b/checker/definition.go @@ -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 import ( @@ -18,9 +12,9 @@ var Version = "built-in" // Option ids. const ( - OptionResolver = "resolver" - OptionCertExpiryWarnDays = "certExpiryWarnDays" - OptionRequireDNSSEC = "requireDNSSEC" + OptionResolver = "resolver" + OptionCertExpiryWarnDays = "certExpiryWarnDays" + OptionRequireDNSSEC = "requireDNSSEC" OptionRequireEmailProtection = "requireEmailProtection" ) diff --git a/checker/dns.go b/checker/dns.go index 473cfda..c9ae0b1 100644 --- a/checker/dns.go +++ b/checker/dns.go @@ -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 import ( diff --git a/checker/interactive.go b/checker/interactive.go index 6e91137..bb6b12d 100644 --- a/checker/interactive.go +++ b/checker/interactive.go @@ -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 import ( diff --git a/checker/provider.go b/checker/provider.go index 44a0940..94bcd1a 100644 --- a/checker/provider.go +++ b/checker/provider.go @@ -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 import ( diff --git a/checker/report.go b/checker/report.go index fd69c54..71e9c95 100644 --- a/checker/report.go +++ b/checker/report.go @@ -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 import ( @@ -47,16 +41,16 @@ type subkeyRow struct { // reportData is the template context. type reportData struct { - Kind string - Headline string - Badge string // "ok" / "warn" / "fail" / "neutral" - QueriedOwner string + Kind string + Headline string + Badge string // "ok" / "warn" / "fail" / "neutral" + QueriedOwner string ExpectedOwner string - Resolver string - DNSSEC string // "secure" / "insecure" / "unknown" - RecordCount int - Username string - CollectedAt string + Resolver string + DNSSEC string // "secure" / "insecure" / "unknown" + RecordCount int + Username string + CollectedAt string OpenPGP *openPGPView SMIMEA *smimeaView @@ -69,41 +63,41 @@ type reportData struct { } type openPGPView struct { - Fingerprint string - KeyID string - Algorithm string - Bits int - UIDs []string - Created string - Expires string - Revoked bool - Encrypt bool - Subkeys []subkeyRow - RawSize int - EntityCount int + Fingerprint string + KeyID string + Algorithm string + Bits int + UIDs []string + Created string + Expires string + Revoked bool + Encrypt bool + Subkeys []subkeyRow + RawSize int + EntityCount int } type smimeaView struct { - Usage string - Selector string - MatchingType string - HashOnly bool - HashHex string - Subject string - Issuer string - Serial string - NotBefore string - NotAfter string - SignatureAlgo string - KeyAlgo string - Bits int - Emails []string - DNSNames []string - EmailProtection bool + Usage string + Selector string + MatchingType string + HashOnly bool + HashHex string + Subject string + Issuer string + Serial string + NotBefore string + NotAfter string + SignatureAlgo string + KeyAlgo string + Bits int + Emails []string + DNSNames []string + EmailProtection bool DigitalSignature bool - KeyEncipherment bool - SelfSigned bool - IsCA bool + KeyEncipherment bool + SelfSigned bool + IsCA bool } // GetHTMLReport implements sdk.CheckerHTMLReporter. diff --git a/checker/rule.go b/checker/rule.go index 55d910a..c0a73d1 100644 --- a/checker/rule.go +++ b/checker/rule.go @@ -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 import ( diff --git a/checker/types.go b/checker/types.go index 8c30e55..ca432eb 100644 --- a/checker/types.go +++ b/checker/types.go @@ -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 // happyDomain. It runs a comprehensive testsuite on the DNS-published // OpenPGP key (RFC 7929) or S/MIME certificate (RFC 8162) corresponding @@ -44,42 +38,42 @@ const ( // UI keys remediation templates off them. const ( // DNS-level. - CodeDNSQueryFailed = "dns_query_failed" - CodeDNSNoRecord = "dns_no_record" + CodeDNSQueryFailed = "dns_query_failed" + CodeDNSNoRecord = "dns_no_record" CodeDNSRecordMismatch = "dns_record_mismatch" - CodeDNSNotSecure = "dnssec_not_validated" + CodeDNSNotSecure = "dnssec_not_validated" CodeOwnerHashMismatch = "owner_hash_mismatch" // OpenPGP. - CodePGPParseError = "pgp_parse_error" - CodePGPNoEntity = "pgp_no_entity" - CodePGPRevoked = "pgp_primary_revoked" - CodePGPExpired = "pgp_primary_expired" - CodePGPExpiringSoon = "pgp_primary_expiring_soon" - CodePGPWeakAlgorithm = "pgp_weak_algorithm" - CodePGPWeakKeySize = "pgp_weak_key_size" - CodePGPNoEncryption = "pgp_no_encryption_subkey" - CodePGPNoIdentity = "pgp_no_identity" - CodePGPUIDMismatch = "pgp_uid_mismatch" + CodePGPParseError = "pgp_parse_error" + CodePGPNoEntity = "pgp_no_entity" + CodePGPRevoked = "pgp_primary_revoked" + CodePGPExpired = "pgp_primary_expired" + CodePGPExpiringSoon = "pgp_primary_expiring_soon" + CodePGPWeakAlgorithm = "pgp_weak_algorithm" + CodePGPWeakKeySize = "pgp_weak_key_size" + CodePGPNoEncryption = "pgp_no_encryption_subkey" + CodePGPNoIdentity = "pgp_no_identity" + CodePGPUIDMismatch = "pgp_uid_mismatch" CodePGPMultipleEntities = "pgp_multiple_entities" - CodePGPRecordTooLarge = "pgp_record_too_large" + CodePGPRecordTooLarge = "pgp_record_too_large" // SMIMEA. - CodeSMIMEABadUsage = "smimea_bad_usage" - CodeSMIMEABadSelector = "smimea_bad_selector" - CodeSMIMEABadMatchType = "smimea_bad_match_type" - CodeSMIMEACertParseError = "smimea_cert_parse_error" - CodeSMIMEACertExpired = "smimea_cert_expired" - CodeSMIMEACertExpiringSoon = "smimea_cert_expiring_soon" - CodeSMIMEACertNotYetValid = "smimea_cert_not_yet_valid" + CodeSMIMEABadUsage = "smimea_bad_usage" + CodeSMIMEABadSelector = "smimea_bad_selector" + CodeSMIMEABadMatchType = "smimea_bad_match_type" + CodeSMIMEACertParseError = "smimea_cert_parse_error" + CodeSMIMEACertExpired = "smimea_cert_expired" + CodeSMIMEACertExpiringSoon = "smimea_cert_expiring_soon" + CodeSMIMEACertNotYetValid = "smimea_cert_not_yet_valid" CodeSMIMEANoEmailProtection = "smimea_no_email_protection_eku" - CodeSMIMEAEmailMismatch = "smimea_email_mismatch" - CodeSMIMEAWeakKeySize = "smimea_weak_key_size" - CodeSMIMEAWeakSignatureAlg = "smimea_weak_signature_algorithm" - CodeSMIMEANoKeyUsage = "smimea_missing_key_usage" - CodeSMIMEAChainUntrusted = "smimea_chain_untrusted" - CodeSMIMEASelfSigned = "smimea_self_signed" - CodeSMIMEAHashOnly = "smimea_hash_only" + CodeSMIMEAEmailMismatch = "smimea_email_mismatch" + CodeSMIMEAWeakKeySize = "smimea_weak_key_size" + CodeSMIMEAWeakSignatureAlg = "smimea_weak_signature_algorithm" + CodeSMIMEANoKeyUsage = "smimea_missing_key_usage" + CodeSMIMEAChainUntrusted = "smimea_chain_untrusted" + CodeSMIMEASelfSigned = "smimea_self_signed" + CodeSMIMEAHashOnly = "smimea_hash_only" ) // Finding describes a single observation produced while running the @@ -184,14 +178,14 @@ type OpenPGPInfo struct { // SubkeyInfo summarises one OpenPGP subkey. type SubkeyInfo struct { - Algorithm string `json:"algorithm"` - Bits int `json:"bits,omitempty"` - CanSign bool `json:"can_sign,omitempty"` - CanEncrypt bool `json:"can_encrypt,omitempty"` - CanAuth bool `json:"can_auth,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty"` - ExpiresAt time.Time `json:"expires_at,omitempty"` - Revoked bool `json:"revoked,omitempty"` + Algorithm string `json:"algorithm"` + Bits int `json:"bits,omitempty"` + CanSign bool `json:"can_sign,omitempty"` + CanEncrypt bool `json:"can_encrypt,omitempty"` + CanAuth bool `json:"can_auth,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + ExpiresAt time.Time `json:"expires_at,omitempty"` + Revoked bool `json:"revoked,omitempty"` } // SMIMEAInfo summarises the S/MIME record. @@ -204,7 +198,7 @@ type SMIMEAInfo struct { // certificate (selector 0, matching type 0). For selector 1 + type 0 // only PublicKey is populated. For matching types 1/2, neither is // populated; only the digest is transported. - Certificate *CertInfo `json:"certificate,omitempty"` + Certificate *CertInfo `json:"certificate,omitempty"` PublicKey *PubKeyInfo `json:"public_key,omitempty"` // HashHex, when set, is the hex digest embedded in the record. @@ -213,21 +207,21 @@ type SMIMEAInfo struct { // CertInfo summarises an X.509 certificate. type CertInfo struct { - Subject string `json:"subject,omitempty"` - Issuer string `json:"issuer,omitempty"` - SerialHex string `json:"serial_hex,omitempty"` - NotBefore time.Time `json:"not_before,omitempty"` - NotAfter time.Time `json:"not_after,omitempty"` - SignatureAlgorithm string `json:"signature_algorithm,omitempty"` - PublicKeyAlgorithm string `json:"public_key_algorithm,omitempty"` - PublicKeyBits int `json:"public_key_bits,omitempty"` - EmailAddresses []string `json:"email_addresses,omitempty"` - DNSNames []string `json:"dns_names,omitempty"` - HasEmailProtectionEKU bool `json:"has_email_protection_eku,omitempty"` - HasDigitalSignature bool `json:"has_digital_signature,omitempty"` - HasKeyEncipherment bool `json:"has_key_encipherment,omitempty"` - IsSelfSigned bool `json:"is_self_signed,omitempty"` - IsCA bool `json:"is_ca,omitempty"` + Subject string `json:"subject,omitempty"` + Issuer string `json:"issuer,omitempty"` + SerialHex string `json:"serial_hex,omitempty"` + NotBefore time.Time `json:"not_before,omitempty"` + NotAfter time.Time `json:"not_after,omitempty"` + SignatureAlgorithm string `json:"signature_algorithm,omitempty"` + PublicKeyAlgorithm string `json:"public_key_algorithm,omitempty"` + PublicKeyBits int `json:"public_key_bits,omitempty"` + EmailAddresses []string `json:"email_addresses,omitempty"` + DNSNames []string `json:"dns_names,omitempty"` + HasEmailProtectionEKU bool `json:"has_email_protection_eku,omitempty"` + HasDigitalSignature bool `json:"has_digital_signature,omitempty"` + HasKeyEncipherment bool `json:"has_key_encipherment,omitempty"` + IsSelfSigned bool `json:"is_self_signed,omitempty"` + IsCA bool `json:"is_ca,omitempty"` } // PubKeyInfo summarises an SPKI-only SMIMEA record. diff --git a/go.mod b/go.mod index e5a7ed6..541266a 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module git.happydns.org/checker-openpgpkey +module git.happydns.org/checker-email-keys go 1.25.0 diff --git a/main.go b/main.go index 0f78fa7..d7e1801 100644 --- a/main.go +++ b/main.go @@ -4,23 +4,23 @@ import ( "flag" "log" - openpgpkey "git.happydns.org/checker-openpgpkey/checker" + emailkeys "git.happydns.org/checker-email-keys/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: // // go build -ldflags "-X main.Version=1.2.3" . var Version = "custom-build" +var listenAddr = flag.String("listen", ":8080", "HTTP listen address") + func main() { 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 { log.Fatalf("server error: %v", err) } diff --git a/plugin/plugin.go b/plugin/plugin.go index 5feb7bd..34d62ae 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -4,18 +4,18 @@ package main import ( - openpgpkey "git.happydns.org/checker-openpgpkey/checker" + emailkeys "git.happydns.org/checker-email-keys/checker" sdk "git.happydns.org/checker-sdk-go/checker" ) // 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" // NewCheckerPlugin is the symbol resolved by happyDomain when loading // the .so file. func NewCheckerPlugin() (*sdk.CheckerDefinition, sdk.ObservationProvider, error) { - openpgpkey.Version = Version - return openpgpkey.Definition(), openpgpkey.Provider(), nil + emailkeys.Version = Version + return emailkeys.Definition(), emailkeys.Provider(), nil }