Skip to content

Commit

Permalink
Merge pull request #110 from tetofonta/main
Browse files Browse the repository at this point in the history
Support for EXTERNAL authentication mechanism and mutual-TLS example
  • Loading branch information
gftea authored Oct 14, 2023
2 parents 92da1bb + a92f9e8 commit f719e5a
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 4 deletions.
16 changes: 15 additions & 1 deletion amqprs/src/api/security.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub struct SecurityCredentials {
enum AuthenticationMechanism {
PLAIN,
AMQPLAIN,
// EXTERNAL,
EXTERNAL,
// RABBIT-CR-DEMO,
}

Expand All @@ -50,11 +50,24 @@ impl SecurityCredentials {
}
}

/// Create and return EXTERNAL without credentials
///
/// This must be used together with mTLS connection.
pub fn new_external() -> Self {
Self {
username: "".to_owned(),
password: "".to_owned(),
mechanism: AuthenticationMechanism::EXTERNAL,
}
}


/// Get the name of authentication mechanism of current credential
pub(crate) fn get_mechanism_name(&self) -> &str {
match self.mechanism {
AuthenticationMechanism::PLAIN => "PLAIN",
AuthenticationMechanism::AMQPLAIN => "AMQPLAIN",
AuthenticationMechanism::EXTERNAL => "EXTERNAL"
}
}
/// Get the security challenge `response` string, to be sent to server.
Expand Down Expand Up @@ -88,6 +101,7 @@ impl SecurityCredentials {
.unwrap();
String::from_utf8(buf.to_vec()).unwrap()
}
AuthenticationMechanism::EXTERNAL => "".to_string(),
}
}
}
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ services:
- RABBITMQ_USERNAME=user
- RABBITMQ_PASSWORD=bitnami
- RABBITMQ_VHOST=/
- RABBITMQ_PLUGINS=rabbitmq_management,rabbitmq_management_agent,rabbitmq_web_dispatch,rabbitmq_auth_mechanism_ssl

volumes:
- "./rabbitmq_conf/custom.conf:/bitnami/rabbitmq/conf/custom.conf"
Expand Down
6 changes: 6 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# features for example target
[features]
example-tls = ["amqprs/tls", "amqprs/traces"]
default = ["amqprs/traces"]

[[example]]
name = "basic_pub_sub"
Expand All @@ -37,6 +38,11 @@ name = "tls"
path = "src/tls.rs"
required-features = ["example-tls"]

[[example]]
name = "mtls"
path = "src/mtls.rs"
required-features = ["example-tls"]

[[example]]
name = "field_table"
path = "src/field_table.rs"
120 changes: 120 additions & 0 deletions examples/src/mtls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use amqprs::{
callbacks::{DefaultChannelCallback, DefaultConnectionCallback},
channel::{
BasicConsumeArguments, BasicPublishArguments, QueueBindArguments, QueueDeclareArguments,
},
connection::{Connection, OpenConnectionArguments},
consumer::DefaultConsumer,
BasicProperties,
};
use tokio::time;
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
use amqprs::security::SecurityCredentials;

use amqprs::tls::TlsAdaptor;

#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
// construct a subscriber that prints formatted traces to stdout
// global subscriber with log level according to RUST_LOG
tracing_subscriber::registry()
.with(fmt::layer())
.with(EnvFilter::from_default_env())
.try_init()
.ok();

////////////////////////////////////////////////////////////////
// TLS specific configuration
let current_dir = std::env::current_dir().unwrap();
let current_dir = current_dir.join("rabbitmq_conf/client/");

let root_ca_cert = current_dir.join("ca_certificate.pem");
let client_cert = current_dir.join("client_AMQPRS_TEST_certificate.pem");
let client_private_key = current_dir.join("client_AMQPRS_TEST_key.pem");
// domain should match the certificate/key files
let domain = "AMQPRS_TEST";

let args = OpenConnectionArguments::new("localhost", 5671, "", "")
.tls_adaptor(
TlsAdaptor::with_client_auth(
Some(root_ca_cert.as_path()),
client_cert.as_path(),
client_private_key.as_path(),
domain.to_owned(),
)
.unwrap(),
)
.credentials(SecurityCredentials::new_external())
.finish();

////////////////////////////////////////////////////////////////
// everything below should be the same as regular connection
// open a connection to RabbitMQ server
let connection = Connection::open(&args).await.unwrap();
connection
.register_callback(DefaultConnectionCallback)
.await
.unwrap();

// open a channel on the connection
let channel = connection.open_channel(None).await.unwrap();
channel
.register_callback(DefaultChannelCallback)
.await
.unwrap();

// declare a queue
let (queue_name, _, _) = channel
.queue_declare(QueueDeclareArguments::default())
.await
.unwrap()
.unwrap();

// bind the queue to exchange
let rounting_key = "amqprs.example";
let exchange_name = "amq.topic";
channel
.queue_bind(QueueBindArguments::new(
&queue_name,
exchange_name,
rounting_key,
))
.await
.unwrap();

//////////////////////////////////////////////////////////////////////////////
// start consumer with given name
let args = BasicConsumeArguments::new(&queue_name, "example_basic_pub_sub");

channel
.basic_consume(DefaultConsumer::new(args.no_ack), args)
.await
.unwrap();

//////////////////////////////////////////////////////////////////////////////
// publish message
let content = String::from(
r#"
{
"publisher": "example"
"data": "Hello, amqprs!"
}
"#,
)
.into_bytes();

// create arguments for basic_publish
let args = BasicPublishArguments::new(exchange_name, rounting_key);

channel
.basic_publish(BasicProperties::default(), content, args)
.await
.unwrap();

// keep the `channel` and `connection` object from dropping before pub/sub is done.
// channel/connection will be closed when drop.
time::sleep(time::Duration::from_secs(1)).await;
// explicitly close
channel.close().await.unwrap();
connection.close().await.unwrap();
}
10 changes: 8 additions & 2 deletions rabbitmq_conf/custom.conf
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
auth_mechanisms.1 = PLAIN
auth_mechanisms.2 = AMQPLAIN
auth_mechanisms.3 = RABBIT-CR-DEMO
auth_mechanisms.3 = EXTERNAL
auth_mechanisms.4 = RABBIT-CR-DEMO

log.default.level = debug
log.console = true
log.console.level = debug

log.default.level = debug
listeners.ssl.default = 5671

ssl_options.cacertfile = /bitnami/tls-test/ca_certificate.pem
ssl_options.certfile = /bitnami/tls-test/server_AMQPRS_TEST_certificate.pem
ssl_options.keyfile = /bitnami/tls-test/server_AMQPRS_TEST_key.pem
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = true
ssl_cert_login_from = subject_alternative_name
ssl_cert_login_san_type = dns
ssl_cert_login_san_index = 1

# private key password
# ssl_options.password = bunnies
3 changes: 2 additions & 1 deletion start_rabbitmq.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/bin/bash

COMMON_NAME=AMQPRS_TEST
USERNAME=user

# Create directories for rabbitmq server and client and alter permissions
#------------------------
Expand All @@ -15,7 +16,7 @@ sudo chmod 444 rabbitmq_conf/client/*
#------------------------
git clone https://github.com/rabbitmq/tls-gen tls-gen
cd tls-gen/basic
make CN=$COMMON_NAME
make CN=$COMMON_NAME CLIENT_ALT_NAME=$USERNAME
make verify CN=$COMMON_NAME
make info CN=$COMMON_NAME
ls -lha ./result
Expand Down

0 comments on commit f719e5a

Please sign in to comment.