Deploying Publicly Trusted TLS Servers on Devices Behind NAT Using SNI-based End-to-End TLS Forwarding (SNIF)
draft-zubov-snif-05
This document is an Internet-Draft (I-D).
Anyone may submit an I-D to the IETF.
This I-D is not endorsed by the IETF and has no formal standing in the
IETF standards process.
| Document | Type | Active Internet-Draft (individual) | |
|---|---|---|---|
| Author | Jim Zubov | ||
| Last updated | 2026-06-29 | ||
| RFC stream | (None) | ||
| Intended RFC status | (None) | ||
| Formats | |||
| Additional resources |
Additional Web Page
GitHub Repository |
||
| Stream | Stream state | (No stream defined) | |
| Consensus boilerplate | Unknown | ||
| RFC Editor Note | (None) | ||
| IESG | IESG state | I-D Exists | |
| Telechat date | (None) | ||
| Responsible AD | (None) | ||
| Send notices to | (None) |
draft-zubov-snif-05
Network Working Group J. Zubov
Internet-Draft VESvault Corp
Intended status: Experimental 29 June 2026
Expires: 31 December 2026
Deploying Publicly Trusted TLS Servers on Devices Behind NAT Using SNI-
based End-to-End TLS Forwarding (SNIF)
draft-zubov-snif-05
Abstract
This document proposes a solution, referred to as SNIF, that provides
the means for any Internet-connected device to:
* allocate a globally unique anonymous hostname;
* obtain and maintain a publicly trusted X.509 certificate issued
for the allocated hostname;
* accept incoming TLS connections on specific TCP ports of the
allocated hostname from any TLS clients that are capable of
sending Server Name Indication.
The private key associated with the X.509 certificate is securely
stored on the TLS terminating device, and is never exposed to any
other party at any step of the process.
About This Document
This note is to be removed before publishing as an RFC.
Status information for this document may be found at
https://datatracker.ietf.org/doc/draft-zubov-snif.
Information can be found at https://snif.host.
Source for this draft and an issue tracker can be found at
https://github.com/vesvault/snif-i-d.
Status of This Memo
This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79.
Zubov Expires 31 December 2026 [Page 1]
Internet-Draft SNIF June 2026
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF). Note that other groups may also distribute
working documents as Internet-Drafts. The list of current Internet-
Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
This Internet-Draft will expire on 31 December 2026.
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the
document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents (https://trustee.ietf.org/
license-info) in effect on the date of publication of this document.
Please review these documents carefully, as they describe your rights
and restrictions with respect to this document. Code Components
extracted from this document must include Revised BSD License text as
described in Section 4.e of the Trust Legal Provisions and are
provided without warranty as described in the Revised BSD License.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1. Notational Conventions . . . . . . . . . . . . . . . . . 4
2. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 4
3. SNIF CA Proxy Protocol . . . . . . . . . . . . . . . . . . . 5
3.1. Protocol Variables . . . . . . . . . . . . . . . . . . . 5
3.2. Protocol Flow . . . . . . . . . . . . . . . . . . . . . . 6
3.3. CN Allocation Request . . . . . . . . . . . . . . . . . . 7
3.4. CSR Submission Request . . . . . . . . . . . . . . . . . 8
3.5. Certificate Download Request . . . . . . . . . . . . . . 8
4. SNIF Relay Protocol Suite . . . . . . . . . . . . . . . . . . 9
4.1. SNIF Messages . . . . . . . . . . . . . . . . . . . . . . 10
4.2. SNIF Connection Roles . . . . . . . . . . . . . . . . . . 12
4.3. SNIF over TCP (baseline) . . . . . . . . . . . . . . . . 14
4.4. SNIF over QUIC (optional) . . . . . . . . . . . . . . . . 15
4.5. SNIF Client Connection Protocol . . . . . . . . . . . . . 18
4.6. SNIF IPC FIFO Protocol . . . . . . . . . . . . . . . . . 20
4.7. Transport discovery and fallback . . . . . . . . . . . . 22
4.8. Abuse Management . . . . . . . . . . . . . . . . . . . . 22
4.9. SNIF with Encrypted ClientHello (optional) . . . . . . . 23
5. Security Considerations . . . . . . . . . . . . . . . . . . . 25
Zubov Expires 31 December 2026 [Page 2]
Internet-Draft SNIF June 2026
5.1. Blind-relay property (applies to all transport
bindings) . . . . . . . . . . . . . . . . . . . . . . . . 27
5.2. Routing name exposure on the infrastructure leg . . . . . 27
5.3. Connection authentication and connection-identifier
confidentiality . . . . . . . . . . . . . . . . . . . . . 28
5.4. QUIC resource and denial-of-service considerations . . . 29
5.5. Connection consolidation (availability trade) . . . . . . 30
5.6. CA Proxy trust is independent of the relay transport . . 31
6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 31
7. References . . . . . . . . . . . . . . . . . . . . . . . . . 31
7.1. Normative References . . . . . . . . . . . . . . . . . . 31
7.2. Informative References . . . . . . . . . . . . . . . . . 32
Changes from draft-zubov-snif-04 . . . . . . . . . . . . . . . . 33
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 35
1. Introduction
Many Internet-connected devices and services operate behind NAT or on
a dynamic IP address, and therefore cannot be reached by inbound
connections over the Internet: they have no stable, publicly routable
address at which a TLS client can establish a connection to them. An
Internet-of-Things (IoT) device is a common example, but the same
constraint applies to a wide range of physical and virtual hosts. A
dedicated relay is commonly used to route the communications between
such a device and its intended users. While all communications are
recommended to be TLS encrypted, if the relay terminates each TLS
connection - as a conventional reverse proxy does - it gains access
to the unencrypted traffic between the devices and user clients,
which may pose an undesirable security risk.
Designing a dedicated relay that works in end-to-end encrypted mode,
where the TLS tunnel is established between the device and the
client, and is passed by the relay in an encrypted form, raises
additional challenges. Clients expect to be able to verify the
authenticity of the TLS certificate presented by the device they are
connecting to. Public certificate authorities require validation of
the ownership of the hostname the certificate is being requested for,
using certain challenge mechanisms. Therefore, the device needs to
allocate a unique hostname, and to be able to complete the CA
challenge in order to acquire a trusted certificate.
Alternatively, the client may decide to use a different certificate
trust scheme, not based on publicly trusted root CAs. In this case,
the client is limited to specifically built software with custom
trust rules, or the system trust root on the client device needs to
be customized.
Zubov Expires 31 December 2026 [Page 3]
Internet-Draft SNIF June 2026
This document proposes a solution, referred to as SNIF, that allows
any common TLS client with standard root CAs, such as a web browser,
to establish a trusted end-to-end TLS connection with a device behind
NAT using the unique hostname permanently allocated to the device,
via a dedicated relay.
IoT devices are a common motivating case, but SNIF is applicable to
any physical or virtual device, service, or software that is behind
NAT, on a dynamic address, or otherwise unable to accept inbound
connections directly, and that can benefit from accepting trusted TLS
connections to an anonymous hostname.
1.1. Notational Conventions
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in BCP
14 [RFC2119] [RFC8174] when, and only when, they appear in all
capitals, as shown here.
2. Overview
_SNIF CA Proxy_ is a combination of web-based services and background
processes that run on a publicly accessible server, normally on the
same physical server as SNIF Relay. SNIF CA Proxy allocates
hostnames for SNIF Connectors and facilitates issuing and renewing
X.509 certificates [RFC5280] without having access to the Connectors'
private keys. The functions of SNIF CA Proxy are described in
Section 3.
_SNIF Relay_ is a process that runs on a publicly accessible server,
normally on the same physical server as SNIF CA Proxy. SNIF Relay
facilitates end-to-end TLS connections, [RFC8446] or older versions,
between SNIF Clients and SNIF Connectors. The functions of SNIF
Relay are described in Section 4.
_SNIF Connector_ is a software process that runs on a device behind
NAT, or on any other type of device or host that intends to provide
TLS-based services that can be accessed by general purpose TLS
clients using SNIF Relay. SNIF Connector can be implemented as a
standalone process that communicates with the TLS server processes
over local filesystem and sockets, or as an integral part of a TLS
server process.
Zubov Expires 31 December 2026 [Page 4]
Internet-Draft SNIF June 2026
_SNIF Client_ is any common TLS-compatible client with SNI capability
[RFC6066], such as a web browser or an email client, that connects to
a SNIF hostname provided by a specific SNIF Connector. SNIF Client
does not need any awareness of SNIF, or of any protocols described in
this document.
_Certificate Authority (CA)_ is a service that issues public trusted
TLS Certificates to specific hostnames when requested by the hostname
owner, upon validating the ownership of the hostname. CA does not
need any awareness of SNIF, except for a working relationship with
the SNIF CA Proxy that requests certificates using protocols
supported by the CA.
_SNIF Peripheral Process_ is any kind of additional service that
extends or supplements functions of SNIF, in a way not defined within
the scope of this document.
3. SNIF CA Proxy Protocol
SNIF CA Proxy Protocol is designed for securely acquiring and
maintaining a publicly trusted TLS/SSL X.509 certificate issued by a
Certificate Authority to a uniquely allocated hostname, by an agent
that has no direct control over that hostname, or over a server the
hostname is pointing to.
SNIF CA Proxy accepts requests from SNIF Connectors via HTTP / HTTPS.
SNIF CA Proxy interacts with the CA using protocols supported by the
CA, such as ACME [RFC8555], not covered by this document.
3.1. Protocol Variables
Each SNIF Connector MUST be configured with an initiation URL
({initUrl}), which is specific to the SNIF CA Proxy server the
Connector intends to work with. Depending on the CA Proxy rules,
{initUrl} might be unique for each Connector, or common for multiple
Connectors.
The canonical name {cn} is received by a SNIF Connector in response
to the CN Allocation Request (Section 3.3), and might be either a
single hostname or a wildcard starting with "*.", depending on the CA
Proxy rules.
{cn_host} is a hostname derived from the {cn} - it is identical to
{cn} in case of a single-host CN, or is the {cn} with truncated
initial "*." in case of a wildcard CN. Note that {cn_host} is
different than the SNIF hostname in case of a wildcard CN.
Zubov Expires 31 December 2026 [Page 5]
Internet-Draft SNIF June 2026
Each SNIF Connector MAY be configured with an API URL base - {apiUrl}
- identifying the SNIF CA Proxy. If configured, the {apiUrl} SHOULD
be HTTPS. The {apiUrl} pertains only to certificate enrollment with
the CA Proxy; it is not used to authenticate the Relay (see
{relayHost} in Section 4.2).
In case {apiUrl} is not configured - the SNIF Connector MUST derive
{apiUrl} from the {cn_host}, upon allocating the CN, as following:
{apiUrl} := http://{cn_host}/snif-cert/
3.2. Protocol Flow
Upon the initial start or after a hard reset, the Connector SHALL
generate a Private Key, which needs to be securely permanently stored
by the Connector. Any key algorithm acceptable by the CA can be
used, the choice of the algorithm should be made according to the CA
guidelines and industry practices.
The Connector SHALL send a CN Allocation Request using the {initUrl}.
Having the canonical name {cn}, the Connector SHALL generate a CSR
[RFC2986] using the Private Key, the subject containing the {cn}. The
CSR subject may or may not have other fields besides {cn}, according
to the specific requirements of the CA.
The Connector SHALL issue a CSR Submission Request to send the CSR to
the CA Proxy.
Once the CSR is submitted, the Connector MUST permanently store the
{cn} by some means - to minimize the storage compartments it might be
practical to generate and store a dummy self-signed certificate with
the {cn} in the subject until it gets replaced with a trusted
certificate issued by the CA.
At this point, the Connector will normally know the SNIF hostname it
will be using with the SNIF Relay - it matches the {cn} in case of a
single host CN, or is one sub-level down from a wildcard {cn}, the
name being derived by the Connector in a way that cannot be derived
from the {cn} and the public key, e.g. a hash of the Private Key.
The Connector can now send a Certificate Download Request, and MUST
verify the returned Certificate. If the Certificate is valid - the
Connector MUST permanently store it.
If the Certificate Download Request fails - the Connector SHOULD
repeat the request after certain delay. In case the response was 401
and the {authUrl} is returned in a header, and the Connector has the
Zubov Expires 31 December 2026 [Page 6]
Internet-Draft SNIF June 2026
means of communicating with the device user - the Connector also
SHOULD alert the user and bring {authUrl} to their attention by some
means, so the user can complete the required authorization steps. If
the Connector has no means of alerting the user, which is often the
case with IoT devices - the user MUST be provided with some external
means of authorizing with the CA Proxy, not covered by this document.
Once the Certificate is validated and stored, the Connector is
capable of terminating SNIF connections, and may proceed launching a
SNIF Control Connection (Section 4.2). The Connector SHOULD
communicate its SNIF hostname by some means to the SNIF Clients that
will be accessing the Connector. The means of such communication are
not covered by this document.
The Connector SHOULD watch for the expiration of the stored
Certificate. If the Certificate is about to expire in 7 days or
less, or has already expired - the Connector SHOULD send a
Certificate Download Request, and repeat with appropriate delays
until the renewed Certificate is successfully downloaded and
verified.
At any stage of the flow, if the Connector receives unexpected volume
of rejections or inconsistent responses from the CA Proxy, the
Connector MAY decide to hard reset the storage and start the flow
over from the beginning. In such case, the Connector will have to
re-send its new SNIF hostname to any concerned SNIF Clients, the
means of such communication is not covered by this document.
3.3. CN Allocation Request
Connection from: SNIF Connector
Connection to: SNIF CA Proxy
Protocol: https or http
GET {initUrl}
Response 200: Canonical Name (CN) is successfully allocated. The
response headers MUST include X-SNIF-CN: with the value of the
allocated {cn}, either a wildcard starting with "*.", or a single
hostname, depending on the CA Proxy rules. The CA Proxy MUST NOT
return a CN that's been previously returned by another CN Allocation
Request. The Connector SHOULD ignore the response body.
Any other response: Error, try again later.
Zubov Expires 31 December 2026 [Page 7]
Internet-Draft SNIF June 2026
3.4. CSR Submission Request
Connection from: SNIF Connector
Connection to: SNIF CA Proxy
Protocol: https or http
PUT {apiUrl}{cn_host}.csr
Content-Type: application/pkcs10
The request body MUST contain a PEM encoded PKCS#10 CSR [RFC5967],
the newlines are either <CR><LF> or <LF>, the length of the body
SHOULD NOT exceed 16384 bytes.
Note that a CSR for the specific allocated CN can be submitted to the
CA Proxy once in a lifetime. In case of an incorrect submission the
Connector SHOULD hard reset the storage and restart the flow from the
beginning, including allocating a new CN.
Response 201: the CSR is successfully submitted. The response
headers MAY include X-SNIF-AuthUrl: with the value of an {authUrl},
that SHOULD, if possible, be communicated to the user to authorize
the certificate issuance.
Response 403: the CSR for this CN has already been submitted, or is
denied by the CA Proxy rules. If the Connector receives 403, it
SHOULD hard reset the storage and restart the CA Proxy flow from the
beginning.
Response 404: the CN was not allocated.
Any other response: Error, try again later.
3.5. Certificate Download Request
Connection from: SNIF Connector
Connection to: SNIF CA Proxy
Protocol: https or http
GET {apiUrl}{cn_host}.crt
The CA Proxy SHOULD check for a cached previously generated
Certificate chain for the {cn}. If the cached Certificate chain is
found and if it expires in more than 10 days in the future - the
cached Certificate chain SHOULD be returned with status 200.
Zubov Expires 31 December 2026 [Page 8]
Internet-Draft SNIF June 2026
Otherwise, if the {cn} has a valid CSR and a proper authorization to
issue a certificate - the CA Proxy SHOULD return status 503 and
SHOULD launch a background process that communicates with the CA to
issue or renew the certificate, and caches the issued Certificate
chain for subsequent Certificate Download Requests.
Response 200: the Certificate chain is returned. The Content-Type of
such response SHOULD be "application/x-x509-ca-cert". The response
body MUST be a PEM encoded X.509 certificate chain, the issued
certificate being the first member, the newlines are either <CR><LF>
or <LF>, the length of the body SHOULD NOT exceed 65535 bytes.
Response 503: the Certificate is being issued, try later.
Response 401: Certificate issuance authorization is required. The
response headers MAY include X-SNIF-AuthUrl: with the value of an
{authUrl}, that SHOULD, if possible, be communicated to the user to
authorize the certificate issuance. If the CA Proxy expects to work
with Connectors that cannot communicate with the user, it MUST
include external means of the authorization, not covered by this
document.
Response 404: the CN was not allocated, or the CSR was not submitted.
Any other response: Error, try again later.
4. SNIF Relay Protocol Suite
The SNIF Relay Protocol Suite defines the communications among a SNIF
Relay, the SNIF Connectors that register with it, the SNIF Clients
that reach those Connectors through it, and the SNIF Peripheral
Processes attached to the Relay.
The suite is defined in two layers. Section 4.1 and Section 4.2
define, independently of any transport, the SNIF Messages exchanged
and the logical connection roles they are exchanged over.
Section 4.3 and Section 4.4 define two interchangeable transport
bindings that realize those roles: SNIF over TCP, which every
conformant Relay and Connector MUST support, and SNIF over QUIC,
which is OPTIONAL.
Across every binding, the SNIF Relay forwards the end-to-end TLS
traffic of a SNIF Client as an opaque octet stream. The Relay MUST
NOT terminate, decrypt, or otherwise interpret that traffic - save
for the single Encrypted ClientHello exception specified by the
blind-relay invariant (Section 4.2) - and the Connector's certificate
private key is never available to the Relay (see also Section 5).
Zubov Expires 31 December 2026 [Page 9]
Internet-Draft SNIF June 2026
4.1. SNIF Messages
A SNIF Message consists of one or more ASCII characters excluding
non-printable characters, terminated by <CR><LF>.
The total length of a SNIF Message, including the terminal <CR><LF>,
SHOULD NOT exceed 4096 bytes.
8-bit characters are discouraged. If 8-bit characters are used, they
SHOULD comply with UTF-8 [RFC3629].
The receiving party SHOULD silently ignore any invalid or malformed
SNIF message.
The following messages are defined. The "Carried on" role names the
logical connection role (Section 4.2) the message is exchanged over;
the transport bindings (Section 4.3, Section 4.4) specify how that
role is realized on the wire. The message wire formats are unchanged
from earlier revisions of this document, so existing TCP deployments
remain conformant.
SNIF LISTEN {hostname}
SNIF LISTEN {hostname} {options}
Sent by: SNIF Connector
Carried on: Control Connection
Informs the Relay that the Connector is ready to accept incoming TLS
connections to {hostname} through the Relay. {hostname} MUST specify
a single host (no wildcards) and MUST match the CN of the Connector's
TLS certificate, either matching a wildcard CN or exactly matching a
single-host CN. SHOULD be sent only once per Control Connection; the
Relay SHOULD ignore invalid or subsequent SNIF LISTEN messages.
{options}, if present, is a whitespace-separated list of tokens by
which the Connector requests optional behaviors for the session. No
options are defined by this document; the field is reserved for
future use. A Relay MUST ignore any option token it does not
recognize, and a Connector MUST NOT assume an unrecognized option
took effect. This keeps SNIF LISTEN forward compatible: future
revisions may define option tokens (for example, to select a
transport behavior) without breaking existing deployments.
SNIF CONNECT {conn_id} {dst_host}:{dst_port} {fwd_host}:{fwd_port} {cln_addr}:{cln_port}
Sent by: SNIF Relay
Zubov Expires 31 December 2026 [Page 10]
Internet-Draft SNIF June 2026
Carried on: Control Connection
Informs the Connector of an incoming TLS connection from a Client to
the Connector's {dst_host}, TCP port {dst_port}. {conn_id} is a
unique alphanumeric connection identifier assigned by the Relay.
{fwd_host}:{fwd_port} is the address of the Relay node that holds
this Client Connection. In the TCP binding it is the address at
which that node accepts the corresponding Service Connection
(Section 4.3). In the QUIC binding {fwd_host} identifies the node
and the Service stream is delivered to it over a QUIC connection on
the snif port (Section 4.4): the Connector reuses its Control
Connection to that node when it has one, and otherwise (in a cluster)
opens a new Control Connection to {fwd_host} on the snif port.
{fwd_port} pertains to the TCP Service Connection and is not used in
the QUIC binding. {cln_addr}:{cln_port} are the Client's remote
IPv4/IPv6 address and TCP port, {cln_addr} enclosed in "[" brackets
"]". The Relay sends SNIF CONNECT only to Connectors whose
{hostname} matches {dst_host}; the Connector need not verify
{dst_host}.
SNIF ACCEPT {conn_id}
Sent by: SNIF Connector
Carried on: Service Connection
Binds a newly opened Service Connection (TCP binding) or Service
stream (QUIC binding) to the {conn_id} previously received in a SNIF
CONNECT message, signaling that the Connector accepts the connection.
It is sent as the leading octets - the in-band SNIF ACCEPT preamble -
of the Service Connection or Service stream in both bindings
(Section 4.3, Section 4.4).
SNIF CLOSE {conn_id}
Sent by: SNIF Connector
Carried on: Control Connection
Instructs the Relay to terminate the Client connection with the
matching {conn_id} (e.g. to reject a connection). For SNIF CLOSE
received from a Connector, the Relay MUST validate that the
connection was targeted at the Connector's {hostname}, otherwise
ignore the message.
SNIF ABUSE {conn_id} {abuse_score}
Sent by: SNIF Connector
Zubov Expires 31 December 2026 [Page 11]
Internet-Draft SNIF June 2026
Carried on: Control Connection
Instructs the Relay to increase the DoS-protection abuse counter for
the Client that initiated {conn_id} by {abuse_score}, an integer from
1 to 255 (1 is the score for a normal, non-abusive connection). The
Relay MUST validate that {conn_id} was targeted at the Connector's
{hostname}, otherwise ignore the message.
SNIF MSG {hostname} {content}
Sent by: SNIF Connector or SNIF Relay
Carried on: Control Connection (and IPC FIFO, Section 4.6)
Relayed between the Connector and the SNIF Peripheral Processes
attached to the Relay. {content} SHOULD NOT contain whitespace or
non-printable characters; its semantics are specific to the targeted
Peripheral Process and are out of scope. For SNIF MSG received from
a Connector, the Relay MUST verify that {hostname} matches the one
associated with the Connector before forwarding to the IPC FIFOs. A
SNIF MSG sent by a Connector might, in some circumstances, return to
it, and where a Connector holds Control Connections to more than one
node of a Relay cluster (Section 4.4) a SNIF MSG for its {hostname}
may be delivered to it once on each of them; a Connector MUST
therefore process SNIF MSG idempotently and SHOULD account for
duplicates to avoid message storms.
NOOP
Sent by: SNIF Connector or SNIF Relay
Carried on: Control Connection
Carries no action. A Relay receiving NOOP from a Connector SHOULD
promptly send NOOP or any other message back, so a Connector MAY use
NOOP as a keep-alive ping. In the QUIC binding, transport-level
keep-alive (Section 4.4) MAY be used instead.
4.2. SNIF Connection Roles
The protocols below are defined in terms of four logical connection
roles. Each transport binding (Section 4.3, Section 4.4) specifies
how these roles are realized on the wire.
_Control Connection_ is a single, persistent, bidirectional channel
between a SNIF Connector and a SNIF Relay that carries asynchronous
SNIF Messages. It is always initiated by the Connector. The
Connector MUST authenticate to the Relay with the certificate and
Zubov Expires 31 December 2026 [Page 12]
Internet-Draft SNIF June 2026
private key for its SNIF hostname (Section 3); the Relay MUST
validate that certificate and, if it is not trusted, MUST close the
connection immediately.
Each SNIF Connector MAY be configured with a {relayHost} - the
hostname identifying the Relay's TLS certificate, by which the
Connector authenticates the Relay. {relayHost} is independent of the
CA Proxy {apiUrl} (Section 3.1): the CA Proxy and the Relay are
commonly the same host but need not be, and the Relay need not serve
HTTPS. If {relayHost} is configured, the Connector MUST validate the
certificate presented by the Relay against {relayHost} - in both the
TCP (Section 4.3) and QUIC (Section 4.4) bindings - closing the
connection if the Relay presents no certificate or the certificate
does not match. If {relayHost} is not configured, the Connector does
not authenticate the Relay's certificate.
_Service Connection_ is a bidirectional, reliable, in-order octet
stream dedicated to exactly one forwarded SNIF Client connection. It
carries the Client's end-to-end TLS traffic as an opaque payload.
The Relay MUST treat this payload as opaque ciphertext and MUST NOT
attempt to interpret it, save for the Encrypted ClientHello exception
in the blind-relay invariant below; the SNI, already interpreted to
route the Client Connection, is merely forwarded to the Connector as
part of this stream. Each Service Connection is associated with a
{conn_id}.
_Client Connection_ is the TLS connection between a SNIF Client and
the SNIF Relay, in which the Relay acts as a transparent end-to-end
forwarder (Section 4.5).
_IPC FIFO_ carries communications between nodes of a SNIF Relay
cluster and/or between a SNIF Relay and SNIF Peripheral Processes
(Section 4.6).
Blind-relay invariant: under every binding the Relay MUST be able to
route a Client Connection to a Connector using only the Server Name
Indication of the Client's TLS ClientHello, and MUST forward the
Service Connection payload without the means to decrypt it. The
Connector's end-to-end certificate private key MUST NOT be
transmitted to, derivable by, or stored on the Relay.
The sole exception to "without the means to decrypt it" is the
Encrypted ClientHello (ECH) transform of Section 4.9: a Relay
configured for ECH MAY decrypt the Client's initial ClientHello
solely to recover the Server Name Indication it routes on, and
forward the reconstructed inner ClientHello in place of the outer
one. This is the only transformation a Relay is permitted to apply
to the end-to-end TLS handshake; every octet from the reconstructed
Zubov Expires 31 December 2026 [Page 13]
Internet-Draft SNIF June 2026
ClientHello onward remains opaque. The ECH key used for this
transform is distinct from the Connector's end-to-end certificate key
and confers no ability to decrypt or impersonate the end-to-end TLS
session, which remains subject to the invariant above.
4.3. SNIF over TCP (baseline)
Every conformant SNIF Relay and SNIF Connector MUST support the TCP
binding. It realizes the Control Connection as the "snif" protocol
and each Service Connection as a separate "snif-srv" connection.
Protocol name (Control): snif
Protocol name (Service): snif-srv
Connection from: SNIF Connector
Connection to: SNIF Relay
_Control Connection._ The Connector opens a TCP connection to the
hostname matching its certificate CN, which points to the Relay.
Upon accepting the TCP connection, the Relay MUST initiate a reversed
TLS session as the client peer and MAY supply a client certificate.
The Connector MUST initiate TLS as the server peer, using its
certificate and private key. If the Connector is configured with
{relayHost} (Section 4.2), it MUST require the Relay to present a
client certificate valid for {relayHost} and MUST shut down the TLS
session and TCP socket if the Relay presents no certificate or it
does not match. On successful negotiation the Relay MUST validate
the Connector's certificate and, if untrusted, MUST shut down the TLS
session and TCP socket immediately. If the host named in the
Connector's certificate does not fall within any of the domains
served by the Relay, the Relay SHOULD close the connection. Once
validated, both peers exchange SNIF Messages (Section 4.1) over the
TLS connection. The Connector sends SNIF LISTEN to begin accepting
Client connections.
_Service Connection._ In response to a SNIF CONNECT received over the
Control Connection, if the Connector accepts the connection it MUST
open a TCP connection to {fwd_host}:{fwd_port} and MUST immediately
send, as plain TCP, a SNIF ACCEPT {conn_id} message carrying the
{conn_id} from the SNIF CONNECT. The Connector MUST then immediately
assign all further traffic of that Service Connection to the matching
TLS server process. To reject, the Connector SHOULD instead send
SNIF CLOSE {conn_id} over the Control Connection. The Connector
SHOULD also send any SNIF Message back over the Control Connection on
accept to update the keep-alive timer (a copy of the SNIF ACCEPT MAY
be used).
Zubov Expires 31 December 2026 [Page 14]
Internet-Draft SNIF June 2026
_Relay forwarding._ On a valid SNIF ACCEPT, the Relay SHOULD link the
Service Connection to the matching Client Connection, forward to it
all buffered TLS data previously received from the Client, and begin
bidirectional forwarding. The Relay MUST reject any further SNIF
ACCEPT bearing an already-linked {conn_id}. If the {conn_id} or
message is invalid, or an abuse threshold is reached, the Relay
SHOULD terminate the Service Connection immediately. When either the
Client or Service Connection closes, or an inactivity timeout
elapses, the Relay SHOULD shut down both.
4.4. SNIF over QUIC (optional)
A SNIF Relay and SNIF Connector MAY support the QUIC binding
[RFC9000]. It realizes the Control Connection and all of a
Connector's Service Connections as a single QUIC connection: the
Control Connection is carried on a dedicated control stream, and each
Service Connection is a distinct QUIC stream. This replaces the per-
Client outbound connection of the TCP binding, and its transport
handshake, with a lightweight per-Client stream open within the
already-established QUIC connection, and isolates each forwarded
session on its own stream (no cross-session head-of-line blocking),
while preserving the blind-relay invariant of Section 4.2. As in the
TCP binding the Connector opens each Service stream and admits the
connection with SNIF ACCEPT, so no Client payload reaches the
Connector before it accepts.
ALPN protocol identifier: "snifq/1" (see Section 4.7 and Section 6)
QUIC connection from: SNIF Connector (QUIC client)
QUIC connection to: SNIF Relay (QUIC server)
Default UDP port: 7123 (the registered "snif" port, see Section 6)
Zubov Expires 31 December 2026 [Page 15]
Internet-Draft SNIF June 2026
_Connection establishment and authentication._ The Connector
establishes a QUIC connection to the Relay on UDP port 7123 (the
registered "snif" port, Section 6), unless a different port has been
advertised out of band (Section 4.7). In the QUIC TLS handshake
[RFC9001] the Connector MUST act as the QUIC client and MUST present,
via TLS client authentication, the certificate for its SNIF hostname
and prove possession of the corresponding private key. The private
key MUST NOT be exposed to the Relay or any other party. The Relay
MUST validate that certificate exactly as in Section 4.3 and MUST
close the connection (with a TLS handshake failure) if it is
untrusted. The Relay, as QUIC server, presents its own certificate.
A Connector configured with {relayHost} (Section 4.2) MUST validate
the Relay certificate against {relayHost}, closing the connection if
it does not match; a Connector without {relayHost} does not
authenticate the Relay certificate. ALPN [RFC7301] MUST be
negotiated to "snifq/1".
The TLS roles in the QUIC binding are the reverse of those in the TCP
binding (Section 4.3), where the Connector is the TLS server and a
Relay certificate is optional. This reversal is inherent to QUIC,
which binds the TLS handshake roles to the transport roles [RFC9001]:
the endpoint that initiates the connection is necessarily the TLS
client. Because the Connector is typically behind NAT and is the
only endpoint able to initiate a connection (Section 1), it MUST be
the QUIC client, and therefore the TLS client, and the Relay MUST
present a server certificate. A Relay certificate is consequently
REQUIRED in this binding rather than optional. A Connector without
{relayHost} does not validate it, so it MAY be self-signed; a
Connector configured with {relayHost} (Section 4.2) validates the
certificate and thereby authenticates the Relay.
_Control stream._ Immediately after the handshake the Connector MUST
open one client-initiated bidirectional stream as the Control
Connection and send SNIF LISTEN on it. SNIF Messages carried on the
Control Connection (Section 4.1) are exchanged on this stream.
Transport-level keep-alive (QUIC PING / idle timeout, [RFC9000]) MAY
replace NOOP.
Zubov Expires 31 December 2026 [Page 16]
Internet-Draft SNIF June 2026
_Service streams._ The Service stream flow mirrors the TCP binding
(Section 4.3), with a QUIC stream in place of a dialed-back
connection. For each incoming Client Connection the Relay MUST send
a single SNIF CONNECT {conn_id} ... message on the control stream and
MUST buffer the Client's initial TLS data (Section 4.5) while
awaiting acceptance. To accept, the Connector MUST open a client-
initiated bidirectional stream and MUST send, as the leading octets
of that stream, a single SNIF ACCEPT {conn_id} message terminated by
<CR><LF>, carrying the {conn_id} from the SNIF CONNECT. The
Connector MUST then assign all further octets of that stream to the
matching TLS server process.
In this binding {fwd_host} (Section 4.1) identifies the Relay node
that holds the Client Connection and to which the Service stream MUST
be delivered; {fwd_port} applies to the TCP dial-back (Section 4.3)
and is not used here. When the Connector already holds a Control
Connection to that node - the usual case, and always so for a single-
node Relay - it opens the Service stream on that connection. When it
does not, the clustered case of the following paragraph applies.
_Clustered Relays._ In a SNIF Relay cluster a Client Connection may
arrive at a node to which the Connector holds no Control Connection.
That node sets {fwd_host} (and, for TCP-capable Connectors,
{fwd_port}) to its own address and, having no local Control
Connection for the Connector, forwards the SNIF CONNECT to a node
that does over an IPC FIFO (Section 4.6), which delivers it on the
control stream. Because a QUIC stream can be opened only within the
connection it belongs to, the Connector cannot serve such a Client on
its existing connection; it MUST instead open a new QUIC connection
to {fwd_host} on the snif port (UDP 7123 by default, or the port
advertised out of band, Section 4.7) and open the Service stream
there. This connection is an ordinary Control Connection: the
Connector establishes and authenticates it exactly as above, sends
SNIF LISTEN on its control stream, and opens the Service stream. The
receiving node thereby gains a local Control Connection and routes
subsequent Clients for that {hostname} to the Connector directly,
without further IPC FIFO relay. Keeping the cross-node leg a QUIC
connection also keeps that leg encrypted, preserving the routing-name
concealment of the QUIC binding (Section 5.2), and of ECH
(Section 4.9) when combined with it, across the cluster.
Zubov Expires 31 December 2026 [Page 17]
Internet-Draft SNIF June 2026
A Connector MAY therefore hold a Control Connection to more than one
node of a cluster at once, each opened reactively as Clients arrive
on new nodes, so the number of connections a Connector maintains is
bounded by the size of the Relay cluster rather than by the number of
Clients. A Connector MAY close a Control Connection that has been
idle, reverting that node to IPC FIFO relay until a later Client
reopens one. Because each such Control Connection carries SNIF MSG
(Section 4.1), a Connector that holds Control Connections to multiple
nodes MUST process SNIF MSG idempotently (Section 4.1).
To reject, the Connector does not open a Service stream, and SHOULD
send SNIF CLOSE {conn_id} on the control stream so the Relay can tear
down the matching Client Connection promptly rather than awaiting its
accept timeout (Section 4.5).
_Relay forwarding._ On a SNIF ACCEPT preamble bearing a known, not-
yet-linked {conn_id} the Relay MUST link the Service stream to the
matching Client Connection, forward to it all buffered Client TLS
data, and begin bidirectional forwarding of the end-to-end TLS
payload. The Relay MUST reject a SNIF ACCEPT bearing an unknown or
already-linked {conn_id} by aborting that stream (RESET_STREAM plus
STOP_SENDING [RFC9000]). When either the Client Connection or the
Service stream ends (FIN or reset), or an inactivity timeout elapses,
the Relay MUST end the other. SNIF ABUSE {conn_id} and SNIF CLOSE
{conn_id} on the control stream refer to the Service stream by
{conn_id}.
Flow control: the Relay SHOULD bound the number of concurrent Service
streams a Connector may open with MAX_STREAMS [RFC9000], and both
peers SHOULD set per-stream and per-connection flow-control limits so
that a single greedy forwarded session cannot starve other Service
streams on the same Connector's connection (see Section 5).
_Encapsulation overhead._ On the Relay-to-Connector leg the Client's
end-to-end TLS payload is carried inside the QUIC connection's own
encrypted transport, so that leg is encrypted twice: once end-to-end
between Client and Connector, and once by QUIC between Relay and
Connector. This overhead is inherent to forwarding opaque end-to-end
TLS over an encrypted transport, and is the cost of the
infrastructure-leg confidentiality the QUIC binding provides
(Section 5.2); the TCP binding avoids it by carrying the Service
Connection as plain TCP, at the cost of exposing that leg
(Section 5.2).
4.5. SNIF Client Connection Protocol
Protocol name: snif-cln
Zubov Expires 31 December 2026 [Page 18]
Internet-Draft SNIF June 2026
Connection from: Any TLS client with Server Name Indication, such as
a web browser or an email client
Connection to: SNIF Relay
From the Client's perspective, a SNIF Client Connection functions as
a direct TLS connection to the device.
The ports the Relay is listening to can be any well-known ports for
services with persistent TLS, such as https or imaps, or can be any
custom ports agreed among the Relay, the Connectors and the Clients.
The Relay accepts an incoming TCP connection, receives and buffers
the incoming initial data from the client, and attempts to interpret
the received data as a TLS handshake.
If the received data is not recognized as a TLS handshake, does not
contain an SNI record in a supported format, or the SNI hostname does
not meet rules defined for the Relay - the Relay SHOULD immediately
reject the TLS session with an appropriate error status, and shut
down the Client Connection.
If the SNI hostname is found acceptable - the Relay allocates a
unique {conn_id}, checks if there are current Control Connections
that match the SNI hostname, and sends a SNIF CONNECT message over
those connections.
If there are no active applicable Control Connections, or if the
Relay doesn't receive a response from a SNIF Connector within a
specified timeframe - the Relay SHOULD forward the same SNIF CONNECT
message over IPC FIFOs (if any are open) to alert cluster peer Relays
and Peripheral processes of the incoming Client Connection.
A Service Connection (TCP binding) or Service stream (QUIC binding)
carrying a matching SNIF ACCEPT establishes an end-to-end TLS circuit
with the Client Connection. Once established, the Relay bi-
directionally forwards all traffic between the Client and the Service
Connection until either of the connections is closed or is timed out
due to inactivity.
Upon receiving a matching SNIF CLOSE - the Relay MUST terminate the
Client Connection. If a Service Connection has already been linked
it MUST be terminated too, otherwise the Relay SHOULD attempt to
gracefully reject TLS on the Client Connection with an appropriate
status prior to shutting down TCP.
Zubov Expires 31 December 2026 [Page 19]
Internet-Draft SNIF June 2026
4.6. SNIF IPC FIFO Protocol
Protocol name: snif-fifo
Connection from: SNIF Relay or SNIF Peripheral Service
Connection to: SNIF Relay or SNIF Peripheral Service
SNIF IPC FIFO is a permanent trusted connection between the SNIF
Relay and a SNIF Peripheral Process, or between a pair of nodes in a
SNIF Relay cluster. An IPC FIFO is usually unidirectional, but a
bidirectional connection can serve as a pair of FIFOs. An IPC FIFO
can be implemented as a Unix FIFO pipe, a TCP socket, an SSH tunnel
or by other means. The mechanism of establishing and maintaining IPC
FIFOs is implementation specific and is not covered by this document.
The following SNIF Messages are defined over an IPC FIFO from the
perspective of a SNIF Relay:
SNIF CONNECT {conn_id} {dst_host}:{dst_port} {fwd_host}:{fwd_port} {cln_addr}:{cln_port}
Direction: Send or Receive
(see Section 4.1).
The SNIF CONNECT message is sent by a Relay over an IPC FIFO in case
the Relay failed to reach the respective Connector through Control
Connections. SNIF CONNECT sent by a Relay MUST be followed up by one
of SNIF CLEAR or SNIF CLOSE to inform the Peripheral Processes of the
further outcome. Even when the SNIF CONNECT is acted upon by a peer
Relay, the resulting Service Connection (TCP binding) or Service
stream (QUIC binding) is established to the {fwd_host} carried in the
message, which is the originating Relay; that Relay therefore always
observes the acceptance and is the one that emits the followup.
When a SNIF CONNECT message is received by a Relay, the Relay SHOULD
forward it to any matching open Control Connections, or ignore it
otherwise.
SNIF CLEAR {conn_id}
Direction: Send
Zubov Expires 31 December 2026 [Page 20]
Internet-Draft SNIF June 2026
The SNIF CLEAR message SHOULD be sent by a Relay only as a followup
to SNIF CONNECT with a matching {conn_id}, in case the Client
Connection that triggered SNIF CONNECT was accepted by a Service
Connection. Because the Service Connection, or Service stream in the
QUIC binding, is always established to the originating Relay (the
{fwd_host} of the SNIF CONNECT), only that Relay processes the SNIF
ACCEPT and therefore only it sends SNIF CLEAR.
The purpose of SNIF CLEAR is to advise Peripheral Processes to cease
further attempts of reaching the Connector by external means, not
specified within this document.
SNIF CLOSE {conn_id}
Direction: Send or Receive
(see Section 4.1).
The SNIF CLOSE message SHOULD be sent by a Relay only as a followup
to SNIF CONNECT with a matching {conn_id}, in case the Client
Connection that triggered SNIF CONNECT was closed without being
accepted.
When the SNIF CLOSE is received by a Relay, the Relay SHOULD
immediately close the matching Client and/or Service Connection if
any found, ignore the message otherwise.
SNIF ABUSE {conn_id} {abuse_score}
Direction: Receive
(see Section 4.1).
SNIF MSG {hostname} {content}
Direction: Send or Receive
(see Section 4.1).
SNIF CTL {ctl_fd} {hostname} {remote_addr}:{remote_port}
SNIF CTL {ctl_fd}
Direction: Send
Zubov Expires 31 December 2026 [Page 21]
Internet-Draft SNIF June 2026
The SNIF CTL message is sent by a Relay to inform Peripheral
Processes about Control Connections. The first version is sent for
each opening Control Connection, and is followed up by the second
version with the matching {ctl_fd} when the Control Connection is
closed. {ctl_fd} is a numeric descriptor which is unique for open
connections, but can be reused after a connection is closed.
4.7. Transport discovery and fallback
The TCP binding (Section 4.3) is the mandatory baseline; a Connector
that cannot use QUIC, or that fails to establish a QUIC connection,
MUST be able to operate over TCP - unless its deployment requires the
server name to be concealed on the infrastructure leg as well as the
access leg, which requires the QUIC binding (Section 5.2) combined
with ECH (Section 4.9). Such a Connector SHOULD instead treat QUIC
unreachability as a failure rather than falling back to TCP, since
the fallback would re-expose the server name on the infrastructure
leg (see the transport-downgrade discussion in Section 5.2). Because
the TCP and QUIC bindings use different transport protocols,
selection is by attempt-and-fallback rather than in-band negotiation:
* A Connector that supports the QUIC binding SHOULD attempt a QUIC
connection to the Relay on UDP port 7123 (the registered "snif"
port, Section 6) first, offering ALPN "snifq/1", and SHOULD fall
back to the TCP binding (Section 4.3) if the QUIC connection
cannot be established within a reasonable time or if "snifq/1" is
not negotiated.
* A Relay MAY advertise QUIC support, including an alternative UDP
port, out of band (e.g. via the CA Proxy initiation response,
Section 3) so Connectors can skip the QUIC attempt when it is
known to be unavailable, or reach it on a non-default port.
A Connector MUST NOT maintain both a TCP and a QUIC Control
Connection to the same Relay node for the same {hostname}
simultaneously. A Connector MAY, however, hold QUIC Control
Connections for the same {hostname} to several distinct nodes of a
Relay cluster at once, as the clustered case of Section 4.4 requires.
4.8. Abuse Management
SNIF Relay SHOULD implement basic protection from denial of service.
A separate abuse count SHOULD be assigned to each remote address,
incremented by 1 on every incoming connection from the address,
incremented by a specified score on every received SNIF ABUSE
message, and periodically decremented or reset at regular time
intervals.
Zubov Expires 31 December 2026 [Page 22]
Internet-Draft SNIF June 2026
If the abuse counter for a certain remote address reaches a specific
threshold, the Relay SHOULD drop any further TCP connections from
that address until the abuse counter goes below the threshold. The
Relay MAY allow some grace above the threshold to incoming SNIF
Service Connections, to minimize stalled Client Connections.
SNIF Connector MAY implement basic protection from denial of service
by limiting the number of accepted connections per period of time
and/or the total number of open connections, and reject connections
over the limit.
4.9. SNIF with Encrypted ClientHello (optional)
A SNIF Relay MAY offer Encrypted ClientHello (ECH) [RFC9849] so that
the true server name a Client requests is concealed from observers on
the access leg between the Client and the Relay (Section 5.2). ECH
is independent of the transport binding and MAY be combined with
either the TCP binding (Section 4.3) or the QUIC binding
(Section 4.4). When a Relay offers ECH it acts as the ECH client-
facing server in the split-mode topology of [RFC9849]: it terminates
the ECH encryption only, never the end-to-end TLS session.
_Keying._ A Relay that offers ECH holds a single ECH key pair for the
names it serves. The private key resides on the Relay only and is
never distributed to Connectors. In a Relay cluster (Section 4.4)
every node MUST hold this ECH private key, because any node may
receive a Client Connection and is then the node that decrypts and
reconstructs its ClientHello (below) in order to route and serve it;
the key is shared only among the mutually trusted nodes of one
logical Relay and is still never distributed to Connectors. The
corresponding ECHConfig, naming a "public_name" within the Relay's
own domain, is published in the DNS HTTPS resource record [RFC9460]
for the SNIF hostnames the Relay serves; a single wildcard record MAY
cover an entire SNIF zone. Because the Relay recovers the routing
name by decryption (below) rather than from a per-Connector value,
one ECHConfig suffices for every Connector and no ECH state is
required in a Connector's DNS.
_Client._ A SNIF Client is any ECH-capable TLS client. It resolves
the SNIF hostname, obtains the Relay's ECHConfig from the HTTPS
record, and connects to the Relay presenting the "public_name" as the
outer (visible) Server Name Indication, with its true (inner)
ClientHello, including the SNIF hostname, encrypted under the
ECHConfig. The Client requires no awareness of SNIF (Section 1).
_Relay processing._ On receiving the Client's initial ClientHello
(Section 4.5) the Relay examines it for an "encrypted_client_hello"
extension of type "outer":
Zubov Expires 31 December 2026 [Page 23]
Internet-Draft SNIF June 2026
* If the extension is absent, the Relay routes on the cleartext
Server Name Indication exactly as in Section 4.5.
* If the extension is present and the Relay can decrypt it with a
held ECH key, the Relay MUST recover and reconstruct the inner
ClientHello as specified in [RFC9849] (expanding any
"ech_outer_extensions" compression against the outer ClientHello).
The reconstruction MUST be byte-exact, because the reconstructed
inner ClientHello is the message that both the Client and the
Connector incorporate into the TLS handshake transcript; any
deviation causes the handshake to fail. The Relay then routes on
the inner Server Name Indication as in Section 4.5 and forwards
the reconstructed inner ClientHello, in place of the outer one, as
the leading end-to-end TLS octets of the Service Connection.
* If the extension is present but the Relay cannot decrypt it (for
example a decoy "GREASE" value ([RFC9849]), or an unknown or
rotated configuration identifier), the Relay MUST treat ECH as not
negotiated and route on the cleartext (outer) Server Name
Indication exactly as in Section 4.5 - with one exception. A
decoy ECH extension carries the Client's true name in that
cleartext SNI, so this routes it to its Connector normally. A
genuine ECH ClientHello the Relay cannot decrypt instead always
carries the Relay's own "public_name" as the outer SNI. Because
"public_name" names the Relay itself and matches no Connector, the
Relay MUST NOT attempt to route such a Client Connection: it MUST
NOT emit a SNIF CONNECT, forward over an IPC FIFO, or otherwise
wait for a Connector to accept, since no accept can arrive and the
Client Connection would only stall until it timed out
(Section 4.5). The Relay MUST instead resolve the Client
Connection immediately: by default it closes the Client
Connection, or, if it holds a certificate for "public_name", it
MAY complete the handshake as "public_name" and return fresh
"retry_configs" as the ECH rejection of [RFC9849]. This
retry_configs path is OPTIONAL, and is the only circumstance in
which a Relay terminates, rather than forwards, a Client's TLS
session, one that never carries Connector traffic. A Relay that
holds no certificate for "public_name" MUST close the Client
Connection.
_Connector._ The Connector terminates the end-to-end TLS session from
the reconstructed inner ClientHello as usual, and MAY ignore the
Server Name Indication as any single-host server may. It MUST,
however, implement the ECH backend behavior of [RFC9849]: it MUST
recognize the inner-type "encrypted_client_hello" marker in the
forwarded ClientHello and emit the corresponding ECH acceptance
signal in its ServerHello, which an ECH-capable Client verifies. A
Connector that omits this signal is interpreted by the Client as ECH
Zubov Expires 31 December 2026 [Page 24]
Internet-Draft SNIF June 2026
rejection, and the connection fails rather than reaching the intended
host. The Connector needs no ECH key and performs no decryption;
this backend behavior is the lighter half of ECH.
_Ecosystem-wide requirement._ Because a Relay publishes its ECHConfig
for a set of SNIF hostnames at once (typically a wildcard record),
every Connector reachable under those names MUST implement the ECH
backend behavior above; a Client directed by the record to use ECH
toward a Connector that does not will fail, and retrying does not
help. Offering ECH is therefore a joint decision of a Relay and all
of its Connectors. A deployment that needs ECH for only a subset of
Connectors MUST publish per-name HTTPS records for those names rather
than a wildcard.
ECH conceals the server name only on the access leg between the
Client and the Relay; on the infrastructure leg the reconstructed
inner ClientHello carries it in the clear. A deployment that
requires the server name concealed on the infrastructure leg as well
SHOULD combine ECH with the QUIC binding (Section 4.4), which
encrypts that leg; the two mechanisms are complementary, and the per-
leg exposure trade-offs are set out in Section 5.2.
5. Security Considerations
Requests to CA Proxy (Section 3) sent over plain unencrypted HTTP,
including a PKCS#10 CSR in the CSR Submission Request payload, do not
contain sensitive information.
The response to the CN Allocation Request, if sent over plain HTTP,
is a randomly generated hostname or wildcard, that will also be
publicly exposed through Certificate Transparency once the
Certificate is issued. Any attempt by an intruder to submit an
alternate CSR for the issued CN prior to the legitimate Connector,
will result in a certificate that doesn't match the Connector's
private key, therefore the Connector will need to hard reset and redo
the initialization. If the intruder alters the X-SNIF-CN: response
sent to the Connector, the CSR submission for a bad CN will be
rejected by the CA Proxy, which will also require to hard reset the
Connector.
The content of the Certificate Download Request response is an X.509
certificate which is safe to be exposed to any parties. If the
intruder alters the HTTP response to the CSR Submission Request or to
the Certificate Download request, the Connector won't receive a valid
certificate and will need a hard reset.
Zubov Expires 31 December 2026 [Page 25]
Internet-Draft SNIF June 2026
SNIF Connector SHOULD NOT send its hostname to any parties until it
downloads and successfully validates the Certificate from the CA
Proxy.
To mitigate request flooding potentially resulting in denial of
service, it is RECOMMENDED for SNIF CA Proxy to require a Certificate
issuance authorization. For SNIF Connectors that have a means of
interacting with a user, such as a console or a web browser, the CA
Proxy SHOULD implement an interactive authorization mechanism not
described in this document, and return {authUrl} to the Connector
(Section 3.5); the Connector SHOULD then present {authUrl} to the
user to complete the process.
For a SNIF CA Proxy that intends to work with Connectors that have
limited or no means of interacting with a user, some non-interactive
Certificate issuance authorization mechanism SHOULD be implemented.
As an example of such mechanism, each SNIF Connector can have a
unique {initUrl} that MUST be HTTPS to avoid possible interception,
and each Connector is supplied with a unique setup URL presented to
the user, the CA Proxy properly mapping each setup URL to the
matching {initUrl}, and using the setup URL to authorize the
certificate issuance and to communicate the SNIF Connector's hostname
to the user's browser. Such mechanism MUST have a means of alerting
the user about misrouted setup, when some agent other than the
legitimate user has used the same setup URL during the setup process,
in which case the user MUST be instructed to immediately hard reset
the Connector and repeat the setup. Such mechanism is RECOMMENDED to
use an HTTPS {apiUrl} (Section 3.1); otherwise it MUST provide a
means to prevent or detect intercepted setup when an intruder alters
a submitted CSR, such as an out-of-band indication of setup
completion (for example, status lights on a hardware device). The
details of such mechanism are not covered by this document.
Since each certificate issued by a CA remains on the certificate
transparency public records, it is RECOMMENDED for SNIF CA Proxy to
only issue Certificates with a wildcard CN. This way, the actual
Connector's hostname (Section 3.2) will not be listed on the public
records.
SNIF IPC FIFO connections SHOULD only be established between mutually
trusted parties, and need to be secured by external means specific to
the implementation, such as filesystem permissions, TLS or SSH
tunnels etc. The security of such external means cannot be assessed
within the scope of this document.
A compromised SNIF CA Proxy can potentially issue certificates to any
hostnames allocated by the Relay, including a catch-all wildcard,
using an alternative private key, and thus allow a man-in-the-middle
Zubov Expires 31 December 2026 [Page 26]
Internet-Draft SNIF June 2026
attack on any SNIF Connectors associated with the Relay. This
vulnerability can be mitigated by constant monitoring of public TLS
Transparency logs, such as [RFC6962]. At least one independent party
SHOULD continuously monitor TLS Transparency logs for each deployed
SNIF CA Proxy and Relay. Once any duplicate or overlapping
certificates are detected - the corresponding SNIF Relay MUST be
permanently deemed compromised.
5.1. Blind-relay property (applies to all transport bindings)
The security of SNIF rests on the SNIF Relay being unable to read or
alter the traffic it forwards. Under every transport binding
(Section 4.3, Section 4.4) the SNIF Relay MUST route a SNIF Client
Connection to a SNIF Connector using only the Server Name Indication
of the Client's TLS ClientHello, and MUST forward the Service
Connection payload as opaque octets without the means to decrypt it.
The private key of the Connector's certificate MUST NOT be
transmitted to, derivable by, or stored on the Relay. Consequently a
compromised Relay can deny service, observe traffic volume and
timing, and learn the routable server name of each connection
(Section 5.2), but cannot read or forge the end-to-end TLS content.
From the Client's perspective the SNIF Client Connection is a TLS
session with a trusted certificate; the security of the TLS content
between the Client and the Connector is specific to the protocol
carried and is unchanged by the choice of binding.
5.2. Routing name exposure on the infrastructure leg
SNIF forwarding requires the Relay to read a routable server name
from the Client's ClientHello. That name is the Connector's SNIF
hostname, which is already public (it appears in Certificate
Transparency logs; see the wildcard-CN guidance earlier in this
section). The Relay therefore necessarily learns which Connector
each Client reaches; this is inherent to SNIF and is not concealed by
any binding.
The bindings differ, however, in what an on-path observer between the
Relay and the Connector can see:
* In the TCP binding (Section 4.3), the Service Connection carries
the Client's end-to-end TLS as plain TCP. The Client's
ClientHello, including the cleartext Server Name Indication, is
visible to any observer on the Relay-to-Connector path.
* In the QUIC binding (Section 4.4), the Service Connection is a
stream within the Relay-to-Connector QUIC connection and is thus
encrypted on that leg; the Server Name Indication is concealed
from on-path observers there (though not from the Relay itself).
Zubov Expires 31 December 2026 [Page 27]
Internet-Draft SNIF June 2026
A Connector whose deployment requires the server name to be hidden
from on-path observers on the infrastructure leg SHOULD use the QUIC
binding (Section 4.4). The QUIC binding does not, however, conceal
the Server Name Indication on the access leg between the Client and
the Relay: that is the Client's own ClientHello, sent in the clear
under every binding. Encrypted ClientHello (ECH) [RFC9849], where a
Relay offers it (Section 4.9), is the only mechanism in this document
that conceals the server name on the access leg. It hides the true
(inner) name from observers between the Client and the Relay, and
keeps a specific name that sits below a public wildcard out of
Certificate Transparency. ECH does not conceal the routing name from
the Relay itself, which decrypts it to route (Section 4.9), nor from
observers on the infrastructure leg, where the reconstructed inner
ClientHello again carries the name in the clear unless the QUIC
binding is also used. ECH and the QUIC binding are therefore
complementary rather than alternatives: ECH covers the access leg,
the QUIC binding covers the infrastructure leg.
Transport downgrade: because binding selection is by attempt-and-
fallback (Section 4.7), a network attacker who blocks QUIC (UDP) can
force a Connector to fall back to the TCP binding and thereby re-
expose the routing name on the infrastructure leg. A Connector that
depends on the QUIC binding's infrastructure-leg confidentiality
SHOULD treat QUIC unreachability as a failure rather than silently
falling back, and a Relay SHOULD advertise QUIC availability out of
band (Section 4.7) so that an unexpected fallback is detectable.
5.3. Connection authentication and connection-identifier
confidentiality
In the TCP binding, the Control Connection is a reverse TLS session
in which the Connector authenticates as the TLS server using its SNIF
certificate and the Relay validates it; in high-security settings the
Connector SHOULD be configured with {relayHost} (Section 4.2), in
which case the Relay MUST supply a client certificate valid for
{relayHost} and the Connector MUST immediately terminate the Control
Connection if the certificate is absent or does not match. If the
Connector does not validate a client certificate from the Relay, it
MUST NOT send sensitive information in SNIF MSG messages and MUST NOT
consider messages received from the Relay to be trusted. The
{conn_id} of a Service Connection is sent over an unsecured TCP
connection; it is single-use and is only actionable by the targeted
Connector, and all subsequent octets are end-to-end TLS.
In the QUIC binding, the Connector authenticates as the QUIC client
using TLS client authentication, presenting the same SNIF certificate
and proving possession of its private key without ever exposing the
key, and the Relay MUST validate it with the same trust rules,
Zubov Expires 31 December 2026 [Page 28]
Internet-Draft SNIF June 2026
closing the connection on failure. The Relay, as QUIC server,
presents its own certificate; a Connector configured with {relayHost}
(Section 4.2) MUST validate the Relay's certificate against it,
closing the connection on mismatch. Because the control stream and
all Service streams share this single authenticated, encrypted QUIC
connection, the SNIF CONNECT and the SNIF ACCEPT preamble, including
{conn_id}, are not exposed in cleartext on the wire, removing the
cleartext {conn_id} exposure of the TCP Service Connection. In
contrast to the TCP binding, where the Relay need not present a
certificate at all, the QUIC binding always requires the Relay to
present a server certificate, so a Connector configured with
{relayHost} can authenticate the Relay on every connection.
When {relayHost} (Section 4.2) is not configured, the Connector does
not authenticate the Relay, in either binding. An active on-path
attacker that can impersonate the Relay may then observe connection
metadata - notably the {conn_id} and the SNIF Messages on the Control
Connection - and, in the TCP binding, redirect the Connector to a
substitute Relay. It cannot read the forwarded Client traffic, which
remains end-to-end TLS terminated only on the Connector. Operators
that require infrastructure-leg confidentiality or Relay authenticity
SHOULD configure {relayHost}.
5.4. QUIC resource and denial-of-service considerations
The QUIC binding multiplexes all of a Connector's forwarded sessions
onto one connection, which changes the resource-exhaustion surface:
* Connection admission. Because the QUIC binding mirrors the TCP
accept flow (Section 4.4), the Connector opens a client-initiated
Service stream only for connections it accepts, so no Client
payload reaches the Connector before it admits the connection,
exactly as in the TCP binding. The only unsolicited traffic a
flood of Client connections can impose on the Connector is the
small SNIF CONNECT messages on the control stream, which the
Relay's abuse counting (Section 4.8) already gates; rejecting a
connection (by not opening a stream, optionally with SNIF CLOSE)
costs the Connector nothing beyond reading that message. This
avoids the unsolicited-push exposure a relay-initiated stream
design would have on a constrained or slow Connector link.
* Cross-node Control Connections. In a Relay cluster a Connector
may hold Control Connections to several nodes at once
(Section 4.4), each opened reactively and authenticated with the
Connector's SNIF certificate, so they are not an avenue for
unauthenticated parties and their count is bounded by the cluster
size rather than by the number of Clients. A Relay SHOULD apply
the same abuse counting (Section 4.8) and per-Connector stream and
Zubov Expires 31 December 2026 [Page 29]
Internet-Draft SNIF June 2026
flow-control limits to each. Because the same SNIF MSG may be
delivered to a Connector on each of these connections, SNIF MSG
handling MUST be idempotent (Section 4.1, Section 4.4).
* Stream concurrency. The Connector opens a client-initiated
Service stream per accepted Client Connection, bounded by the
Relay's advertised MAX_STREAMS [RFC9000]. The Relay SHOULD use
MAX_STREAMS to cap the number of concurrent forwarded sessions per
Connector; the Connector likewise SHOULD limit how many it opens.
The Connector already controls concurrency in the TCP binding,
where it chooses whether to dial back a Service Connection in
response to each SNIF CONNECT; MAX_STREAMS provides the equivalent
control as an explicit, Relay-advertised limit native to the
transport, rather than adding backpressure the TCP binding lacked.
* Flow-control starvation. A single greedy forwarded session could
otherwise consume the connection's flow-control budget and starve
sibling Service streams. A Relay and a Connector SHOULD set per-
stream and per-connection flow-control limits so no one session
can starve the others.
* Amplification. The Connector is the QUIC client (initiator), so
the standard QUIC anti-amplification limit applies and the Relay
(QUIC server) MUST NOT be usable as an amplification reflector.
The SNIF ABUSE mechanism continues to apply per {conn_id} /
Service stream.
* 0-RTT. If 0-RTT is used to resume the QUIC connection, early data
MUST be limited to idempotent operations; in particular, control
messages whose replay could cause harm MUST NOT be sent in 0-RTT.
Re-sending SNIF LISTEN for the same {hostname} is idempotent and
is permitted.
5.5. Connection consolidation (availability trade)
Consolidating a Connector's sessions onto one QUIC connection
improves resilience to packet loss (loss on one stream does not stall
others) but concentrates connection-level faults: a QUIC connection
close, stateless reset, or path failure drops all of that Connector's
forwarded sessions at once, whereas independent TCP Service
Connections fail independently. The dropped forwarded sessions
cannot themselves be re-established: each is an end-to-end TLS
session terminated on the Client and the Connector, so once its
transport is gone the session is irrecoverable and the affected
Clients simply reconnect, which the Relay forwards as fresh Service
streams. Re-establishment therefore applies to the Control
Connection, not to the individual Service streams: a Connector using
the QUIC binding SHOULD detect connection loss promptly and re-
Zubov Expires 31 December 2026 [Page 30]
Internet-Draft SNIF June 2026
establish the Control Connection, so that those Client reconnections
can be served again.
5.6. CA Proxy trust is independent of the relay transport
The man-in-the-middle exposure from a compromised SNIF CA Proxy
issuing certificates under an alternative key (described earlier in
this section, with its Certificate Transparency monitoring
mitigation) is a property of the certificate-issuance trust model and
is unaffected by the choice of relay transport binding. Neither the
TCP binding nor the QUIC binding adds to nor reduces that exposure.
6. IANA Considerations
Service Names "snif", "snif-srv", "snif-cln" and "snif-fifo" are
registered with IANA.
TCP port 7123 is registered with IANA for service "snif".
This document also requests that UDP port 7123 be assigned to service
"snif" for the SNIF over QUIC binding (Section 4.4), matching the
existing TCP assignment.
This document additionally requests registration of the following
protocol identifier in the "TLS Application-Layer Protocol
Negotiation (ALPN) Protocol IDs" registry [RFC7301], identifying the
SNIF over QUIC binding (Section 4.4):
Protocol: SNIF over QUIC
Identification Sequence: 0x73 0x6E 0x69 0x66 0x71 0x2F 0x31
("snifq/1")
Reference: This document, Section 4.4
7. References
7.1. Normative References
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>.
[RFC2986] Nystrom, M. and B. Kaliski, "PKCS #10: Certification
Request Syntax Specification Version 1.7", RFC 2986,
DOI 10.17487/RFC2986, November 2000,
<https://www.rfc-editor.org/info/rfc2986>.
Zubov Expires 31 December 2026 [Page 31]
Internet-Draft SNIF June 2026
[RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S.,
Housley, R., and W. Polk, "Internet X.509 Public Key
Infrastructure Certificate and Certificate Revocation List
(CRL) Profile", RFC 5280, DOI 10.17487/RFC5280, May 2008,
<https://www.rfc-editor.org/info/rfc5280>.
[RFC6066] Eastlake 3rd, D., "Transport Layer Security (TLS)
Extensions: Extension Definitions", RFC 6066,
DOI 10.17487/RFC6066, January 2011,
<https://www.rfc-editor.org/info/rfc6066>.
[RFC7301] Friedl, S., Popov, A., Langley, A., and E. Stephan,
"Transport Layer Security (TLS) Application-Layer Protocol
Negotiation Extension", RFC 7301, DOI 10.17487/RFC7301,
July 2014, <https://www.rfc-editor.org/info/rfc7301>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
[RFC8446] Rescorla, E., "The Transport Layer Security (TLS) Protocol
Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018,
<https://www.rfc-editor.org/info/rfc8446>.
[RFC9000] Iyengar, J., Ed. and M. Thomson, Ed., "QUIC: A UDP-Based
Multiplexed and Secure Transport", RFC 9000,
DOI 10.17487/RFC9000, May 2021,
<https://www.rfc-editor.org/info/rfc9000>.
[RFC9001] Thomson, M., Ed. and S. Turner, Ed., "Using TLS to Secure
QUIC", RFC 9001, DOI 10.17487/RFC9001, May 2021,
<https://www.rfc-editor.org/info/rfc9001>.
[RFC9460] Schwartz, B., Bishop, M., and E. Nygren, "Service Binding
and Parameter Specification via the DNS (SVCB and HTTPS
Resource Records)", RFC 9460, DOI 10.17487/RFC9460,
November 2023, <https://www.rfc-editor.org/info/rfc9460>.
[RFC9849] Rescorla, E., Oku, K., Sullivan, N., and C. A. Wood, "TLS
Encrypted Client Hello", RFC 9849, DOI 10.17487/RFC9849,
March 2026, <https://www.rfc-editor.org/info/rfc9849>.
7.2. Informative References
[RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
10646", STD 63, RFC 3629, DOI 10.17487/RFC3629, November
2003, <https://www.rfc-editor.org/info/rfc3629>.
Zubov Expires 31 December 2026 [Page 32]
Internet-Draft SNIF June 2026
[RFC5967] Turner, S., "The application/pkcs10 Media Type", RFC 5967,
DOI 10.17487/RFC5967, August 2010,
<https://www.rfc-editor.org/info/rfc5967>.
[RFC6962] Laurie, B., Langley, A., and E. Kasper, "Certificate
Transparency", RFC 6962, DOI 10.17487/RFC6962, June 2013,
<https://www.rfc-editor.org/info/rfc6962>.
[RFC8555] Barnes, R., Hoffman-Andrews, J., McCarney, D., and J.
Kasten, "Automatic Certificate Management Environment
(ACME)", RFC 8555, DOI 10.17487/RFC8555, March 2019,
<https://www.rfc-editor.org/info/rfc8555>.
Changes from draft-zubov-snif-04
This appendix summarizes the substantive changes in this revision.
It is to be removed before publishing as an RFC.
* Retitled from "... on IoT Devices ..." to "... on Devices Behind
NAT ..." and generalized the Introduction and Overview so that an
IoT device is presented as a common example rather than a
constraint; SNIF applies to any device, service, or software that
is behind NAT, on a dynamic address, or otherwise unable to accept
inbound connections. The draft name and the "SNIF" abbreviation
are unchanged.
* Restructured the SNIF Relay Protocol Suite (Section 4) into a
transport-neutral layer and two interchangeable transport
bindings. The SNIF Message wire formats are unchanged, so
existing TCP deployments remain conformant.
* Lifted the full SNIF Message catalog into Section 4.1 and added a
new Section 4.2 (SNIF Connection Roles) that names the Control,
Service, Client, and IPC FIFO roles and states the blind-relay
invariant.
* Folded the former Control (snif) and Service (snif-srv) connection
mechanics into Section 4.3 (SNIF over TCP), now the mandatory
baseline binding.
Zubov Expires 31 December 2026 [Page 33]
Internet-Draft SNIF June 2026
* Added Section 4.4 (SNIF over QUIC), an OPTIONAL binding that
carries the Control Connection and all Service Connections as
streams of a single QUIC connection, replacing the per-Client
outbound connection and its transport handshake with a lightweight
per-Client stream open and isolating forwarded sessions per
stream. The binding mirrors the TCP accept flow: the Connector
opens each Service stream and admits the connection with SNIF
ACCEPT, so no Client payload reaches the Connector before it
accepts.
* Added Section 4.7 (Transport discovery and fallback): QUIC is
attempted first and falls back to TCP; the ALPN identifier
"snifq/1" version-tags the QUIC binding.
* Reorganized the Security Considerations (Section 5) to name the
blind-relay property, describe infrastructure-leg Server Name
Indication exposure and Encrypted ClientHello, scope the cleartext
{conn_id} exposure to the TCP binding, and add QUIC resource/DoS
and connection-consolidation considerations. The CA Proxy trust
considerations are unchanged.
* Added IANA requests to register the ALPN protocol identifier
"snifq/1" and to assign UDP port 7123 to "snif" for the QUIC
binding. Added normative references RFC 9000 and RFC 9001 (QUIC)
and RFC 7301 (ALPN), and a normative reference to RFC 9849
(Encrypted ClientHello).
* Added an optional {relayHost} Connector configuration
(Section 4.2): the hostname identifying the Relay's TLS
certificate. When set, the Connector MUST validate the
certificate presented by the Relay against {relayHost} in both the
TCP and QUIC bindings; when unset, the Relay certificate is not
authenticated. {apiUrl} is now used only for CA Proxy enrollment
and no longer plays a relay-authentication role.
* The Relay SHOULD close the Control Connection if the host named in
the Connector's certificate does not fall within any of the
domains the Relay serves.
* Added Section 4.9 (SNIF with Encrypted ClientHello), an OPTIONAL
feature in which the Relay holds a single ECH key, publishes the
ECHConfig in the DNS HTTPS resource record, and acts as the ECH
client-facing server: it decrypts the ClientHello solely to
recover and route on the inner Server Name Indication,
reconstructs the inner ClientHello byte-exactly, and forwards it
in place of the outer one, while the Connector supplies the ECH
acceptance signal without holding any ECH key. Section 4.2
records this ECH unwrap as the sole permitted exception to the
Zubov Expires 31 December 2026 [Page 34]
Internet-Draft SNIF June 2026
blind-relay invariant, and Section 5.2 is rewritten to state that
ECH conceals the server name on the access leg while the QUIC
binding conceals it on the infrastructure leg. Added a normative
reference to RFC 9460 (SVCB/HTTPS DNS records).
Author's Address
Jim Zubov
VESvault Corp
Email: jz@vesvault.com
URI: https://snif.host
Zubov Expires 31 December 2026 [Page 35]