10
0
Fork 0
mirror of https://github.com/ZeusWPI/ZNS.git synced 2024-11-21 21:41:10 +01:00

add README and fix some todos

This commit is contained in:
Xander Bil 2024-08-19 22:12:54 +02:00
parent 271ee5e395
commit c1ab0cc953
No known key found for this signature in database
GPG key ID: EC9706B54A278598
4 changed files with 94 additions and 7 deletions

83
README.md Normal file
View file

@ -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 `<your zauth username>.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 <path to private ssh key> --username <zauth username>
```
Now you can run `nsupdate -k Kdns.private`.
```
> zone username.users.zeus.gent
> update add username.users.zeus.gent 300 A <ip address>
> 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.

View file

@ -1,5 +1,5 @@
use base64::prelude::*; use base64::prelude::*;
use num_bigint::{BigInt, BigUint}; use num_bigint::BigUint;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use std::error::Error; use std::error::Error;
use std::fs::{self, File}; use std::fs::{self, File};
@ -76,7 +76,7 @@ fn read_bytes(reader: &mut Reader) -> Result<Vec<u8>, ZNSError> {
impl KeyTransformer for Ed25519KeyPair { impl KeyTransformer for Ed25519KeyPair {
fn from_openssh(reader: &mut Reader) -> Result<Self, ZNSError> { fn from_openssh(reader: &mut Reader) -> Result<Self, ZNSError> {
// public key parts // public key parts//TODO: change to SIG
let length = reader.read_u32()?; let length = reader.read_u32()?;
reader.read(length as usize)?; reader.read(length as usize)?;
@ -259,6 +259,7 @@ impl KeyTransformer for OpenSSHKey {
const OPENSSH_START: &str = "-----BEGIN OPENSSH PRIVATE KEY-----"; const OPENSSH_START: &str = "-----BEGIN OPENSSH PRIVATE KEY-----";
const OPENSSH_END: &str = "-----END 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<dyn Error>> { fn ssh_to_dnskey(file_content: &str, username: &str) -> Result<(), Box<dyn Error>> {
if !file_content.starts_with(OPENSSH_START) || !file_content.ends_with(OPENSSH_END) { 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<dyn Error
let mut reader = Reader::new(&bin); let mut reader = Reader::new(&bin);
let key = OpenSSHKey::from_openssh(&mut reader)?; let key = OpenSSHKey::from_openssh(&mut reader)?;
let mut file_private = File::create("Kdns.private")?; let mut file_private = File::create(format!("{}.private", FILENAME))?;
let mut file_public = File::create("Kdns.key")?; let mut file_public = File::create(format!("{}.key", FILENAME))?;
let (private, public) = key.to_dnskey(username); let (private, public) = key.to_dnskey(username);
file_private.write(private.as_bytes())?; file_private.write(private.as_bytes())?;
@ -292,7 +293,10 @@ fn main() {
match fs::read_to_string(args.key) { match fs::read_to_string(args.key) {
Ok(contents) => match ssh_to_dnskey(contents.trim(), &args.username) { Ok(contents) => 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),
}, },
Err(error) => eprintln!("{}", error), Err(error) => eprintln!("{}", error),

View file

@ -51,7 +51,7 @@ impl ResponseHandler for UpdateHandler {
//TODO: this code is ugly //TODO: this code is ugly
let last = message.additional.last(); 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)?; let sig = Sig::new(last.unwrap(), raw)?;
if !authenticate::authenticate(&sig, &zone.qname, connection) if !authenticate::authenticate(&sig, &zone.qname, connection)

View file

@ -11,7 +11,7 @@ pub enum Type {
pub enum RRType { pub enum RRType {
A = 1, A = 1,
SOA = 6, SOA = 6,
KEY = 24, //TODO: change to SIG SIG = 24,
DNSKEY = 48, DNSKEY = 48,
OPT = 41, OPT = 41,
ANY = 255, ANY = 255,