Add builtin domains to configuration
This commit is contained in:
parent
03428cef02
commit
4483185e75
@ -5,7 +5,7 @@ domain:
|
||||
service:
|
||||
passphrase: foobar
|
||||
dns_records:
|
||||
- type: A
|
||||
- kind: A
|
||||
addr: 127.0.0.1
|
||||
- type: AAAA
|
||||
- kind: AAAA
|
||||
addr: ::1
|
||||
|
19
README.md
19
README.md
@ -53,6 +53,18 @@ domain:
|
||||
# renewed.
|
||||
#contact_email: REQUIRED if service.http.https_addr is set
|
||||
|
||||
# builtins are domains whose configuration is built into domani. These domains
|
||||
# are not able to be configured via the web interface, and will be hidden from
|
||||
# it unless the `public` key is set to true.
|
||||
#builtins:
|
||||
|
||||
# An example built-in domain backed by a git repo.
|
||||
#example.com:
|
||||
# kind: git
|
||||
# url: "https://somewhere.com/some/repo.git"
|
||||
# branch: main
|
||||
# public: false
|
||||
|
||||
service:
|
||||
|
||||
# Passphrase which must be given by users who are configuring new domains via
|
||||
@ -66,14 +78,14 @@ service:
|
||||
# A CNAME record with the primary_domain of this server is automatically
|
||||
# included.
|
||||
dns_records:
|
||||
#- type: A
|
||||
#- kind: A
|
||||
# addr: 127.0.0.1
|
||||
|
||||
#- type: AAAA
|
||||
#- kind: AAAA
|
||||
# addr: ::1
|
||||
|
||||
# NOTE that the name given here must resolve to the Domani server.
|
||||
#- type: CNAME
|
||||
#- kind: CNAME
|
||||
# name: domain.com
|
||||
|
||||
# The domain name which will be used to serve the web interface of Domani. If
|
||||
@ -126,5 +138,6 @@ Within the shell which opens you can do `cargo run` to start a local instance.
|
||||
* Support for more backends than just git repositories, including:
|
||||
* IPFS/IPNS
|
||||
* Alternative URLs (reverse proxy)
|
||||
* Small static files (e.g. for well-knowns)
|
||||
* Google Drive
|
||||
* Dropbox
|
||||
|
@ -1,7 +1,9 @@
|
||||
use std::{net, path, str::FromStr};
|
||||
use std::{collections, net, path, str::FromStr};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::domain;
|
||||
|
||||
fn default_resolver_addr() -> net::SocketAddr {
|
||||
net::SocketAddr::from_str("1.1.1.1:53").unwrap()
|
||||
}
|
||||
@ -25,10 +27,19 @@ pub struct ConfigACME {
|
||||
pub contact_email: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BuiltinDomain {
|
||||
#[serde(flatten)]
|
||||
pub domain: domain::Domain,
|
||||
|
||||
pub public: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Config {
|
||||
pub store_dir_path: path::PathBuf,
|
||||
#[serde(default)]
|
||||
pub dns: ConfigDNS,
|
||||
pub acme: Option<ConfigACME>,
|
||||
pub builtins: collections::HashMap<domain::Name, BuiltinDomain>,
|
||||
}
|
||||
|
@ -80,6 +80,9 @@ impl From<store::GetError> for SyncError {
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum SyncWithConfigError {
|
||||
#[error("cannot call SyncWithConfig on builtin domain")]
|
||||
BuiltinDomain,
|
||||
|
||||
#[error("invalid url")]
|
||||
InvalidURL,
|
||||
|
||||
@ -127,6 +130,7 @@ impl From<checker::CheckDomainError> for SyncWithConfigError {
|
||||
impl From<store::SetError> for SyncWithConfigError {
|
||||
fn from(e: store::SetError) -> SyncWithConfigError {
|
||||
match e {
|
||||
store::SetError::BuiltinDomain => SyncWithConfigError::BuiltinDomain,
|
||||
store::SetError::Unexpected(e) => SyncWithConfigError::Unexpected(e),
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::str::FromStr;
|
||||
use std::{cmp, fmt};
|
||||
use std::{cmp, fmt, hash};
|
||||
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use trust_dns_client::rr as trust_dns_rr;
|
||||
@ -7,7 +7,7 @@ use trust_dns_client::rr as trust_dns_rr;
|
||||
#[derive(Debug, Clone)]
|
||||
/// Validated representation of a domain name
|
||||
pub struct Name {
|
||||
inner: trust_dns_rr::Name,
|
||||
rr: trust_dns_rr::Name,
|
||||
utf8_str: String,
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ impl Name {
|
||||
}
|
||||
|
||||
pub fn as_rr(&self) -> &trust_dns_rr::Name {
|
||||
&self.inner
|
||||
&self.rr
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,13 +36,21 @@ impl FromStr for Name {
|
||||
|
||||
n.set_fqdn(true);
|
||||
|
||||
Ok(Name { inner: n, utf8_str })
|
||||
Ok(Name { rr: n, utf8_str })
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialEq for Name {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.inner == other.inner
|
||||
self.rr == other.rr
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::Eq for Name {}
|
||||
|
||||
impl hash::Hash for Name {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.rr.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
use std::path;
|
||||
use std::str::FromStr;
|
||||
use std::{fs, io};
|
||||
use std::{collections, fs, io, path, str::FromStr};
|
||||
|
||||
use crate::domain;
|
||||
use crate::error::unexpected::{self, Intoable, Mappable};
|
||||
@ -16,6 +14,9 @@ pub enum GetError {
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum SetError {
|
||||
#[error("cannot call set on builtin domain")]
|
||||
BuiltinDomain,
|
||||
|
||||
#[error(transparent)]
|
||||
Unexpected(#[from] unexpected::Error),
|
||||
}
|
||||
@ -96,6 +97,51 @@ impl Store for FSStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StoreWithBuiltin<S: Store> {
|
||||
inner: S,
|
||||
domains: collections::HashMap<domain::Name, domain::config::BuiltinDomain>,
|
||||
}
|
||||
|
||||
impl<S: Store> StoreWithBuiltin<S> {
|
||||
pub fn new(
|
||||
inner: S,
|
||||
builtin_domains: collections::HashMap<domain::Name, domain::config::BuiltinDomain>,
|
||||
) -> StoreWithBuiltin<S> {
|
||||
StoreWithBuiltin {
|
||||
inner,
|
||||
domains: builtin_domains,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Store> Store for StoreWithBuiltin<S> {
|
||||
fn get(&self, domain: &domain::Name) -> Result<domain::Domain, GetError> {
|
||||
if let Some(domain) = self.domains.get(domain) {
|
||||
return Ok(domain.domain.clone());
|
||||
}
|
||||
self.inner.get(domain)
|
||||
}
|
||||
|
||||
fn set(&self, domain: &domain::Name, config: &domain::Domain) -> Result<(), SetError> {
|
||||
if self.domains.get(domain).is_some() {
|
||||
return Err(SetError::BuiltinDomain);
|
||||
}
|
||||
self.inner.set(domain, config)
|
||||
}
|
||||
|
||||
fn all_domains(&self) -> Result<Vec<domain::Name>, unexpected::Error> {
|
||||
let inner_domains = self.inner.all_domains()?;
|
||||
let mut domains: Vec<domain::Name> = self
|
||||
.domains
|
||||
.iter()
|
||||
.filter(|(_, v)| v.public)
|
||||
.map(|(k, _)| k.clone())
|
||||
.collect();
|
||||
domains.extend(inner_domains);
|
||||
Ok(domains)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Store, *};
|
||||
|
@ -89,10 +89,13 @@ async fn main() {
|
||||
.await
|
||||
.expect("domain checker initialization failed");
|
||||
|
||||
let domain_config_store =
|
||||
let domain_store =
|
||||
domani::domain::store::FSStore::new(&config.domain.store_dir_path.join("domains"))
|
||||
.expect("domain config store initialization failed");
|
||||
|
||||
let domain_store =
|
||||
domani::domain::store::StoreWithBuiltin::new(domain_store, config.domain.builtins);
|
||||
|
||||
let domain_acme_manager = if config.service.http.https_addr.is_some() {
|
||||
let acme_config = config
|
||||
.domain
|
||||
@ -121,7 +124,7 @@ async fn main() {
|
||||
let domain_manager = domani::domain::manager::ManagerImpl::new(
|
||||
&mut task_stack,
|
||||
origin_store,
|
||||
domain_config_store,
|
||||
domain_store,
|
||||
domain_checker,
|
||||
domain_acme_manager,
|
||||
);
|
||||
|
@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind")]
|
||||
/// A unique description of an origin, from where a domain might be served.
|
||||
pub enum Descr {
|
||||
Git { url: String, branch_name: String },
|
||||
|
@ -10,7 +10,7 @@ fn default_primary_domain() -> domain::Name {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum ConfigDNSRecord {
|
||||
A { addr: net::Ipv4Addr },
|
||||
AAAA { addr: net::Ipv6Addr },
|
||||
|
@ -302,6 +302,7 @@ impl<'svc> Service {
|
||||
|
||||
let error_msg = match sync_result {
|
||||
Ok(_) => None,
|
||||
Err(domain::manager::SyncWithConfigError::BuiltinDomain) => Some("This domain is not able to be configured, please contact the server administrator.".to_string()),
|
||||
Err(domain::manager::SyncWithConfigError::InvalidURL) => Some("Fetching the git repository failed, please double check that you input the correct URL.".to_string()),
|
||||
Err(domain::manager::SyncWithConfigError::InvalidBranchName) => Some("The git repository does not have a branch of the given name, please double check that you input the correct name.".to_string()),
|
||||
Err(domain::manager::SyncWithConfigError::AlreadyInProgress) => Some("The configuration of your domain is still in progress, please refresh in a few minutes.".to_string()),
|
||||
|
Loading…
Reference in New Issue
Block a user