Minimize configuration interface API

This commit is contained in:
Dirkjan Ochtman 2021-12-13 17:06:48 +01:00 committed by masalachai
parent 375a151851
commit 62645eec96
6 changed files with 51 additions and 158 deletions

View File

@ -12,7 +12,6 @@ celes = "2.1"
chrono = "0.4"
quick-xml = { version = "0.22", features = [ "serialize" ] }
rustls = "0.20"
rustls-pemfile = "0.2"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = [ "full" ] }
tokio-rustls = "0.23"

View File

@ -4,8 +4,8 @@
//!
//! ```no_run
//! use std::collections::HashMap;
//! use std::net::ToSocketAddrs;
//!
//! use epp_client::config::{EppClientConfig, RegistryConfig};
//! use epp_client::EppClient;
//! use epp_client::domain::check::DomainCheck;
//! use epp_client::common::NoExtension;
@ -13,20 +13,10 @@
//! #[tokio::main]
//! async fn main() {
//!
//! // Create a config
//! let mut registry: HashMap<String, RegistryConfig> = HashMap::new();
//! registry.insert(
//! "registry_name".to_owned(),
//! RegistryConfig {
//! host: "example.com".to_owned(),
//! port: 700,
//! tls_files: None,
//! },
//! );
//! let config = EppClientConfig { registry };
//!
//! // Create an instance of EppClient, passing the config and the registry you want to connect to
//! let mut client = match EppClient::new(&config, "registry_name").await {
//! // Create an instance of EppClient
//! let host = "example.com";
//! let addr = (host, 7000).to_socket_addrs().unwrap().next().unwrap();
//! let mut client = match EppClient::new("registry_name".to_string(), addr, host, None).await {
//! Ok(client) => client,
//! Err(e) => panic!("Failed to create EppClient: {}", e)
//! };
@ -44,9 +34,9 @@
//! ```
use std::error::Error;
use std::net::SocketAddr;
use crate::common::NoExtension;
use crate::config::EppClientConfig;
use crate::common::{Certificate, NoExtension, PrivateKey};
use crate::connection::EppConnection;
use crate::error;
use crate::hello::{Greeting, GreetingDocument, HelloDocument};
@ -65,16 +55,13 @@ impl EppClient {
/// Creates a new EppClient object and does an EPP Login to a given registry to become ready
/// for subsequent transactions on this client instance
pub async fn new(
config: &EppClientConfig,
registry: &str,
) -> Result<EppClient, Box<dyn Error>> {
let registry_creds = match config.registry(registry) {
Some(creds) => creds,
None => return Err(format!("missing credentials for {}", registry).into()),
};
Ok(EppClient {
connection: EppConnection::connect(registry.to_string(), registry_creds).await?,
registry: String,
addr: SocketAddr,
hostname: &str,
identity: Option<(Vec<Certificate>, PrivateKey)>,
) -> Result<Self, Box<dyn Error>> {
Ok(Self {
connection: EppConnection::connect(registry, addr, hostname, identity).await?,
})
}

View File

@ -358,3 +358,13 @@ impl PostalInfo {
}
}
}
/// This type contains a single DER-encoded X.509 certificate.
///
/// The rustls-pemfile crate can be used to parse a PEM file.
pub struct Certificate(pub Vec<u8>);
/// This type contains a DER-encoded ASN.1 private key in PKCS#8 or PKCS#1 format.
///
/// The rustls-pemfile crate can be used to parse a PEM file in these formats.
pub struct PrivateKey(pub Vec<u8>);

View File

@ -1,59 +0,0 @@
//! Config
//!
//! This module contains the connection configuration for the EPP client.
//!
//! ## Example
//!
//! ```no_run
//! use std::collections::HashMap;
//!
//! use epp_client::config::{EppClientConfig, RegistryConfig};
//!
//! // Create a config
//! let mut registry: HashMap<String, RegistryConfig> = HashMap::new();
//! registry.insert(
//! "registry_name".to_owned(),
//! RegistryConfig {
//! host: "example.com".to_owned(),
//! port: 700,
//! tls_files: None,
//! },
//! );
//! let config = EppClientConfig { registry };
//!
//! // Get configuration for the relevant registry section
//! let registry = config.registry("verisign").unwrap();
//! ```
use std::collections::HashMap;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
/// Paths to the client certificate and client key PEM files
#[derive(Serialize, Deserialize, Debug)]
pub struct EppClientTlsFiles {
pub cert_chain: PathBuf,
pub key: PathBuf,
}
/// Config that stores settings for multiple registries
#[derive(Serialize, Deserialize, Debug)]
pub struct EppClientConfig {
pub registry: HashMap<String, RegistryConfig>,
}
impl EppClientConfig {
/// Returns the config for a particular registry
pub fn registry(&self, registry: &str) -> Option<&RegistryConfig> {
self.registry.get(registry)
}
}
/// Connection details to connect to and authenticate with a registry
#[derive(Serialize, Deserialize, Debug)]
pub struct RegistryConfig {
pub host: String,
pub port: u16,
pub tls_files: Option<EppClientTlsFiles>,
}

View File

@ -1,20 +1,17 @@
//! Manages registry connections and reading/writing to them
use std::convert::TryInto;
use std::fs::File;
use std::io::{BufReader, Seek, SeekFrom};
use std::net::SocketAddr;
use std::sync::Arc;
use std::{error::Error, io as stdio, net::ToSocketAddrs};
use std::{error::Error, io as stdio};
use std::{io, str, u32};
use rustls::{Certificate, PrivateKey};
use rustls::{OwnedTrustAnchor, RootCertStore};
use rustls_pemfile;
use tokio::{io::AsyncReadExt, io::AsyncWriteExt, net::TcpStream};
use tokio_rustls::{client::TlsStream, rustls::ClientConfig, TlsConnector};
use tracing::{debug, info, warn};
use tracing::{debug, info};
use crate::config::RegistryConfig;
use crate::common::{Certificate, PrivateKey};
use crate::error;
/// EPP Connection struct with some metadata for the connection
@ -28,9 +25,11 @@ impl EppConnection {
/// Create an EppConnection instance with the stream to the registry
pub(crate) async fn connect(
registry: String,
config: &RegistryConfig,
addr: SocketAddr,
hostname: &str,
identity: Option<(Vec<Certificate>, PrivateKey)>,
) -> Result<EppConnection, Box<dyn Error>> {
let mut stream = epp_connect(config).await?;
let mut stream = epp_connect(addr, hostname, identity).await?;
let mut buf = vec![0u8; 4096];
stream.read(&mut buf).await?;
@ -121,17 +120,11 @@ impl EppConnection {
/// Establishes a TLS connection to a registry and returns a ConnectionStream instance containing the
/// socket stream to read/write to the connection
async fn epp_connect(
registry_creds: &RegistryConfig,
addr: SocketAddr,
hostname: &str,
identity: Option<(Vec<Certificate>, PrivateKey)>,
) -> Result<TlsStream<TcpStream>, error::Error> {
info!(
"Connecting: EPP Server: {} Port: {}",
registry_creds.host, registry_creds.port
);
let addr = (registry_creds.host.as_str(), registry_creds.port)
.to_socket_addrs()?
.next()
.ok_or(stdio::ErrorKind::NotFound)?;
info!("Connecting to server: {:?}", addr,);
let mut roots = RootCertStore::empty();
roots.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
@ -146,41 +139,15 @@ async fn epp_connect(
.with_safe_defaults()
.with_root_certificates(roots);
let config = match &registry_creds.tls_files {
Some(files) => {
let (certs_file, key_file) = (&files.cert_chain, &files.key);
let certs = rustls_pemfile::certs(&mut BufReader::new(File::open(certs_file)?))?
let config = match identity {
Some((certs, key)) => {
let certs = certs
.into_iter()
.map(Certificate)
.collect::<Vec<_>>();
let mut key;
let mut r = BufReader::new(File::open(key_file).unwrap());
let mut rsa_keys = rustls_pemfile::rsa_private_keys(&mut r).unwrap();
if rsa_keys.len() > 1 {
warn!("Multiple RSA keys found in PEM file {:?}", key_file);
}
key = rsa_keys.pop();
if key.is_none() {
r.seek(SeekFrom::Start(0))?;
let mut pkcs8_keys = rustls_pemfile::pkcs8_private_keys(&mut r).unwrap();
if pkcs8_keys.len() > 1 {
warn!("Multiple PKCS8 keys found in PEM file {:?}", key_file);
}
key = pkcs8_keys.pop();
}
match key {
Some(key) => builder
.with_single_cert(certs, PrivateKey(key))
.map_err(|e| error::Error::Other(e.to_string()))?,
None => {
return Err(error::Error::Other(
"No private key found in PEM file".to_owned(),
))
}
}
.map(|cert| rustls::Certificate(cert.0))
.collect();
builder
.with_single_cert(certs, rustls::PrivateKey(key.0))
.map_err(|e| error::Error::Other(e.to_string()))?
}
None => builder.with_no_client_auth(),
};
@ -188,10 +155,10 @@ async fn epp_connect(
let connector = TlsConnector::from(Arc::new(config));
let stream = TcpStream::connect(&addr).await?;
let domain = registry_creds.host.as_str().try_into().map_err(|_| {
let domain = hostname.try_into().map_err(|_| {
stdio::Error::new(
stdio::ErrorKind::InvalidInput,
format!("Invalid domain: {}", registry_creds.host),
format!("Invalid domain: {}", hostname),
)
})?;

View File

@ -40,8 +40,8 @@
//!
//! ```no_run
//! use std::collections::HashMap;
//! use std::net::ToSocketAddrs;
//!
//! use epp_client::config::{EppClientConfig, RegistryConfig};
//! use epp_client::EppClient;
//! use epp_client::domain::check::DomainCheck;
//! use epp_client::common::NoExtension;
@ -51,20 +51,10 @@
//! #[tokio::main]
//! async fn main() {
//!
//! // Create a config
//! let mut registry: HashMap<String, RegistryConfig> = HashMap::new();
//! registry.insert(
//! "registry_name".to_owned(),
//! RegistryConfig {
//! host: "example.com".to_owned(),
//! port: 700,
//! tls_files: None,
//! },
//! );
//! let config = EppClientConfig { registry };
//!
//! // Create an instance of EppClient, passing the config and the registry you want to connect to
//! let mut client = match EppClient::new(&config, "registry_name").await {
//! // Create an instance of EppClient
//! let host = "example.com";
//! let addr = (host, 7000).to_socket_addrs().unwrap().next().unwrap();
//! let mut client = match EppClient::new("registry_name".to_string(), addr, host, None).await {
//! Ok(client) => client,
//! Err(e) => panic!("Failed to create EppClient: {}", e)
//! };
@ -99,7 +89,6 @@
pub mod client;
pub mod common;
pub mod config;
pub mod connection;
pub mod error;
pub mod hello;