Compare commits
No commits in common. "9f7b585517f26804182be8125f0e68067b2950a8" and "ad80498b4c11531e35c97e99ec923f4d983d1f7b" have entirely different histories.
9f7b585517
...
ad80498b4c
2 changed files with 120 additions and 53 deletions
120
checker/discover.go
Normal file
120
checker/discover.go
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
package checker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
||||||
|
)
|
||||||
|
|
||||||
|
// directTLSServices enumerates SRV service names (the "service" part of
|
||||||
|
// _service._proto.domain) that by convention mean "direct TLS on connect",
|
||||||
|
// as opposed to STARTTLS or plaintext.
|
||||||
|
//
|
||||||
|
// Matching on the service name is more authoritative than matching on the
|
||||||
|
// port: port 636 could carry anything, but _ldaps._tcp unambiguously
|
||||||
|
// designates LDAP over TLS — even on a non-standard port.
|
||||||
|
var directTLSServices = map[string]bool{
|
||||||
|
"https": true,
|
||||||
|
"ftps": true, // FTPS implicit
|
||||||
|
"smtps": true, // SMTP over TLS (legacy port 465 semantics)
|
||||||
|
"submissions": true, // RFC 8314: SMTP submission over TLS
|
||||||
|
"imaps": true,
|
||||||
|
"pop3s": true,
|
||||||
|
"nntps": true,
|
||||||
|
"ircs": true,
|
||||||
|
"telnets": true,
|
||||||
|
"ldaps": true,
|
||||||
|
"sips": true,
|
||||||
|
"ipps": true, // IPP over TLS (printing)
|
||||||
|
"xmpps-client": true, // XMPP client over direct TLS
|
||||||
|
"xmpps-server": true, // XMPP server-to-server over direct TLS
|
||||||
|
"mqtts": true,
|
||||||
|
"coaps": true,
|
||||||
|
"stuns": true,
|
||||||
|
"turns": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// starttlsSpec describes how to surface a STARTTLS-capable SRV service as
|
||||||
|
// a DiscoveredEndpoint: the endpoint type (which carries the protocol
|
||||||
|
// family in its suffix) and whether the protocol historically treats
|
||||||
|
// STARTTLS as mandatory or opportunistic.
|
||||||
|
type starttlsSpec struct {
|
||||||
|
Type string
|
||||||
|
Opportunistic bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// starttlsServices enumerates SRV service names that speak plaintext on
|
||||||
|
// connect and then negotiate TLS via a protocol-specific STARTTLS handshake.
|
||||||
|
//
|
||||||
|
// The Type follows the "starttls-<proto>" convention agreed with the
|
||||||
|
// future TLS checker (the consumer); the SDK itself has no opinion on
|
||||||
|
// these values. The suffix mirrors the SRV service-name vocabulary so
|
||||||
|
// producers and consumers stay naturally aligned.
|
||||||
|
//
|
||||||
|
// The Opportunistic flag is exposed to consumers as Meta["starttls"] =
|
||||||
|
// "required" | "opportunistic" so rules can pick an appropriate severity
|
||||||
|
// when the server does not advertise STARTTLS.
|
||||||
|
var starttlsServices = map[string]starttlsSpec{
|
||||||
|
"submission": {"starttls-smtp", false}, // RFC 8314: STARTTLS required
|
||||||
|
"smtp": {"starttls-smtp", true}, // port 25: opportunistic
|
||||||
|
"imap": {"starttls-imap", false},
|
||||||
|
"pop3": {"starttls-pop3", false},
|
||||||
|
"xmpp-client": {"starttls-xmpp-client", false}, // RFC 7590
|
||||||
|
"xmpp-server": {"starttls-xmpp-server", true}, // s2s: opportunistic
|
||||||
|
"ldap": {"starttls-ldap", true},
|
||||||
|
"nntp": {"starttls-nntp", true},
|
||||||
|
"ftp": {"starttls-ftp", true},
|
||||||
|
"sieve": {"starttls-sieve", false},
|
||||||
|
"postgresql": {"starttls-postgres", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscoverEndpoints is invoked right after Collect. It declares (host, port)
|
||||||
|
// pairs worth testing by other checkers:
|
||||||
|
//
|
||||||
|
// - direct-TLS endpoints (Type="tls") whose SRV service name is listed in
|
||||||
|
// directTLSServices (e.g. _ldaps, _sips, _https),
|
||||||
|
// - STARTTLS-capable endpoints (Type="starttls-<proto>") whose SRV service
|
||||||
|
// name is listed in starttlsServices (e.g. _submission, _imap, _xmpp-client).
|
||||||
|
//
|
||||||
|
// Unknown service names produce no endpoints: we lean on the SRV naming
|
||||||
|
// convention rather than guessing from the port, since a port alone
|
||||||
|
// conveys no protocol semantics.
|
||||||
|
func (p *srvProvider) DiscoverEndpoints(data any) ([]sdk.DiscoveredEndpoint, error) {
|
||||||
|
d, ok := data.(*SRVData)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected data type %T", data)
|
||||||
|
}
|
||||||
|
var out []sdk.DiscoveredEndpoint
|
||||||
|
for _, r := range d.Records {
|
||||||
|
if r.IsNullTarget || r.Target == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if directTLSServices[r.Service] {
|
||||||
|
out = append(out, sdk.DiscoveredEndpoint{
|
||||||
|
Type: "tls",
|
||||||
|
Host: r.Target,
|
||||||
|
Port: r.Port,
|
||||||
|
SNI: r.Target,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if spec, ok := starttlsServices[r.Service]; ok {
|
||||||
|
policy := "required"
|
||||||
|
if spec.Opportunistic {
|
||||||
|
policy = "opportunistic"
|
||||||
|
}
|
||||||
|
out = append(out, sdk.DiscoveredEndpoint{
|
||||||
|
Type: spec.Type,
|
||||||
|
Host: r.Target,
|
||||||
|
Port: r.Port,
|
||||||
|
SNI: r.Target,
|
||||||
|
Meta: map[string]any{
|
||||||
|
"starttls": policy,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
package checker
|
package checker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
sdk "git.happydns.org/checker-sdk-go/checker"
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -19,54 +17,3 @@ func (p *srvProvider) Key() sdk.ObservationKey {
|
||||||
func (p *srvProvider) Definition() *sdk.CheckerDefinition {
|
func (p *srvProvider) Definition() *sdk.CheckerDefinition {
|
||||||
return Definition()
|
return Definition()
|
||||||
}
|
}
|
||||||
|
|
||||||
// directTLSPorts lists TCP ports where clients speak TLS immediately upon
|
|
||||||
// connection (as opposed to STARTTLS upgrades). A dedicated TLS checker
|
|
||||||
// consumes these endpoints to validate certificates.
|
|
||||||
var directTLSPorts = map[uint16]bool{
|
|
||||||
443: true, // HTTPS
|
|
||||||
465: true, // SMTPS
|
|
||||||
563: true, // NNTPS
|
|
||||||
636: true, // LDAPS
|
|
||||||
853: true, // DoT
|
|
||||||
989: true, // FTPS data
|
|
||||||
990: true, // FTPS control
|
|
||||||
992: true, // Telnet/TLS
|
|
||||||
993: true, // IMAPS
|
|
||||||
995: true, // POP3S
|
|
||||||
5061: true, // SIPS
|
|
||||||
5223: true, // XMPP client TLS
|
|
||||||
5349: true, // STUN/TURN/TLS
|
|
||||||
6697: true, // IRCS
|
|
||||||
8443: true, // HTTPS alt
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiscoverEndpoints is invoked by the host right after Collect. It declares
|
|
||||||
// (host, port) pairs worth testing by other checkers — here: TLS endpoints
|
|
||||||
// whose SRV target points at a well-known direct-TLS port.
|
|
||||||
//
|
|
||||||
// STARTTLS SRVs (e.g. _xmpp-server._tcp on 5269, _sips._tcp notwithstanding)
|
|
||||||
// are intentionally not emitted yet: a dedicated "smtp-starttls" / "xmpp-starttls"
|
|
||||||
// endpoint type will be defined when the TLS checker grows that capability.
|
|
||||||
func (p *srvProvider) DiscoverEndpoints(data any) ([]sdk.DiscoveredEndpoint, error) {
|
|
||||||
d, ok := data.(*SRVData)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unexpected data type %T", data)
|
|
||||||
}
|
|
||||||
var out []sdk.DiscoveredEndpoint
|
|
||||||
for _, r := range d.Records {
|
|
||||||
if r.IsNullTarget || r.Target == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !directTLSPorts[r.Port] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out = append(out, sdk.DiscoveredEndpoint{
|
|
||||||
Type: "tls",
|
|
||||||
Host: r.Target,
|
|
||||||
Port: r.Port,
|
|
||||||
SNI: r.Target,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue