Implement support for AAAA records
This commit is contained in:
parent
7d64f44dab
commit
8600c1050e
@ -7,3 +7,5 @@ service:
|
|||||||
dns_records:
|
dns_records:
|
||||||
- type: A
|
- type: A
|
||||||
addr: 127.0.0.1
|
addr: 127.0.0.1
|
||||||
|
- type: AAAA
|
||||||
|
addr: ::1
|
||||||
|
@ -66,6 +66,9 @@ service:
|
|||||||
#- type: A
|
#- type: A
|
||||||
# addr: 127.0.0.1
|
# addr: 127.0.0.1
|
||||||
|
|
||||||
|
#- type: AAAA
|
||||||
|
# addr: ::1
|
||||||
|
|
||||||
# The domain name which will be used to serve the web interface of Domani. If
|
# 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
|
# service.http.https_addr is enabled then an HTTPS certificate for this domain
|
||||||
# will be retrieved automatically.
|
# will be retrieved automatically.
|
||||||
@ -113,7 +116,6 @@ Within the shell which opens you can do `cargo run` to start a local instance.
|
|||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
* Support for AAAA and CNAME records
|
|
||||||
* Support for more backends than just git repositories, including:
|
* Support for more backends than just git repositories, including:
|
||||||
* IPFS/IPNS
|
* IPFS/IPNS
|
||||||
* Alternative URLs (reverse proxy)
|
* Alternative URLs (reverse proxy)
|
||||||
|
@ -23,9 +23,33 @@ pub enum CheckDomainError {
|
|||||||
|
|
||||||
pub enum DNSRecord {
|
pub enum DNSRecord {
|
||||||
A(net::Ipv4Addr),
|
A(net::Ipv4Addr),
|
||||||
|
AAAA(net::Ipv6Addr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DNSRecord {
|
impl DNSRecord {
|
||||||
|
async fn check_aaaa(
|
||||||
|
client: &mut AsyncClient,
|
||||||
|
domain: &trust_dns_client::rr::Name,
|
||||||
|
addr: &net::Ipv6Addr,
|
||||||
|
) -> Result<bool, unexpected::Error> {
|
||||||
|
let response = client
|
||||||
|
.query(domain.clone(), DNSClass::IN, RecordType::AAAA)
|
||||||
|
.await
|
||||||
|
.or_unexpected_while("querying A record")?;
|
||||||
|
|
||||||
|
let records = response.answers();
|
||||||
|
|
||||||
|
if records.len() != 1 {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the single record isn't a AAAA, or it's not the target AAAA, then return false
|
||||||
|
match records[0].data() {
|
||||||
|
Some(RData::AAAA(remote_addr)) if remote_addr == addr => Ok(true),
|
||||||
|
_ => return Ok(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn check_a(
|
async fn check_a(
|
||||||
client: &mut AsyncClient,
|
client: &mut AsyncClient,
|
||||||
domain: &trust_dns_client::rr::Name,
|
domain: &trust_dns_client::rr::Name,
|
||||||
@ -42,10 +66,9 @@ impl DNSRecord {
|
|||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the single record isn't a A, or it's not the target A, then return
|
// if the single record isn't a A, or it's not the target A, then return false
|
||||||
// TargetANAMENotSet
|
|
||||||
match records[0].data() {
|
match records[0].data() {
|
||||||
Some(RData::A(remote_a)) if remote_a == addr => Ok(true),
|
Some(RData::A(remote_addr)) if remote_addr == addr => Ok(true),
|
||||||
_ => return Ok(false),
|
_ => return Ok(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,6 +80,7 @@ impl DNSRecord {
|
|||||||
) -> Result<bool, unexpected::Error> {
|
) -> Result<bool, unexpected::Error> {
|
||||||
match self {
|
match self {
|
||||||
Self::A(addr) => Self::check_a(client, domain, &addr).await,
|
Self::A(addr) => Self::check_a(client, domain, &addr).await,
|
||||||
|
Self::AAAA(addr) => Self::check_aaaa(client, domain, &addr).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,10 @@ async fn main() {
|
|||||||
|
|
||||||
let domain_checker = {
|
let domain_checker = {
|
||||||
let dns_records = config.service.dns_records.clone();
|
let dns_records = config.service.dns_records.clone();
|
||||||
|
if dns_records.len() == 0 {
|
||||||
|
panic!("service.dns_records must have at least one record defined")
|
||||||
|
}
|
||||||
|
|
||||||
domani::domain::checker::DNSChecker::new(
|
domani::domain::checker::DNSChecker::new(
|
||||||
&config.domain.dns,
|
&config.domain.dns,
|
||||||
dns_records.into_iter().map(|r| r.into()).collect(),
|
dns_records.into_iter().map(|r| r.into()).collect(),
|
||||||
|
@ -2,23 +2,25 @@ pub mod http;
|
|||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use crate::domain;
|
use crate::domain;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{net, str::FromStr};
|
use std::{net, str::FromStr};
|
||||||
|
|
||||||
fn default_primary_domain() -> domain::Name {
|
fn default_primary_domain() -> domain::Name {
|
||||||
domain::Name::from_str("localhost").unwrap()
|
domain::Name::from_str("localhost").unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum ConfigDNSRecord {
|
pub enum ConfigDNSRecord {
|
||||||
A { addr: net::Ipv4Addr },
|
A { addr: net::Ipv4Addr },
|
||||||
|
AAAA { addr: net::Ipv6Addr },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ConfigDNSRecord> for domain::checker::DNSRecord {
|
impl From<ConfigDNSRecord> for domain::checker::DNSRecord {
|
||||||
fn from(r: ConfigDNSRecord) -> Self {
|
fn from(r: ConfigDNSRecord) -> Self {
|
||||||
match r {
|
match r {
|
||||||
ConfigDNSRecord::A { addr } => Self::A(addr),
|
ConfigDNSRecord::A { addr } => Self::A(addr),
|
||||||
|
ConfigDNSRecord::AAAA { addr } => Self::AAAA(addr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use hyper::{Body, Method, Request, Response};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{future, net, sync};
|
use std::{future, sync};
|
||||||
|
|
||||||
use crate::error::unexpected;
|
use crate::error::unexpected;
|
||||||
use crate::{domain, service, util};
|
use crate::{domain, service, util};
|
||||||
@ -225,10 +225,10 @@ impl<'svc> Service {
|
|||||||
domain_config: service::util::FlatConfig,
|
domain_config: service::util::FlatConfig,
|
||||||
) -> Response<Body> {
|
) -> Response<Body> {
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct Response {
|
struct Response<'a> {
|
||||||
domain: domain::Name,
|
domain: domain::Name,
|
||||||
flat_config: service::util::FlatConfig,
|
flat_config: service::util::FlatConfig,
|
||||||
target_a: net::Ipv4Addr,
|
dns_records: &'a [service::ConfigDNSRecord],
|
||||||
challenge_token: String,
|
challenge_token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,21 +249,12 @@ impl<'svc> Service {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_a = match self
|
|
||||||
.config
|
|
||||||
.dns_records
|
|
||||||
.get(0)
|
|
||||||
.expect("at least one target record expected")
|
|
||||||
{
|
|
||||||
service::ConfigDNSRecord::A { addr } => addr.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.render_page(
|
self.render_page(
|
||||||
"/domain_init.html",
|
"/domain_init.html",
|
||||||
Response {
|
Response {
|
||||||
domain: args.domain,
|
domain: args.domain,
|
||||||
flat_config: config.into(),
|
flat_config: config.into(),
|
||||||
target_a: target_a,
|
dns_records: &self.config.dns_records,
|
||||||
challenge_token: config_hash,
|
challenge_token: config_hash,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1,23 +1,9 @@
|
|||||||
<h2>Configure DNS</h2>
|
<h2>Configure DNS</h2>
|
||||||
|
|
||||||
<p>Next you will need to configure your DNS server to point to Domani. There
|
<p>This step requires a passphrase that has been given to you by the
|
||||||
are two entries you will need to add:</p>
|
administrator of the Domani server:</p>
|
||||||
|
|
||||||
<ul>
|
<form method="GET" action="/domain_sync.html" id="syncForm">
|
||||||
<li>
|
|
||||||
A <code>A {{ data.domain }}</code> entry with the value
|
|
||||||
<code>{{ data.target_a }}</code>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
A <code>TXT _domani_challenge.{{ data.domain }}</code> entry with the value
|
|
||||||
<code>{{ data.challenge_token }}</code>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>Once complete, you can hit the following button to check your configuration
|
|
||||||
and set up your domain.</p>
|
|
||||||
|
|
||||||
<form method="GET" action="/domain_sync.html">
|
|
||||||
<input name="domain" type="hidden" value="{{ data.domain }}" />
|
<input name="domain" type="hidden" value="{{ data.domain }}" />
|
||||||
{{ #each data.flat_config }}
|
{{ #each data.flat_config }}
|
||||||
<input name="{{ @key }}" type="hidden" value="{{ this }}" />
|
<input name="{{ @key }}" type="hidden" value="{{ this }}" />
|
||||||
@ -25,10 +11,50 @@ and set up your domain.</p>
|
|||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label>
|
<label>
|
||||||
Passphrase (required during closed-beta):
|
Passphrase:
|
||||||
<input name="passphrase" placeholder="shhhh" required />
|
<input name="passphrase" placeholder="shhhh" required />
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<input type="submit" value="Next" />
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<p>Next you will need to configure your DNS server to point to Domani. There are
|
||||||
|
two entries you will need to add. The first entry tells the Domani server that
|
||||||
|
it is allowed to serve this domain with your given configuration:</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Domain</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>TXT</td>
|
||||||
|
<td>_domani_challenge.{{ data.domain }}</td>
|
||||||
|
<td>{{ data.challenge_token }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>The second entry ensures that other users find the Domani server when they
|
||||||
|
query for your domain name. It can be <strong>one or more of</strong>:</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Domain</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{{ #each data.dns_records }}
|
||||||
|
<tr>
|
||||||
|
<td>{{ this.type }}</td>
|
||||||
|
<td>{{ lookup ../data "domain" }}</td>
|
||||||
|
<td>{{ this.addr }}</td>
|
||||||
|
</tr>
|
||||||
|
{{ /each }}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>Once both entries are installed, you can hit the following button to check
|
||||||
|
your configuration and set up your domain.</p>
|
||||||
|
|
||||||
|
<input type="submit" value="Next" form="syncForm" />
|
||||||
|
Loading…
Reference in New Issue
Block a user