diff --git a/README.md b/README.md new file mode 100644 index 0000000..70305be --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +# Zeus (Domain) Name Server + +Is implementation of an authoritative DNS server. + +It gives all users who have a [Zauth](https://zauth.zeus.gent) account an own domain: `username.users.zeus.gent`. + +## General Information + +Creating/Updating your DNS records is only possible using dynamic DNS updating (DDNS, rfc2136). +It's an extension of DNS that lets you update your DNS records using the DNS protocol. + +ZNS authenticates these update requests using SIG(0) (rfc2931). +This is another extension of DNS that defines a signature record. It is appended to the query and contains the signature of the original query and +some other information like expiration time to prevent replay attacks. + +The signature is created with the private key of the signer and validated on the server with the corresponding public key. +ZNS has 2 methods of validating the signature: +- Using your SSH Keys in [Zauth](https://zauth.zeus.gent) +- Using a [DNSKEY record](https://datatracker.ietf.org/doc/html/rfc4034#section-2) + + +## User Guide + +How to add an `A` record to `.users.zeus.gent`. + +### Step 1 + +Create an SSH key pair (or use an existing one). Currently, only ED25519 and RSA SSH key types are supported. +Add the public key to your Zauth account. + +### Step 2 + +The (most) painless way for sending DNS update queries is using the `nsupdate` program. +With `nsupdate -k keys`, you can pass it your keys. But `nsupdate` expects your keys to have a certain format, so it won't accept the OPENSSH private key format. +That's why there is a CLI (`zns-cli`) available that converts the OPENSSH private key format and creates `.key` and `.private` files corresponding with your public and private keys. +And with some more info like the update ZONE (`username.users.zeus.gent`), the signing algorithm (ED25519 or RSA), ... + +Execute: + +```sh +zns-cli --key --username +``` + +Now you can run `nsupdate -k Kdns.private`. + +``` +> zone username.users.zeus.gent +> update add username.users.zeus.gent 300 A +> send +``` + +This will add an A record to `username.users.zeus.gent`. +The message will be signed with the private key, and the server will try to validate by trying to find a valid public SSH key from your Zauth account. Matching the `username` given in the zone. +The default expiration time with `nsupdate` is 5 minutes. + +That's it... not that hard, is it? + +### Step 3 (Optional) + +It is also possible to put your public key in a DNSKEY record instead of Zauth. In the previous step, `zns-cli` also generated a `.key` file. +This contains a DNSKEY resource record you can add to your zone using `nsupdate`. Now the signature can be validated directly using this record. + +It's also possible to directly generate a DNSKEY record key pair using `dnssec-keygen`. + +## Server Setup Guide + +There are three crates available at the root of the repo. + +`zns` is a library which is both used by `zns-cli` and `zns-daemon`. +`zns-daemon` is the server that handles DNS queries. + +The following environment variables should be set (or stored in a `.env` file): +``` +DATABASE_URL=postgres://zns@localhost/zns +ZAUTH_URL="https://zauth.zeus.gent" +ZONE="users.zeus.gent" +``` + +Optional: `ZNS_ADDRESS` and `ZNS_PORT`. + +After setting `DATABASE_URL`, create the database and run the migrations with `diesel migration run`. + +It's quite possible that something is not conform to an RFC, creating an issue is appreciated. diff --git a/zns-cli/src/main.rs b/zns-cli/src/main.rs index a976aba..492ba01 100644 --- a/zns-cli/src/main.rs +++ b/zns-cli/src/main.rs @@ -1,5 +1,5 @@ use base64::prelude::*; -use num_bigint::{BigInt, BigUint}; +use num_bigint::BigUint; use num_traits::FromPrimitive; use std::error::Error; use std::fs::{self, File}; @@ -76,7 +76,7 @@ fn read_bytes(reader: &mut Reader) -> Result, ZNSError> { impl KeyTransformer for Ed25519KeyPair { fn from_openssh(reader: &mut Reader) -> Result { - // public key parts + // public key parts//TODO: change to SIG let length = reader.read_u32()?; reader.read(length as usize)?; @@ -259,6 +259,7 @@ impl KeyTransformer for OpenSSHKey { const OPENSSH_START: &str = "-----BEGIN OPENSSH PRIVATE KEY-----"; const OPENSSH_END: &str = "-----END OPENSSH PRIVATE KEY-----"; +const FILENAME: &str = "Kdns"; fn ssh_to_dnskey(file_content: &str, username: &str) -> Result<(), Box> { if !file_content.starts_with(OPENSSH_START) || !file_content.ends_with(OPENSSH_END) { @@ -277,8 +278,8 @@ fn ssh_to_dnskey(file_content: &str, username: &str) -> Result<(), Box match ssh_to_dnskey(contents.trim(), &args.username) { - Ok(()) => println!("Success"), + Ok(()) => println!( + "Successfully written {}.private and {}.key", + FILENAME, FILENAME + ), Err(error) => eprintln!("{}", error), }, Err(error) => eprintln!("{}", error), diff --git a/zns-daemon/src/handlers/update/mod.rs b/zns-daemon/src/handlers/update/mod.rs index fe669d1..5e5293f 100644 --- a/zns-daemon/src/handlers/update/mod.rs +++ b/zns-daemon/src/handlers/update/mod.rs @@ -51,7 +51,7 @@ impl ResponseHandler for UpdateHandler { //TODO: this code is ugly let last = message.additional.last(); - if last.is_some() && last.unwrap()._type == Type::Type(RRType::KEY) { + if last.is_some() && last.unwrap()._type == Type::Type(RRType::SIG) { let sig = Sig::new(last.unwrap(), raw)?; if !authenticate::authenticate(&sig, &zone.qname, connection) diff --git a/zns/src/structs.rs b/zns/src/structs.rs index f9e6f2c..7b4edbb 100644 --- a/zns/src/structs.rs +++ b/zns/src/structs.rs @@ -11,7 +11,7 @@ pub enum Type { pub enum RRType { A = 1, SOA = 6, - KEY = 24, //TODO: change to SIG + SIG = 24, DNSKEY = 48, OPT = 41, ANY = 255,