Allow for disabling https for proxied domains

This commit is contained in:
Brian Picciano 2023-08-03 14:54:59 +02:00
parent 7a1a2297d4
commit dcbf45ec85
4 changed files with 69 additions and 51 deletions

View File

@ -79,6 +79,36 @@ domain:
# domain list, but will not be configurable in the web interface
#public: false
#proxied:
# An example built-in 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.
#
# HTTP requests to the backing service will automatically have
# X-Forwarded-For and (if HTTPS) X-Forwarded-Proto headers added to them.
#
# Proxies are currently limited in the following ways:
# * http_url must be to an http endpoint (not https)
# * dns.resolver_addr is ignored and the system-wide dns is used
#
#example.com:
#http_url: "http://some.other.service.com"
#gemini_url: "gemini://some.other.service.com"
# Extra headers to add to proxied requests
#http_request_headers:
# - name: Host
# value: "yet.another.service.com"
# - name: X-HEADER-TO-DELETE
# value: ""
# Set to true to prevent the domain from being served over https.
#https_disabled: false
service:
# Passphrase which must be given by users who are configuring new domains via
@ -91,7 +121,7 @@ service:
#
# A CNAME record with the interface_domain of this server is automatically
# included, if it's not null itself.
dns_records:
#dns_records:
#- kind: A
# addr: 127.0.0.1
@ -119,45 +149,11 @@ service:
# enabled. You can enable HTTPS by setting this to "[::]:443".
#https_addr: null
#proxied_domains:
# An example built-in domain backed by an HTTP reverse-proxy to some
# other web-service. Requests to the backing service will automatically
# have X-Forwarded-For and (if HTTPS) X-Forwarded-Proto headers added to
# them.
#
# Proxies are currently limited in the following ways:
# * url must be to an http endpoint (not https)
# * dns.resolver_addr is ignored and the system-wide dns is used
#
#proxy.example.com:
# url: "http://some.other.service.com"
#
# # Extra headers to add to proxied requests
# request_headers:
# - name: Host
# value: "yet.another.service.com"
# - name: X-HEADER-TO-DELETE
# value: ""
#gemini:
# The address to listen for gemini requests on. Set this to null to disable
# gemini support.
#gemini_addr: "[::]:3965"
#proxied_domains:
# An example built-in domain backed by a reverse-proxy to some other
# gemini server. Requests to this domain will have connections
# transparently proxied to the backing server.
#
# Proxies are currently limited in the following ways:
# * url must be to a gemini endpoint
# * dns.resolver_addr is ignored and the system-wide dns is used
#
#proxy.example.com:
# url: "gemini://some.other.service.com"
```
The YAML config file can be passed to the Domani process via the `--config-path`

View File

@ -40,17 +40,18 @@ pub struct ConfigBuiltinDomain {
#[serde_as]
#[derive(Clone, Deserialize, Serialize)]
pub struct ConfigProxiedDomain {
#[serde(default)]
#[serde_as(as = "Option<TryFromInto<String>>")]
pub gemini_url: Option<proxied_domain::GeminiUrl>,
#[serde(default)]
#[serde_as(as = "Option<TryFromInto<String>>")]
pub http_url: Option<proxied_domain::HttpUrl>,
#[serde(default)]
#[serde_as(as = "TryFromInto<Vec<proxied_domain::HttpRequestHeader>>")]
pub http_request_headers: proxied_domain::HttpRequestHeaders,
#[serde(default)]
pub https_disabled: bool,
}
#[derive(Deserialize, Serialize)]

View File

@ -187,7 +187,7 @@ impl ManagerImpl {
task_stack.push_spawn(|canceller| {
let manager = manager.clone();
async move {
manager.sync_all_domains(canceller).await;
manager.sync_all_domains_job(canceller).await;
Ok(())
}
});
@ -204,7 +204,7 @@ impl ManagerImpl {
self.origin_store.sync(origin_descr)
}
async fn sync_domain_certs(&self, domain: &domain::Name) -> unexpected::Result<()> {
fn sync_domain_gemini_cert(&self, domain: &domain::Name) -> unexpected::Result<()> {
if let Some(ref gemini_store) = self.gemini_store {
log::info!("Syncing gemini certificate for domain {domain}");
if let Some(_) = gemini_store.get_certificate(domain).or_unexpected()? {
@ -218,7 +218,10 @@ impl ManagerImpl {
gemini_store.set_certificate(domain, pkey, cert)?;
}
Ok(())
}
async fn sync_domain_https_cert(&self, domain: &domain::Name) -> unexpected::Result<()> {
if let Some(ref acme_manager) = self.acme_manager {
log::info!("Syncing HTTPS certificate for domain {domain}");
acme_manager.sync_domain(domain.clone()).await?;
@ -227,20 +230,23 @@ impl ManagerImpl {
Ok(())
}
async fn sync_all_domains_once(&self) -> unexpected::Result<()> {
async fn sync_all_domains(&self) -> unexpected::Result<()> {
let domains = self
.all_domains()
.or_unexpected_while("fetching all domains")?
.into_iter();
for ManagedDomain { domain, .. } in domains {
let settings = match self
let (settings, https_cert, gemini_cert) = match self
.get_settings(&domain)
.map_unexpected_while(|| format!("fetching settings for {domain}"))?
{
GetSettingsResult::Stored(settings) => Some(settings),
GetSettingsResult::Builtin(config) => Some(config.settings),
GetSettingsResult::Proxied(_) => None,
GetSettingsResult::Stored(settings) => (Some(settings), true, true),
GetSettingsResult::Builtin(config) => (Some(config.settings), true, true),
// 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),
};
if let Some(settings) = settings {
@ -253,20 +259,27 @@ impl ManagerImpl {
})?;
}
self.sync_domain_certs(&domain)
.await
.map_unexpected_while(|| format!("syncing certs for domain {domain}",))?;
if gemini_cert {
self.sync_domain_gemini_cert(&domain)
.map_unexpected_while(|| format!("syncing gemini cert for domain {domain}"))?;
}
if https_cert {
self.sync_domain_https_cert(&domain)
.await
.map_unexpected_while(|| format!("syncing https cert for domain {domain}",))?;
}
}
Ok(())
}
async fn sync_all_domains(&self, canceller: CancellationToken) {
async fn sync_all_domains_job(&self, canceller: CancellationToken) {
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(20 * 60));
loop {
tokio::select! {
_ = canceller.cancelled() => return,
_ = interval.tick() => if let Err(err) = self.sync_all_domains_once().await {
_ = interval.tick() => if let Err(err) = self.sync_all_domains().await {
log::error!("Failed to sync all domains: {err}")
},
}
@ -326,10 +339,16 @@ impl Manager for ManagerImpl {
.or_unexpected_while("calculating config hash")?;
self.domain_checker.check_domain(&domain, &hash).await?;
self.sync_domain_origin(&domain, &settings.origin_descr)?;
self.sync_domain_certs(&domain)
self.sync_domain_gemini_cert(&domain)
.or_unexpected_while("syncing domain gemini cert")?;
self.sync_domain_https_cert(&domain)
.await
.or_unexpected_while("syncing domain certs")?;
.or_unexpected_while("syncing domain https cert")?;
self.domain_store.set(&domain, &settings)?;
Ok(())

View File

@ -473,6 +473,8 @@ impl Service {
.as_str(),
)
});
} else {
return self.render_error_page(404, "Domain not found");
}
}