Initial commit
This commit is contained in:
parent
b97f30faf4
commit
c1020c8be7
16 changed files with 118 additions and 209 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,2 +1,2 @@
|
||||||
checker-openpgpkey
|
checker-email-keys
|
||||||
checker-openpgpkey.so
|
checker-email-keys.so
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
2
Makefile
2
Makefile
|
|
@ -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
2
NOTICE
|
|
@ -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).
|
||||||
|
|
|
||||||
43
README.md
43
README.md
|
|
@ -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.
|
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
|
@ -32,9 +26,9 @@ import (
|
||||||
|
|
||||||
// serviceBody is the common envelope for the two services.
|
// serviceBody is the common envelope for the two services.
|
||||||
type serviceBody struct {
|
type serviceBody struct {
|
||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
OpenPGP *dns.OPENPGPKEY `json:"openpgpkey,omitempty"`
|
OpenPGP *dns.OPENPGPKEY `json:"openpgpkey,omitempty"`
|
||||||
SMIMEA *dns.SMIMEA `json:"smimea,omitempty"`
|
SMIMEA *dns.SMIMEA `json:"smimea,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect runs the full DANE-email testsuite and returns an *EmailKeyData
|
// 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)
|
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.",
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
|
@ -18,9 +12,9 @@ var Version = "built-in"
|
||||||
|
|
||||||
// Option ids.
|
// Option ids.
|
||||||
const (
|
const (
|
||||||
OptionResolver = "resolver"
|
OptionResolver = "resolver"
|
||||||
OptionCertExpiryWarnDays = "certExpiryWarnDays"
|
OptionCertExpiryWarnDays = "certExpiryWarnDays"
|
||||||
OptionRequireDNSSEC = "requireDNSSEC"
|
OptionRequireDNSSEC = "requireDNSSEC"
|
||||||
OptionRequireEmailProtection = "requireEmailProtection"
|
OptionRequireEmailProtection = "requireEmailProtection"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
|
@ -47,16 +41,16 @@ type subkeyRow struct {
|
||||||
|
|
||||||
// reportData is the template context.
|
// reportData is the template context.
|
||||||
type reportData struct {
|
type reportData struct {
|
||||||
Kind string
|
Kind string
|
||||||
Headline string
|
Headline string
|
||||||
Badge string // "ok" / "warn" / "fail" / "neutral"
|
Badge string // "ok" / "warn" / "fail" / "neutral"
|
||||||
QueriedOwner string
|
QueriedOwner string
|
||||||
ExpectedOwner string
|
ExpectedOwner string
|
||||||
Resolver string
|
Resolver string
|
||||||
DNSSEC string // "secure" / "insecure" / "unknown"
|
DNSSEC string // "secure" / "insecure" / "unknown"
|
||||||
RecordCount int
|
RecordCount int
|
||||||
Username string
|
Username string
|
||||||
CollectedAt string
|
CollectedAt string
|
||||||
|
|
||||||
OpenPGP *openPGPView
|
OpenPGP *openPGPView
|
||||||
SMIMEA *smimeaView
|
SMIMEA *smimeaView
|
||||||
|
|
@ -69,41 +63,41 @@ type reportData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type openPGPView struct {
|
type openPGPView struct {
|
||||||
Fingerprint string
|
Fingerprint string
|
||||||
KeyID string
|
KeyID string
|
||||||
Algorithm string
|
Algorithm string
|
||||||
Bits int
|
Bits int
|
||||||
UIDs []string
|
UIDs []string
|
||||||
Created string
|
Created string
|
||||||
Expires string
|
Expires string
|
||||||
Revoked bool
|
Revoked bool
|
||||||
Encrypt bool
|
Encrypt bool
|
||||||
Subkeys []subkeyRow
|
Subkeys []subkeyRow
|
||||||
RawSize int
|
RawSize int
|
||||||
EntityCount int
|
EntityCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
type smimeaView struct {
|
type smimeaView struct {
|
||||||
Usage string
|
Usage string
|
||||||
Selector string
|
Selector string
|
||||||
MatchingType string
|
MatchingType string
|
||||||
HashOnly bool
|
HashOnly bool
|
||||||
HashHex string
|
HashHex string
|
||||||
Subject string
|
Subject string
|
||||||
Issuer string
|
Issuer string
|
||||||
Serial string
|
Serial string
|
||||||
NotBefore string
|
NotBefore string
|
||||||
NotAfter string
|
NotAfter string
|
||||||
SignatureAlgo string
|
SignatureAlgo string
|
||||||
KeyAlgo string
|
KeyAlgo string
|
||||||
Bits int
|
Bits int
|
||||||
Emails []string
|
Emails []string
|
||||||
DNSNames []string
|
DNSNames []string
|
||||||
EmailProtection bool
|
EmailProtection bool
|
||||||
DigitalSignature bool
|
DigitalSignature bool
|
||||||
KeyEncipherment bool
|
KeyEncipherment bool
|
||||||
SelfSigned bool
|
SelfSigned bool
|
||||||
IsCA bool
|
IsCA bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHTMLReport implements sdk.CheckerHTMLReporter.
|
// GetHTMLReport implements sdk.CheckerHTMLReporter.
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
|
|
||||||
110
checker/types.go
110
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
|
// 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
|
||||||
|
|
@ -44,42 +38,42 @@ const (
|
||||||
// UI keys remediation templates off them.
|
// UI keys remediation templates off them.
|
||||||
const (
|
const (
|
||||||
// DNS-level.
|
// DNS-level.
|
||||||
CodeDNSQueryFailed = "dns_query_failed"
|
CodeDNSQueryFailed = "dns_query_failed"
|
||||||
CodeDNSNoRecord = "dns_no_record"
|
CodeDNSNoRecord = "dns_no_record"
|
||||||
CodeDNSRecordMismatch = "dns_record_mismatch"
|
CodeDNSRecordMismatch = "dns_record_mismatch"
|
||||||
CodeDNSNotSecure = "dnssec_not_validated"
|
CodeDNSNotSecure = "dnssec_not_validated"
|
||||||
CodeOwnerHashMismatch = "owner_hash_mismatch"
|
CodeOwnerHashMismatch = "owner_hash_mismatch"
|
||||||
|
|
||||||
// OpenPGP.
|
// OpenPGP.
|
||||||
CodePGPParseError = "pgp_parse_error"
|
CodePGPParseError = "pgp_parse_error"
|
||||||
CodePGPNoEntity = "pgp_no_entity"
|
CodePGPNoEntity = "pgp_no_entity"
|
||||||
CodePGPRevoked = "pgp_primary_revoked"
|
CodePGPRevoked = "pgp_primary_revoked"
|
||||||
CodePGPExpired = "pgp_primary_expired"
|
CodePGPExpired = "pgp_primary_expired"
|
||||||
CodePGPExpiringSoon = "pgp_primary_expiring_soon"
|
CodePGPExpiringSoon = "pgp_primary_expiring_soon"
|
||||||
CodePGPWeakAlgorithm = "pgp_weak_algorithm"
|
CodePGPWeakAlgorithm = "pgp_weak_algorithm"
|
||||||
CodePGPWeakKeySize = "pgp_weak_key_size"
|
CodePGPWeakKeySize = "pgp_weak_key_size"
|
||||||
CodePGPNoEncryption = "pgp_no_encryption_subkey"
|
CodePGPNoEncryption = "pgp_no_encryption_subkey"
|
||||||
CodePGPNoIdentity = "pgp_no_identity"
|
CodePGPNoIdentity = "pgp_no_identity"
|
||||||
CodePGPUIDMismatch = "pgp_uid_mismatch"
|
CodePGPUIDMismatch = "pgp_uid_mismatch"
|
||||||
CodePGPMultipleEntities = "pgp_multiple_entities"
|
CodePGPMultipleEntities = "pgp_multiple_entities"
|
||||||
CodePGPRecordTooLarge = "pgp_record_too_large"
|
CodePGPRecordTooLarge = "pgp_record_too_large"
|
||||||
|
|
||||||
// SMIMEA.
|
// SMIMEA.
|
||||||
CodeSMIMEABadUsage = "smimea_bad_usage"
|
CodeSMIMEABadUsage = "smimea_bad_usage"
|
||||||
CodeSMIMEABadSelector = "smimea_bad_selector"
|
CodeSMIMEABadSelector = "smimea_bad_selector"
|
||||||
CodeSMIMEABadMatchType = "smimea_bad_match_type"
|
CodeSMIMEABadMatchType = "smimea_bad_match_type"
|
||||||
CodeSMIMEACertParseError = "smimea_cert_parse_error"
|
CodeSMIMEACertParseError = "smimea_cert_parse_error"
|
||||||
CodeSMIMEACertExpired = "smimea_cert_expired"
|
CodeSMIMEACertExpired = "smimea_cert_expired"
|
||||||
CodeSMIMEACertExpiringSoon = "smimea_cert_expiring_soon"
|
CodeSMIMEACertExpiringSoon = "smimea_cert_expiring_soon"
|
||||||
CodeSMIMEACertNotYetValid = "smimea_cert_not_yet_valid"
|
CodeSMIMEACertNotYetValid = "smimea_cert_not_yet_valid"
|
||||||
CodeSMIMEANoEmailProtection = "smimea_no_email_protection_eku"
|
CodeSMIMEANoEmailProtection = "smimea_no_email_protection_eku"
|
||||||
CodeSMIMEAEmailMismatch = "smimea_email_mismatch"
|
CodeSMIMEAEmailMismatch = "smimea_email_mismatch"
|
||||||
CodeSMIMEAWeakKeySize = "smimea_weak_key_size"
|
CodeSMIMEAWeakKeySize = "smimea_weak_key_size"
|
||||||
CodeSMIMEAWeakSignatureAlg = "smimea_weak_signature_algorithm"
|
CodeSMIMEAWeakSignatureAlg = "smimea_weak_signature_algorithm"
|
||||||
CodeSMIMEANoKeyUsage = "smimea_missing_key_usage"
|
CodeSMIMEANoKeyUsage = "smimea_missing_key_usage"
|
||||||
CodeSMIMEAChainUntrusted = "smimea_chain_untrusted"
|
CodeSMIMEAChainUntrusted = "smimea_chain_untrusted"
|
||||||
CodeSMIMEASelfSigned = "smimea_self_signed"
|
CodeSMIMEASelfSigned = "smimea_self_signed"
|
||||||
CodeSMIMEAHashOnly = "smimea_hash_only"
|
CodeSMIMEAHashOnly = "smimea_hash_only"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Finding describes a single observation produced while running the
|
// Finding describes a single observation produced while running the
|
||||||
|
|
@ -184,14 +178,14 @@ type OpenPGPInfo struct {
|
||||||
|
|
||||||
// SubkeyInfo summarises one OpenPGP subkey.
|
// SubkeyInfo summarises one OpenPGP subkey.
|
||||||
type SubkeyInfo struct {
|
type SubkeyInfo struct {
|
||||||
Algorithm string `json:"algorithm"`
|
Algorithm string `json:"algorithm"`
|
||||||
Bits int `json:"bits,omitempty"`
|
Bits int `json:"bits,omitempty"`
|
||||||
CanSign bool `json:"can_sign,omitempty"`
|
CanSign bool `json:"can_sign,omitempty"`
|
||||||
CanEncrypt bool `json:"can_encrypt,omitempty"`
|
CanEncrypt bool `json:"can_encrypt,omitempty"`
|
||||||
CanAuth bool `json:"can_auth,omitempty"`
|
CanAuth bool `json:"can_auth,omitempty"`
|
||||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||||
ExpiresAt time.Time `json:"expires_at,omitempty"`
|
ExpiresAt time.Time `json:"expires_at,omitempty"`
|
||||||
Revoked bool `json:"revoked,omitempty"`
|
Revoked bool `json:"revoked,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SMIMEAInfo summarises the S/MIME record.
|
// SMIMEAInfo summarises the S/MIME record.
|
||||||
|
|
@ -204,7 +198,7 @@ type SMIMEAInfo struct {
|
||||||
// certificate (selector 0, matching type 0). For selector 1 + type 0
|
// certificate (selector 0, matching type 0). For selector 1 + type 0
|
||||||
// only PublicKey is populated. For matching types 1/2, neither is
|
// only PublicKey is populated. For matching types 1/2, neither is
|
||||||
// populated; only the digest is transported.
|
// populated; only the digest is transported.
|
||||||
Certificate *CertInfo `json:"certificate,omitempty"`
|
Certificate *CertInfo `json:"certificate,omitempty"`
|
||||||
PublicKey *PubKeyInfo `json:"public_key,omitempty"`
|
PublicKey *PubKeyInfo `json:"public_key,omitempty"`
|
||||||
|
|
||||||
// HashHex, when set, is the hex digest embedded in the record.
|
// HashHex, when set, is the hex digest embedded in the record.
|
||||||
|
|
@ -213,21 +207,21 @@ type SMIMEAInfo struct {
|
||||||
|
|
||||||
// CertInfo summarises an X.509 certificate.
|
// CertInfo summarises an X.509 certificate.
|
||||||
type CertInfo struct {
|
type CertInfo struct {
|
||||||
Subject string `json:"subject,omitempty"`
|
Subject string `json:"subject,omitempty"`
|
||||||
Issuer string `json:"issuer,omitempty"`
|
Issuer string `json:"issuer,omitempty"`
|
||||||
SerialHex string `json:"serial_hex,omitempty"`
|
SerialHex string `json:"serial_hex,omitempty"`
|
||||||
NotBefore time.Time `json:"not_before,omitempty"`
|
NotBefore time.Time `json:"not_before,omitempty"`
|
||||||
NotAfter time.Time `json:"not_after,omitempty"`
|
NotAfter time.Time `json:"not_after,omitempty"`
|
||||||
SignatureAlgorithm string `json:"signature_algorithm,omitempty"`
|
SignatureAlgorithm string `json:"signature_algorithm,omitempty"`
|
||||||
PublicKeyAlgorithm string `json:"public_key_algorithm,omitempty"`
|
PublicKeyAlgorithm string `json:"public_key_algorithm,omitempty"`
|
||||||
PublicKeyBits int `json:"public_key_bits,omitempty"`
|
PublicKeyBits int `json:"public_key_bits,omitempty"`
|
||||||
EmailAddresses []string `json:"email_addresses,omitempty"`
|
EmailAddresses []string `json:"email_addresses,omitempty"`
|
||||||
DNSNames []string `json:"dns_names,omitempty"`
|
DNSNames []string `json:"dns_names,omitempty"`
|
||||||
HasEmailProtectionEKU bool `json:"has_email_protection_eku,omitempty"`
|
HasEmailProtectionEKU bool `json:"has_email_protection_eku,omitempty"`
|
||||||
HasDigitalSignature bool `json:"has_digital_signature,omitempty"`
|
HasDigitalSignature bool `json:"has_digital_signature,omitempty"`
|
||||||
HasKeyEncipherment bool `json:"has_key_encipherment,omitempty"`
|
HasKeyEncipherment bool `json:"has_key_encipherment,omitempty"`
|
||||||
IsSelfSigned bool `json:"is_self_signed,omitempty"`
|
IsSelfSigned bool `json:"is_self_signed,omitempty"`
|
||||||
IsCA bool `json:"is_ca,omitempty"`
|
IsCA bool `json:"is_ca,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PubKeyInfo summarises an SPKI-only SMIMEA record.
|
// PubKeyInfo summarises an SPKI-only SMIMEA record.
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -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
10
main.go
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue