add tsig auth support
This commit is contained in:
parent
c8906c0060
commit
424f830e5a
6 changed files with 162 additions and 56 deletions
44
Cargo.lock
generated
44
Cargo.lock
generated
|
@ -447,6 +447,21 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||||
|
dependencies = [
|
||||||
|
"foreign-types-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-shared"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -983,6 +998,33 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl"
|
||||||
|
version = "0.10.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"foreign-types",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"openssl-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-sys"
|
||||||
|
version = "0.9.72"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.0.0"
|
version = "6.0.0"
|
||||||
|
@ -1802,6 +1844,7 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
"openssl",
|
||||||
"radix_trie",
|
"radix_trie",
|
||||||
"rand",
|
"rand",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -1827,6 +1870,7 @@ dependencies = [
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
"openssl",
|
||||||
"rand",
|
"rand",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
trust-dns-client = { version = "0.21", features = ["dnssec"] }
|
trust-dns-client = { version = "0.21", features = ["dnssec-openssl"] }
|
||||||
trust-dns-proto = "0.21"
|
trust-dns-proto = "0.21"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
|
@ -5,8 +5,14 @@ log:
|
||||||
- target: stderr
|
- target: stderr
|
||||||
any: debug
|
any: debug
|
||||||
|
|
||||||
|
key:
|
||||||
|
- id: dev
|
||||||
|
algorithm: hmac-sha256
|
||||||
|
secret: mbmz4J3Efm1BUjqe12M1RHsOnPjYhKQe+2iKO4tL+a4=
|
||||||
|
|
||||||
acl:
|
acl:
|
||||||
- id: example_acl
|
- id: example_acl
|
||||||
|
key: dev
|
||||||
address: [ 127.0.0.1, ::1]
|
address: [ 127.0.0.1, ::1]
|
||||||
action: [transfer, update]
|
action: [transfer, update]
|
||||||
|
|
||||||
|
|
102
src/config.rs
102
src/config.rs
|
@ -1,13 +1,11 @@
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use std::time::Duration as StdDuration;
|
||||||
|
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use chrono::Duration;
|
use chrono::Duration as ChronoDuration;
|
||||||
|
|
||||||
|
use crate::models::name::SerdeName;
|
||||||
use rocket::{Request, State, http::Status, request::{FromRequest, Outcome}};
|
use crate::dns::TsigAlgorithm;
|
||||||
use rocket::outcome::try_outcome;
|
|
||||||
|
|
||||||
use crate::dns::{DnsClient, DnsConnectorClient, RecordConnector, ZoneConnector};
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -18,66 +16,70 @@ pub struct Config {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct DnsClientConfig {
|
pub struct DnsClientConfig {
|
||||||
pub server: SocketAddr
|
pub server: SocketAddr,
|
||||||
|
#[serde(deserialize_with = "from_std_duration")]
|
||||||
|
pub timeout: StdDuration,
|
||||||
|
pub tsig: Option<TsigConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct TsigConfig {
|
||||||
|
pub name: SerdeName,
|
||||||
|
#[serde(deserialize_with = "from_base64")]
|
||||||
|
pub key: Vec<u8>,
|
||||||
|
#[serde(deserialize_with = "from_tsigalg")]
|
||||||
|
pub algorithm: TsigAlgorithm,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct WebAppConfig {
|
pub struct WebAppConfig {
|
||||||
#[serde(deserialize_with = "from_duration")]
|
#[serde(deserialize_with = "from_chrono_duration")]
|
||||||
pub token_duration: Duration,
|
pub token_duration: ChronoDuration,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_duration<'de, D>(deserializer: D) -> Result<Duration, D::Error>
|
|
||||||
|
fn from_std_duration<'de, D>(deserializer: D) -> Result<StdDuration, D::Error>
|
||||||
where D: Deserializer<'de>
|
where D: Deserializer<'de>
|
||||||
{
|
{
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
String::deserialize(deserializer)
|
String::deserialize(deserializer)
|
||||||
.and_then(|string| humantime::parse_duration(&string).map_err(|err| Error::custom(err.to_string())))
|
.and_then(|string| humantime::parse_duration(&string).map_err(|err| Error::custom(err.to_string())))
|
||||||
.and_then(|duration| Duration::from_std(duration).map_err(|err| Error::custom(err.to_string())))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Maybe remove this
|
fn from_chrono_duration<'de, D>(deserializer: D) -> Result<ChronoDuration, D::Error>
|
||||||
#[rocket::async_trait]
|
where D: Deserializer<'de>
|
||||||
impl<'r> FromRequest<'r> for DnsClient {
|
{
|
||||||
type Error = ();
|
use serde::de::Error;
|
||||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
String::deserialize(deserializer)
|
||||||
let config = try_outcome!(request.guard::<&State<Config>>().await);
|
.and_then(|string| humantime::parse_duration(&string).map_err(|err| Error::custom(err.to_string())))
|
||||||
match DnsClient::new(config.dns.server).await {
|
.and_then(|duration| ChronoDuration::from_std(duration).map_err(|err| Error::custom(err.to_string())))
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to connect to DNS server: {}", e);
|
|
||||||
Outcome::Failure((Status::InternalServerError, ()))
|
|
||||||
},
|
|
||||||
Ok(c) => Outcome::Success(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rocket::async_trait]
|
|
||||||
impl<'r> FromRequest<'r> for Box<dyn RecordConnector> {
|
fn from_base64<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
|
||||||
type Error = ();
|
where D: Deserializer<'de>
|
||||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
{
|
||||||
let config = try_outcome!(request.guard::<&State<Config>>().await);
|
use serde::de::Error;
|
||||||
match DnsClient::new(config.dns.server).await {
|
String::deserialize(deserializer)
|
||||||
Err(e) => {
|
.and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string())))
|
||||||
error!("Failed to connect to DNS server: {}", e);
|
|
||||||
Outcome::Failure((Status::InternalServerError, ()))
|
|
||||||
},
|
|
||||||
Ok(c) => Outcome::Success(Box::new(DnsConnectorClient::new(c)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rocket::async_trait]
|
fn from_tsigalg<'de, D>(deserializer: D) -> Result<TsigAlgorithm, D::Error>
|
||||||
impl<'r> FromRequest<'r> for Box<dyn ZoneConnector> {
|
where D: Deserializer<'de>
|
||||||
type Error = ();
|
{
|
||||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
use serde::de::Error;
|
||||||
let config = try_outcome!(request.guard::<&State<Config>>().await);
|
|
||||||
match DnsClient::new(config.dns.server).await {
|
let algo = match String::deserialize(deserializer)?.as_str() {
|
||||||
Err(e) => {
|
"hmac-sha256" => TsigAlgorithm::HmacSha256,
|
||||||
error!("Failed to connect to DNS server: {}", e);
|
"hmac-sha384" => TsigAlgorithm::HmacSha384,
|
||||||
Outcome::Failure((Status::InternalServerError, ()))
|
"hmac-sha512" => TsigAlgorithm::HmacSha512,
|
||||||
},
|
_ => return Err(Error::custom("Unsupported mac algorithm"))
|
||||||
Ok(c) => Outcome::Success(Box::new(DnsConnectorClient::new(c)))
|
};
|
||||||
}
|
|
||||||
|
if !algo.supported() {
|
||||||
|
Err(Error::custom("Unsupported mac algorithm"))
|
||||||
|
} else {
|
||||||
|
Ok(algo)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
use std::{future::Future, pin::Pin, task::{Context, Poll}};
|
use std::{future::Future, pin::Pin, task::{Context, Poll}};
|
||||||
|
use std::sync::Arc;
|
||||||
use futures_util::{ready, Stream, StreamExt};
|
use futures_util::{ready, Stream, StreamExt};
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use tokio::{net::TcpStream as TokioTcpStream, task};
|
use tokio::{net::TcpStream as TokioTcpStream, task};
|
||||||
use trust_dns_client::{client::AsyncClient, error::ClientError, op::DnsResponse, tcp::TcpClientStream};
|
use trust_dns_client::{client::AsyncClient, error::ClientError, op::DnsResponse, tcp::TcpClientStream};
|
||||||
|
use trust_dns_client::rr::dnssec::tsig::TSigner;
|
||||||
use trust_dns_proto::error::{ProtoError, ProtoErrorKind};
|
use trust_dns_proto::error::{ProtoError, ProtoErrorKind};
|
||||||
use trust_dns_proto::iocompat::AsyncIoTokioAsStd;
|
use trust_dns_proto::iocompat::AsyncIoTokioAsStd;
|
||||||
|
|
||||||
|
use crate::config::DnsClientConfig;
|
||||||
|
|
||||||
|
|
||||||
pub struct DnsClient(AsyncClient);
|
pub struct DnsClient(AsyncClient);
|
||||||
|
|
||||||
|
@ -26,13 +30,25 @@ impl DerefMut for DnsClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DnsClient {
|
impl DnsClient {
|
||||||
pub async fn new(addr: SocketAddr) -> Result<Self, ProtoError> {
|
pub async fn from_config(dns_config: &DnsClientConfig) -> Result<Self, ProtoError> {
|
||||||
let (stream, handle) = TcpClientStream::<AsyncIoTokioAsStd<TokioTcpStream>>::new(addr);
|
let (stream, handle) = TcpClientStream::<AsyncIoTokioAsStd<TokioTcpStream>>::new(dns_config.server);
|
||||||
|
let signer = if let Some(tsig_config) = dns_config.tsig.as_ref() {
|
||||||
|
Some(Arc::new(TSigner::new(
|
||||||
|
tsig_config.key.clone(),
|
||||||
|
tsig_config.algorithm.clone(),
|
||||||
|
tsig_config.name.clone().into_inner(),
|
||||||
|
60,
|
||||||
|
)?.into()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let client = AsyncClient::with_timeout(
|
let client = AsyncClient::with_timeout(
|
||||||
stream,
|
stream,
|
||||||
handle,
|
handle,
|
||||||
std::time::Duration::from_secs(5),
|
dns_config.timeout,
|
||||||
None);
|
signer,
|
||||||
|
);
|
||||||
let (client, bg) = client.await?;
|
let (client, bg) = client.await?;
|
||||||
task::spawn(bg);
|
task::spawn(bg);
|
||||||
return Ok(DnsClient(client))
|
return Ok(DnsClient(client))
|
||||||
|
@ -61,3 +77,4 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,45 @@ pub use trust_dns_client::rr::{
|
||||||
RData, DNSClass, Record
|
RData, DNSClass, Record
|
||||||
};
|
};
|
||||||
pub use trust_dns_proto::rr::Name;
|
pub use trust_dns_proto::rr::Name;
|
||||||
|
pub use trust_dns_proto::rr::dnssec::rdata::tsig::TsigAlgorithm;
|
||||||
|
|
||||||
// Reexport module types
|
// Reexport module types
|
||||||
pub use connector::{RecordConnector, ZoneConnector, ConnectorError};
|
pub use connector::{RecordConnector, ZoneConnector, ConnectorError};
|
||||||
pub use dns_connector::{DnsConnectorClient, DnsConnectorError};
|
pub use dns_connector::{DnsConnectorClient, DnsConnectorError};
|
||||||
pub use client::DnsClient;
|
pub use client::DnsClient;
|
||||||
|
|
||||||
|
|
||||||
|
use rocket::{Request, State, http::Status, request::{FromRequest, Outcome}};
|
||||||
|
use rocket::outcome::try_outcome;
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for Box<dyn RecordConnector> {
|
||||||
|
type Error = ();
|
||||||
|
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
|
let config = try_outcome!(request.guard::<&State<Config>>().await);
|
||||||
|
match DnsClient::from_config(&config.dns).await {
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to connect to DNS server: {}", e);
|
||||||
|
Outcome::Failure((Status::InternalServerError, ()))
|
||||||
|
},
|
||||||
|
Ok(c) => Outcome::Success(Box::new(DnsConnectorClient::new(c)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for Box<dyn ZoneConnector> {
|
||||||
|
type Error = ();
|
||||||
|
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
|
let config = try_outcome!(request.guard::<&State<Config>>().await);
|
||||||
|
match DnsClient::from_config(&config.dns).await {
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to connect to DNS server: {}", e);
|
||||||
|
Outcome::Failure((Status::InternalServerError, ()))
|
||||||
|
},
|
||||||
|
Ok(c) => Outcome::Success(Box::new(DnsConnectorClient::new(c)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue