Add STARTTLS hosts during discover

This commit is contained in:
nemunaire 2026-04-19 13:30:22 +07:00
commit ad80498b4c

View file

@ -12,13 +12,7 @@ import (
// //
// Matching on the service name is more authoritative than matching on the // Matching on the service name is more authoritative than matching on the
// port: port 636 could carry anything, but _ldaps._tcp unambiguously // port: port 636 could carry anything, but _ldaps._tcp unambiguously
// designates LDAP over TLS — even on a non-standard port. Conversely, a // designates LDAP over TLS — even on a non-standard port.
// site may run HTTPS on a non-443 port and still want it probed.
//
// STARTTLS variants (_xmpp-client, _smtp, _submission, _imap, _pop3…) are
// intentionally excluded here; a dedicated endpoint type (e.g.
// "smtp-starttls") will be introduced when a TLS checker grows the
// capability to upgrade those protocols.
var directTLSServices = map[string]bool{ var directTLSServices = map[string]bool{
"https": true, "https": true,
"ftps": true, // FTPS implicit "ftps": true, // FTPS implicit
@ -40,9 +34,51 @@ var directTLSServices = map[string]bool{
"turns": 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) // DiscoverEndpoints is invoked right after Collect. It declares (host, port)
// pairs worth testing by other checkers — here: TLS endpoints whose SRV // pairs worth testing by other checkers:
// service name is a known direct-TLS protocol (see directTLSServices). //
// - 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) { func (p *srvProvider) DiscoverEndpoints(data any) ([]sdk.DiscoveredEndpoint, error) {
d, ok := data.(*SRVData) d, ok := data.(*SRVData)
if !ok { if !ok {
@ -53,15 +89,32 @@ func (p *srvProvider) DiscoverEndpoints(data any) ([]sdk.DiscoveredEndpoint, err
if r.IsNullTarget || r.Target == "" { if r.IsNullTarget || r.Target == "" {
continue continue
} }
if !directTLSServices[r.Service] {
if directTLSServices[r.Service] {
out = append(out, sdk.DiscoveredEndpoint{
Type: "tls",
Host: r.Target,
Port: r.Port,
SNI: r.Target,
})
continue continue
} }
out = append(out, sdk.DiscoveredEndpoint{
Type: "tls", if spec, ok := starttlsServices[r.Service]; ok {
Host: r.Target, policy := "required"
Port: r.Port, if spec.Opportunistic {
SNI: r.Target, 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 return out, nil
} }