Skip to content

Expose Acceptor constructors for in-memory rustls configs (ServerConfig / TlsAcceptor) #916

Description

@torinnd

What is the problem your feature solves, or the need it fulfills?

Acceptor::tls_handshake() is public, but an Acceptor can only be obtained via TlsSettings::build(), which loads a certificate and key from files on disk (and panics if it can't). There's no way to construct an Acceptor from a rustls::ServerConfig built at runtime.

This blocks any setup whose key material lives in memory rather than in files. A common case for this would be when the certificate and key arrive from a secrets manager (AWS Secrets Manager, HashiCorp Vault, Cloudflare Secrets Store, etc.). With only a file-based constructor, the sole way to feed such a config to the existing public tls_handshake() is to write the private key out to a temporary file so TlsSettings can read it back. For secrets-manager deployments this can be a security regression, as it forces writing private keys to local disk.

Describe the solution you'd like

A single public constructor on Acceptor that takes an already-built rustls config:

impl Acceptor {
    /// Build an Acceptor from a runtime-constructed rustls `ServerConfig`,
    /// rather than from certificate/key files.
    pub fn from_server_config(config: Arc<ServerConfig>) -> Self {
        Self {
            acceptor: RusTlsAcceptor::from(config),
            callbacks: None,
        }
    }
}

I think this is the whole change. It's the same construction TlsSettings::build() already performs in its last step (RusTlsAcceptor::from(...)), just taking the ServerConfig as an argument instead of loading it from files — so no existing behavior changes, and it composes with the already-public tls_handshake(). ServerConfig is already a public re-export and Acceptor::acceptor is already a public field, so this exposes nothing new beyond construction; callbacks: None is the same no-callback path build() produces today. rustls's own ResolvesServerCert then covers per-connection / per-SNI cert selection.

Describe alternatives you've considered

  • Writing the key and cert to ephemeral files so TlsSettings::intermediate() can read them: this takes key material that arrived in memory (e.g. from a secrets manager) and persists it to local disk, with the lifecycle/cleanup burden that implies.
  • A per-connection certificate callback on the listener: heavier, and the rustls feature currently returns an error from TlsSettings::with_callbacks() anyway. A ServerConfig constructor is the smaller primitive, and callbacks could be layered on later.

Additional context

For the in-memory case described above, this constructor is all that's needed to handshake on an already-accepted stream (after a CONNECT) and never touch the listener path, so no TlsSettings changes are required.

The same primitive would also cover a second use case I don't have but you might care about: listeners whose TLS config is built at runtime rather than from files (ACME, or per-SNI certs configured at startup via a ResolvesServerCert). That might want a matching TlsSettings::with_server_config(...) entry point so the config flows through add_endpoint. It's a larger surface for which I don't currently have a draft PR, but I'd be happy to take a stab at it if it was deemed worthwhile.

Metadata

Metadata

Assignees

Labels

AcceptedThis change is accepted by us and merged to our internal repoergonomicsEase of use, developer friendliness

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions