Documentation
¶
Index ¶
- type DropNonTLSListenerWrapper
- type SNIWhitelistListenerWrapper
- func (SNIWhitelistListenerWrapper) CaddyModule() caddy.ModuleInfo
- func (h *SNIWhitelistListenerWrapper) Provision(_ caddy.Context) error
- func (h *SNIWhitelistListenerWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error
- func (h *SNIWhitelistListenerWrapper) WrapListener(l net.Listener) net.Listener
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type DropNonTLSListenerWrapper ¶
type DropNonTLSListenerWrapper struct {
// Reset tears down the connection with a TCP reset (RST) instead of a
// graceful close (FIN), so the client learns nothing beyond "the
// connection went away". Only applies to TCP connections.
// Default: true.
Reset *bool `json:"reset,omitempty"`
}
DropNonTLSListenerWrapper closes connections that do not begin with a TLS handshake record carrying a ClientHello, without writing anything to them.
On a port that is meant to speak TLS only, it makes the port stop answering anything else — plaintext HTTP (which would otherwise get Go's `400 Client sent an HTTP request to an HTTPS server.`), the h2c connection preface, legacy SSLv2-style hellos, bare port scans, random bytes — with zero bytes written back, instead of the responses the TLS stack would otherwise emit (a fatal alert for some of those shapes).
The check is on the first six bytes only: the five-byte record header (content type handshake 0x16, record-layer major version 3 — SSL 3.0 through TLS 1.3 all use 0x03 there — and a record length that fits in a TLS record and can hold a handshake header) plus the handshake message type, which must be ClientHello (0x01): the only message a client is allowed to send first. Nothing else is parsed, and a connection that passes is handed to the TLS stack untouched, so a real client cannot be affected. A crafted header that is plausible but followed by a malformed ClientHello body still reaches crypto/tls; this is a cheap "is this a TLS client" filter, not a TLS validator.
Plaintext HTTP is covered as a side effect: a request line always starts with an ASCII method name, so it can never begin with 0x16.
This listener wrapper must be placed BEFORE the "tls" listener wrapper, for it to work properly.
func (DropNonTLSListenerWrapper) CaddyModule ¶
func (DropNonTLSListenerWrapper) CaddyModule() caddy.ModuleInfo
func (*DropNonTLSListenerWrapper) UnmarshalCaddyfile ¶
func (h *DropNonTLSListenerWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error
UnmarshalCaddyfile sets up the wrapper from Caddyfile tokens. Syntax:
drop_non_tls [reset|fin]
The optional argument selects how a rejected connection is torn down; it defaults to reset.
func (*DropNonTLSListenerWrapper) WrapListener ¶
func (h *DropNonTLSListenerWrapper) WrapListener(l net.Listener) net.Listener
type SNIWhitelistListenerWrapper ¶
type SNIWhitelistListenerWrapper struct {
// AllowedSNIs is the list of server names to accept. Entries may use a
// left-most label wildcard, e.g. "*.example.com" matches
// "a.example.com" but not "a.b.example.com". "*" accepts any name.
// Comparison is case-insensitive and a trailing dot is ignored.
// Required.
AllowedSNIs []string `json:"allowed_snis,omitempty"`
// Reset tears down the connection with a TCP reset (RST) instead of a
// graceful close (FIN), so the client learns nothing beyond "the
// connection went away". Only applies to TCP connections.
// Default: true.
Reset *bool `json:"reset,omitempty"`
// contains filtered or unexported fields
}
SNIWhitelistListenerWrapper closes connections whose TLS ClientHello carries a server name (SNI) that is not in an allowlist, without writing anything to them.
Rejecting a connection later, at certificate selection time, makes the TLS stack send a fatal alert ("internal error") before closing; that is a well-formed TLS response and it tells the client (and any scanner) that a TLS server is present. This wrapper instead makes the decision from the raw ClientHello, before the TLS stack sees it, so not a single byte is sent back. Rejected connections are torn down with a TCP RST rather than a graceful FIN unless the connection mode is set to "fin".
Connections whose ClientHello cannot be inspected (not a TLS handshake record, larger than the inspection limit, or malformed) are passed through unchanged so that the TLS stack remains the backstop and no legitimate client is filtered out by accident. Likewise, a ClientHello that does not carry an SNI at all does not match any allowlist entry and is therefore rejected.
This listener wrapper must be placed BEFORE the "tls" listener wrapper, for it to work properly.
func (SNIWhitelistListenerWrapper) CaddyModule ¶
func (SNIWhitelistListenerWrapper) CaddyModule() caddy.ModuleInfo
func (*SNIWhitelistListenerWrapper) Provision ¶
func (h *SNIWhitelistListenerWrapper) Provision(_ caddy.Context) error
Provision normalizes the allowlist and verifies that it is not empty.
func (*SNIWhitelistListenerWrapper) UnmarshalCaddyfile ¶
func (h *SNIWhitelistListenerWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error
UnmarshalCaddyfile sets up the wrapper from Caddyfile tokens. Syntax:
sni_whitelist [reset|fin] {
<server_name>
<server_name>
...
}
The optional argument selects how rejected connections are torn down; it defaults to reset. Every name in the block is one allowed server name, one per line.
func (*SNIWhitelistListenerWrapper) WrapListener ¶
func (h *SNIWhitelistListenerWrapper) WrapListener(l net.Listener) net.Listener