Compare commits
2 Commits
083976935c
...
92f7d3d52a
Author | SHA1 | Date | |
---|---|---|---|
|
92f7d3d52a | ||
|
818c728258 |
26
README.md
26
README.md
@ -64,10 +64,17 @@ domain:
|
||||
# renewed.
|
||||
#contact_email: REQUIRED if service.http.https_addr is set
|
||||
|
||||
# The domain name which will be used to serve the web interface of Domani. If
|
||||
# service.http.https_addr is enabled then an HTTPS certificate for this domain
|
||||
# will be retrieved automatically.
|
||||
#
|
||||
# This can be set to null to disable the web interface entirely.
|
||||
#interface_domain: "localhost"
|
||||
|
||||
# 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:
|
||||
#builtin_domains:
|
||||
|
||||
# An example built-in domain backed by a git repo.
|
||||
#git.example.com:
|
||||
@ -79,13 +86,14 @@ domain:
|
||||
# domain list, but will not be configurable in the web interface
|
||||
#public: false
|
||||
|
||||
#proxied:
|
||||
#proxied_domains:
|
||||
|
||||
# An example built-in domain backed by an gemini and HTTP reverse-proxies to
|
||||
# An example proxied domain backed by an gemini and HTTP reverse-proxies to
|
||||
# other backends.
|
||||
#
|
||||
# HTTP requests will be proxied to http_url, and gemini requests will be
|
||||
# proxied to gemini_url. Either can be null.
|
||||
# proxied to gemini_url. Either can be null to disable serving on that
|
||||
# protocol.
|
||||
#
|
||||
# HTTP requests to the backing service will automatically have
|
||||
# X-Forwarded-For and (if HTTPS) X-Forwarded-Proto headers added to them.
|
||||
@ -106,7 +114,8 @@ domain:
|
||||
# - name: X-HEADER-TO-DELETE
|
||||
# value: ""
|
||||
|
||||
# Set to true to prevent the domain from being served over https.
|
||||
# Set to true to prevent the domain from being served over https, even if
|
||||
# http_url is set.
|
||||
#https_disabled: false
|
||||
|
||||
service:
|
||||
@ -132,13 +141,6 @@ service:
|
||||
#- kind: CNAME
|
||||
# name: domain.com
|
||||
|
||||
# The domain name which will be used to serve the web interface of Domani. If
|
||||
# service.http.https_addr is enabled then an HTTPS certificate for this domain
|
||||
# will be retrieved automatically.
|
||||
#
|
||||
# This can be set to null to disable the web interface entirely.
|
||||
#interface_domain: "localhost"
|
||||
|
||||
#http:
|
||||
|
||||
# The address to listen for HTTP requests on. This must use port 80 if
|
||||
|
@ -9,7 +9,11 @@ fn default_resolver_addr() -> net::SocketAddr {
|
||||
net::SocketAddr::from_str("1.1.1.1:53").unwrap()
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
fn default_interface_domain() -> Option<domain::Name> {
|
||||
Some(domain::Name::from_str("localhost").unwrap())
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct ConfigDNS {
|
||||
#[serde(default = "default_resolver_addr")]
|
||||
pub resolver_addr: net::SocketAddr,
|
||||
@ -23,7 +27,7 @@ impl Default for ConfigDNS {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct ConfigACME {
|
||||
pub contact_email: String,
|
||||
}
|
||||
@ -54,7 +58,7 @@ pub struct ConfigProxiedDomain {
|
||||
pub https_disabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
pub store_dir_path: path::PathBuf,
|
||||
#[serde(default)]
|
||||
@ -62,8 +66,11 @@ pub struct Config {
|
||||
pub acme: Option<ConfigACME>,
|
||||
|
||||
#[serde(default)]
|
||||
pub builtins: collections::HashMap<domain::Name, ConfigBuiltinDomain>,
|
||||
pub builtin_domains: collections::HashMap<domain::Name, ConfigBuiltinDomain>,
|
||||
|
||||
#[serde(default)]
|
||||
pub proxied: collections::HashMap<domain::Name, ConfigProxiedDomain>,
|
||||
pub proxied_domains: collections::HashMap<domain::Name, ConfigProxiedDomain>,
|
||||
|
||||
#[serde(default = "default_interface_domain")]
|
||||
pub interface_domain: Option<domain::Name>,
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
use crate::domain::{self, acme, checker, config, gemini, store, tls};
|
||||
use crate::domain::{self, acme, checker, gemini, store, tls};
|
||||
use crate::error::unexpected::{self, Mappable};
|
||||
use crate::{origin, task_stack, util};
|
||||
|
||||
use std::{collections, sync};
|
||||
use std::sync;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
pub enum GetSettingsResult {
|
||||
Stored(domain::Settings),
|
||||
Builtin(domain::config::ConfigBuiltinDomain),
|
||||
Proxied(domain::config::ConfigProxiedDomain),
|
||||
Interface,
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
@ -153,8 +154,7 @@ pub struct ManagerImpl {
|
||||
domain_checker: checker::DNSChecker,
|
||||
acme_manager: Option<Box<dyn acme::manager::Manager + Send + Sync>>,
|
||||
gemini_store: Option<Box<dyn gemini::Store + Send + Sync>>,
|
||||
builtins: collections::HashMap<domain::Name, config::ConfigBuiltinDomain>,
|
||||
proxied: collections::HashMap<domain::Name, config::ConfigProxiedDomain>,
|
||||
config: domain::Config,
|
||||
}
|
||||
|
||||
impl ManagerImpl {
|
||||
@ -170,8 +170,7 @@ impl ManagerImpl {
|
||||
domain_checker: checker::DNSChecker,
|
||||
acme_manager: Option<AcmeManager>,
|
||||
gemini_store: Option<GeminiStore>,
|
||||
builtins: collections::HashMap<domain::Name, config::ConfigBuiltinDomain>,
|
||||
proxied: collections::HashMap<domain::Name, config::ConfigProxiedDomain>,
|
||||
config: domain::Config,
|
||||
) -> sync::Arc<Self> {
|
||||
let manager = sync::Arc::new(ManagerImpl {
|
||||
origin_store: Box::from(origin_store),
|
||||
@ -180,8 +179,7 @@ impl ManagerImpl {
|
||||
acme_manager: acme_manager
|
||||
.map(|m| Box::new(m) as Box<dyn acme::manager::Manager + Send + Sync>),
|
||||
gemini_store: gemini_store.map(|m| Box::new(m) as Box<dyn gemini::Store + Send + Sync>),
|
||||
builtins,
|
||||
proxied,
|
||||
config,
|
||||
});
|
||||
|
||||
task_stack.push_spawn(|canceller| {
|
||||
@ -247,6 +245,8 @@ impl ManagerImpl {
|
||||
// A proxied domain never needs gemini certs, since gemini requests will be
|
||||
// transparently proxied to the backing server anyway.
|
||||
GetSettingsResult::Proxied(config) => (None, !config.https_disabled, false),
|
||||
|
||||
GetSettingsResult::Interface => (None, true, false),
|
||||
};
|
||||
|
||||
if let Some(settings) = settings {
|
||||
@ -289,11 +289,15 @@ impl ManagerImpl {
|
||||
|
||||
impl Manager for ManagerImpl {
|
||||
fn get_settings(&self, domain: &domain::Name) -> Result<GetSettingsResult, GetSettingsError> {
|
||||
if let Some(config) = self.builtins.get(domain) {
|
||||
if Some(domain) == self.config.interface_domain.as_ref() {
|
||||
return Ok(GetSettingsResult::Interface);
|
||||
}
|
||||
|
||||
if let Some(config) = self.config.builtin_domains.get(domain) {
|
||||
return Ok(GetSettingsResult::Builtin(config.clone()));
|
||||
}
|
||||
|
||||
if let Some(config) = self.proxied.get(domain) {
|
||||
if let Some(config) = self.config.proxied_domains.get(domain) {
|
||||
return Ok(GetSettingsResult::Proxied(config.clone()));
|
||||
}
|
||||
|
||||
@ -313,6 +317,11 @@ impl Manager for ManagerImpl {
|
||||
unexpected::Error::from("can't call get_file on proxied domain").into(),
|
||||
);
|
||||
}
|
||||
GetSettingsResult::Interface => {
|
||||
return Err(
|
||||
unexpected::Error::from("can't call get_file on interface domain").into(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let path = settings.process_path(path);
|
||||
@ -330,7 +339,11 @@ impl Manager for ManagerImpl {
|
||||
settings: domain::Settings,
|
||||
) -> util::BoxFuture<'mgr, Result<(), SyncWithSettingsError>> {
|
||||
Box::pin(async move {
|
||||
if self.builtins.contains_key(&domain) || self.proxied.contains_key(&domain) {
|
||||
let is_interface = Some(&domain) == self.config.interface_domain.as_ref();
|
||||
let is_builtin = self.config.builtin_domains.contains_key(&domain);
|
||||
let is_proxied = self.config.proxied_domains.contains_key(&domain);
|
||||
|
||||
if is_interface || is_builtin || is_proxied {
|
||||
return Err(SyncWithSettingsError::NotModifiable);
|
||||
}
|
||||
|
||||
@ -384,7 +397,8 @@ impl Manager for ManagerImpl {
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.builtins
|
||||
self.config
|
||||
.builtin_domains
|
||||
.iter()
|
||||
.map(|(domain, config)| ManagedDomain {
|
||||
domain: domain.clone(),
|
||||
@ -392,7 +406,8 @@ impl Manager for ManagerImpl {
|
||||
})
|
||||
.collect_into(&mut res);
|
||||
|
||||
self.proxied
|
||||
self.config
|
||||
.proxied_domains
|
||||
.iter()
|
||||
.map(|(domain, _)| ManagedDomain {
|
||||
domain: domain.clone(),
|
||||
@ -400,6 +415,13 @@ impl Manager for ManagerImpl {
|
||||
})
|
||||
.collect_into(&mut res);
|
||||
|
||||
if let Some(ref interface_domain) = self.config.interface_domain {
|
||||
res.push(ManagedDomain {
|
||||
domain: interface_domain.clone(),
|
||||
public: false,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
11
src/main.rs
11
src/main.rs
@ -60,7 +60,7 @@ async fn main() {
|
||||
// Since the interface domain _must_ point to the service (otherwise it wouldn't work) it's
|
||||
// reasonable to assume that a CNAME on any domain would suffice to point that domain to
|
||||
// the service.
|
||||
if let Some(ref interface_domain) = config.service.interface_domain {
|
||||
if let Some(ref interface_domain) = config.domain.interface_domain {
|
||||
let interface_cname = domani::service::ConfigDNSRecord::CNAME {
|
||||
name: interface_domain.clone(),
|
||||
};
|
||||
@ -106,6 +106,7 @@ async fn main() {
|
||||
let acme_config = config
|
||||
.domain
|
||||
.acme
|
||||
.clone()
|
||||
.expect("acme configuration must be set if https is enabled");
|
||||
|
||||
let domain_acme_store =
|
||||
@ -143,8 +144,7 @@ async fn main() {
|
||||
domain_checker,
|
||||
domain_acme_manager,
|
||||
domain_gemini_store,
|
||||
config.domain.builtins.clone(),
|
||||
config.domain.proxied.clone(),
|
||||
config.domain.clone(),
|
||||
);
|
||||
|
||||
let _ = domani::service::http::Service::new(
|
||||
@ -152,7 +152,8 @@ async fn main() {
|
||||
domain_manager.clone(),
|
||||
domani::domain::manager::HttpsCertResolver::from(domain_manager.clone()),
|
||||
config.service.clone(),
|
||||
config.domain.proxied.clone(),
|
||||
config.domain.proxied_domains.clone(),
|
||||
config.domain.interface_domain.clone(),
|
||||
);
|
||||
|
||||
if gemini_enabled {
|
||||
@ -161,7 +162,7 @@ async fn main() {
|
||||
domain_manager.clone(),
|
||||
domani::domain::manager::GeminiCertResolver::from(domain_manager.clone()),
|
||||
config.service.gemini.clone(),
|
||||
config.domain.proxied.clone(),
|
||||
config.domain.proxied_domains.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,6 @@
|
||||
use crate::{domain, service};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{net, str::FromStr};
|
||||
|
||||
fn default_interface_domain() -> Option<domain::Name> {
|
||||
Some(domain::Name::from_str("localhost").unwrap())
|
||||
}
|
||||
use std::net;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(tag = "kind")]
|
||||
@ -31,9 +27,6 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub dns_records: Vec<ConfigDNSRecord>,
|
||||
|
||||
#[serde(default = "default_interface_domain")]
|
||||
pub interface_domain: Option<domain::Name>,
|
||||
|
||||
#[serde(default)]
|
||||
pub http: service::http::Config,
|
||||
|
||||
|
@ -15,14 +15,6 @@ use std::{collections, future, net, sync};
|
||||
use crate::error::unexpected;
|
||||
use crate::{domain, service, task_stack};
|
||||
|
||||
pub struct Service {
|
||||
domain_manager: sync::Arc<dyn domain::manager::Manager>,
|
||||
cert_resolver: sync::Arc<dyn rustls::server::ResolvesServerCert>,
|
||||
handlebars: handlebars::Handlebars<'static>,
|
||||
config: service::Config,
|
||||
proxied: collections::HashMap<domain::Name, domain::ConfigProxiedDomain>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct BasePresenter<'a, T> {
|
||||
page_name: &'a str,
|
||||
@ -52,13 +44,23 @@ struct DomainSyncArgs {
|
||||
url_encoded_domain_settings: util::UrlEncodedDomainSettings,
|
||||
}
|
||||
|
||||
pub struct Service {
|
||||
domain_manager: sync::Arc<dyn domain::manager::Manager>,
|
||||
cert_resolver: sync::Arc<dyn rustls::server::ResolvesServerCert>,
|
||||
handlebars: handlebars::Handlebars<'static>,
|
||||
config: service::Config,
|
||||
proxied_domains: collections::HashMap<domain::Name, domain::ConfigProxiedDomain>,
|
||||
interface_domain: Option<domain::Name>,
|
||||
}
|
||||
|
||||
impl Service {
|
||||
pub fn new<CertResolver>(
|
||||
task_stack: &mut task_stack::TaskStack<unexpected::Error>,
|
||||
domain_manager: sync::Arc<dyn domain::manager::Manager>,
|
||||
cert_resolver: CertResolver,
|
||||
config: service::Config,
|
||||
proxied: collections::HashMap<domain::Name, domain::ConfigProxiedDomain>,
|
||||
proxied_domains: collections::HashMap<domain::Name, domain::ConfigProxiedDomain>,
|
||||
interface_domain: Option<domain::Name>,
|
||||
) -> sync::Arc<Service>
|
||||
where
|
||||
CertResolver: rustls::server::ResolvesServerCert + 'static,
|
||||
@ -70,7 +72,8 @@ impl Service {
|
||||
cert_resolver: sync::Arc::from(cert_resolver),
|
||||
handlebars: tpl::get(),
|
||||
config,
|
||||
proxied,
|
||||
proxied_domains,
|
||||
interface_domain,
|
||||
});
|
||||
|
||||
task_stack.push_spawn(|canceller| tasks::listen_http(service.clone(), canceller));
|
||||
@ -224,6 +227,7 @@ impl Service {
|
||||
config.public.then(|| config.settings)
|
||||
}
|
||||
Ok(domain::manager::GetSettingsResult::Proxied(_)) => None,
|
||||
Ok(domain::manager::GetSettingsResult::Interface) => None,
|
||||
Err(domain::manager::GetSettingsError::NotFound) => None,
|
||||
Err(domain::manager::GetSettingsError::Unexpected(e)) => {
|
||||
return self.internal_error(
|
||||
@ -457,7 +461,7 @@ impl Service {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(config) = self.proxied.get(&domain) {
|
||||
if let Some(config) = self.proxied_domains.get(&domain) {
|
||||
if let Some(ref http_url) = config.http_url {
|
||||
return service::http::proxy::serve_http_request(
|
||||
http_url.original_url.as_str(),
|
||||
@ -478,7 +482,7 @@ impl Service {
|
||||
}
|
||||
}
|
||||
|
||||
if Some(&domain) == self.config.interface_domain.as_ref() {
|
||||
if Some(&domain) == self.interface_domain.as_ref() {
|
||||
return self.serve_interface(req).await;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@ pub async fn listen_http(
|
||||
|
||||
// only used for logging
|
||||
let listen_host = service
|
||||
.config
|
||||
.interface_domain
|
||||
.clone()
|
||||
.map_or(addr.ip().to_string(), |ref d| d.as_str().to_string());
|
||||
@ -56,7 +55,6 @@ pub async fn listen_https(
|
||||
|
||||
// only used for logging
|
||||
let listen_host = service
|
||||
.config
|
||||
.interface_domain
|
||||
.clone()
|
||||
.map_or(addr.ip().to_string(), |ref d| d.as_str().to_string());
|
||||
|
Loading…
Reference in New Issue
Block a user