Skip to main content

Security

Sasl picks the authentication: anonymous, plain or external; an amqps:// URL needs the rustls or native-tls feature.

A service logs in one of two ways, and a broker uses one of them. Credentials in the URL, amqp://user:password@host, log in with SASL PLAIN. A profile set with sasl goes with a URL that carries no credentials:

ProfileLogin
Sasl::plain(user, password)SASL PLAIN with the user and password as written
Sasl::anonymous()SASL ANONYMOUS, for a broker that admits clients without credentials
Sasl::external()SASL EXTERNAL: the broker takes an identity established outside SASL; it needs an amqps:// URL

A password the deployment hands the service, from the environment or a secret store, goes through Sasl::plain. So does a password with a character the URL reserves, such as @, :, / or %. A connection with neither credentials nor a profile opens without SASL, and a broker that requires a login refuses it. A refused login, such as a wrong password, and Sasl::external on an amqp:// URL fail startup with AmqpError::Connect.

An amqps:// URL encrypts the connection with TLS. The rustls feature checks the broker's certificate against the Mozilla root certificates built into the binary. The native-tls feature uses the operating system's TLS library and its trust store; with both features enabled, native-tls is the one used. The connect checks the certificate against the host in the URL, so the URL names the broker by a host name or an IPv4 address. An IPv6 address gives TLS no server name, so the connect fails on it. The connection presents no client certificate.

A TestApp test reads the URL as the connect reads it, so an amqps:// URL without a TLS feature fails startup there too. The login itself happens only against a broker, in a live test. The generated AsyncAPI document reports the host and port and never the credentials in the URL.

Connecting to production takes the URL and the credentials from the environment.

Example​

The dependency line:

ruststream-amqp = { version = "0.7", features = ["rustls"] }

The service logs in as orders-svc over TLS. The password comes from the environment.

/// The broker at `url`, a URL without credentials; the service logs in with SASL PLAIN.
fn broker(url: &str, user: &str, password: &str) -> AmqpBroker {
AmqpBroker::new(url).sasl(Sasl::plain(user, password))
}

fn service(broker: AmqpBroker) -> impl App<State = ()> {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(broker, |b| {
b.include(place);
})
}

#[ruststream::app]
fn app() -> impl App<State = ()> {
let password = env::var("AMQP_PASSWORD").unwrap_or_default();
service(broker(
"amqps://broker.internal:5671",
"orders-svc",
&password,
))
}