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:
| Profile | Login |
|---|---|
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.
- Macros
- Manual
/// 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,
))
}
/// 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| {
let place = subscriber(AmqpAddress::queue(orders()), Place);
b.include(place.build());
})
}
fn app() -> impl App<State = ()> {
let password = env::var("AMQP_PASSWORD").unwrap_or_default();
service(broker(
"amqps://broker.internal:5671",
"orders-svc",
&password,
))
}