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"
|
||||
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]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.0.1"
|
||||
|
@ -983,6 +998,33 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.0.0"
|
||||
|
@ -1802,6 +1844,7 @@ dependencies = [
|
|||
"futures-util",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"openssl",
|
||||
"radix_trie",
|
||||
"rand",
|
||||
"thiserror",
|
||||
|
@ -1827,6 +1870,7 @@ dependencies = [
|
|||
"ipnet",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"openssl",
|
||||
"rand",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
|
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
trust-dns-client = { version = "0.21", features = ["dnssec"] }
|
||||
trust-dns-client = { version = "0.21", features = ["dnssec-openssl"] }
|
||||
trust-dns-proto = "0.21"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
|
|
@ -5,8 +5,14 @@ log:
|
|||
- target: stderr
|
||||
any: debug
|
||||
|
||||
key:
|
||||
- id: dev
|
||||
algorithm: hmac-sha256
|
||||
secret: mbmz4J3Efm1BUjqe12M1RHsOnPjYhKQe+2iKO4tL+a4=
|
||||
|
||||
acl:
|
||||
- id: example_acl
|
||||
key: dev
|
||||
address: [ 127.0.0.1, ::1]
|
||||
action: [transfer, update]
|
||||
|
||||
|
|
102
src/config.rs
102
src/config.rs
|
@ -1,13 +1,11 @@
|
|||
use std::net::SocketAddr;
|
||||
use std::time::Duration as StdDuration;
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use chrono::Duration;
|
||||
use chrono::Duration as ChronoDuration;
|
||||
|
||||
|
||||
use rocket::{Request, State, http::Status, request::{FromRequest, Outcome}};
|
||||
use rocket::outcome::try_outcome;
|
||||
|
||||
use crate::dns::{DnsClient, DnsConnectorClient, RecordConnector, ZoneConnector};
|
||||
use crate::models::name::SerdeName;
|
||||
use crate::dns::TsigAlgorithm;
|
||||
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
@ -18,66 +16,70 @@ pub struct Config {
|
|||
|
||||
#[derive(Debug, Deserialize)]
|
||||
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)]
|
||||
pub struct WebAppConfig {
|
||||
#[serde(deserialize_with = "from_duration")]
|
||||
pub token_duration: Duration,
|
||||
#[serde(deserialize_with = "from_chrono_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>
|
||||
{
|
||||
use serde::de::Error;
|
||||
String::deserialize(deserializer)
|
||||
.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
|
||||
#[rocket::async_trait]
|
||||
impl<'r> FromRequest<'r> for DnsClient {
|
||||
type Error = ();
|
||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||
let config = try_outcome!(request.guard::<&State<Config>>().await);
|
||||
match DnsClient::new(config.dns.server).await {
|
||||
Err(e) => {
|
||||
error!("Failed to connect to DNS server: {}", e);
|
||||
Outcome::Failure((Status::InternalServerError, ()))
|
||||
},
|
||||
Ok(c) => Outcome::Success(c)
|
||||
}
|
||||
}
|
||||
fn from_chrono_duration<'de, D>(deserializer: D) -> Result<ChronoDuration, D::Error>
|
||||
where D: Deserializer<'de>
|
||||
{
|
||||
use serde::de::Error;
|
||||
String::deserialize(deserializer)
|
||||
.and_then(|string| humantime::parse_duration(&string).map_err(|err| Error::custom(err.to_string())))
|
||||
.and_then(|duration| ChronoDuration::from_std(duration).map_err(|err| Error::custom(err.to_string())))
|
||||
}
|
||||
|
||||
#[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::new(config.dns.server).await {
|
||||
Err(e) => {
|
||||
error!("Failed to connect to DNS server: {}", e);
|
||||
Outcome::Failure((Status::InternalServerError, ()))
|
||||
},
|
||||
Ok(c) => Outcome::Success(Box::new(DnsConnectorClient::new(c)))
|
||||
}
|
||||
}
|
||||
|
||||
fn from_base64<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
|
||||
where D: Deserializer<'de>
|
||||
{
|
||||
use serde::de::Error;
|
||||
String::deserialize(deserializer)
|
||||
.and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string())))
|
||||
}
|
||||
|
||||
#[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::new(config.dns.server).await {
|
||||
Err(e) => {
|
||||
error!("Failed to connect to DNS server: {}", e);
|
||||
Outcome::Failure((Status::InternalServerError, ()))
|
||||
},
|
||||
Ok(c) => Outcome::Success(Box::new(DnsConnectorClient::new(c)))
|
||||
}
|
||||
fn from_tsigalg<'de, D>(deserializer: D) -> Result<TsigAlgorithm, D::Error>
|
||||
where D: Deserializer<'de>
|
||||
{
|
||||
use serde::de::Error;
|
||||
|
||||
let algo = match String::deserialize(deserializer)?.as_str() {
|
||||
"hmac-sha256" => TsigAlgorithm::HmacSha256,
|
||||
"hmac-sha384" => TsigAlgorithm::HmacSha384,
|
||||
"hmac-sha512" => TsigAlgorithm::HmacSha512,
|
||||
_ => return Err(Error::custom("Unsupported mac algorithm"))
|
||||
};
|
||||
|
||||
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::sync::Arc;
|
||||
use futures_util::{ready, Stream, StreamExt};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use tokio::{net::TcpStream as TokioTcpStream, task};
|
||||
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::iocompat::AsyncIoTokioAsStd;
|
||||
|
||||
use crate::config::DnsClientConfig;
|
||||
|
||||
|
||||
pub struct DnsClient(AsyncClient);
|
||||
|
||||
|
@ -26,13 +30,25 @@ impl DerefMut for DnsClient {
|
|||
}
|
||||
|
||||
impl DnsClient {
|
||||
pub async fn new(addr: SocketAddr) -> Result<Self, ProtoError> {
|
||||
let (stream, handle) = TcpClientStream::<AsyncIoTokioAsStd<TokioTcpStream>>::new(addr);
|
||||
pub async fn from_config(dns_config: &DnsClientConfig) -> Result<Self, ProtoError> {
|
||||
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(
|
||||
stream,
|
||||
handle,
|
||||
std::time::Duration::from_secs(5),
|
||||
None);
|
||||
dns_config.timeout,
|
||||
signer,
|
||||
);
|
||||
let (client, bg) = client.await?;
|
||||
task::spawn(bg);
|
||||
return Ok(DnsClient(client))
|
||||
|
@ -61,3 +77,4 @@ where
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,45 @@ pub use trust_dns_client::rr::{
|
|||
RData, DNSClass, Record
|
||||
};
|
||||
pub use trust_dns_proto::rr::Name;
|
||||
pub use trust_dns_proto::rr::dnssec::rdata::tsig::TsigAlgorithm;
|
||||
|
||||
// Reexport module types
|
||||
pub use connector::{RecordConnector, ZoneConnector, ConnectorError};
|
||||
pub use dns_connector::{DnsConnectorClient, DnsConnectorError};
|
||||
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