5

GitHub - lorin/dns-alloy: Modeling DNS with Alloy

 2 years ago
source link: https://github.com/lorin/dns-alloy
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Learning DNS by modeling it with ALloy

Alloy is a great tool to use for learning an unfamiliar domain by incrementally modeling it. I've never really grokked how the DNS system works, and so I'm using this as an opportunity to both practice with Alloy and learn more about DNS. Note that this is file can be loaded directly into the Alloy analyzer.

Domain name space

The domain name space is a tree, where each node corresponds to a domain name. Here's an example from RFC1034:

                                   |
                                   |
             +---------------------+------------------+
             |                     |                  |
            MIL                   EDU                ARPA
             |                     |                  |
             |                     |                  |
       +-----+-----+               |     +------+-----+-----+
       |     |     |               |     |      |           |
      BRL  NOSC  DARPA             |  IN-ADDR  SRI-NIC     ACC
                                   |
       +--------+------------------+---------------+--------+
       |        |                  |               |        |
      UCI      MIT                 |              UDEL     YALE
                |                 ISI
                |                  |
            +---+---+              |
            |       |              |
           LCS  ACHILLES  +--+-----+-----+--------+
            |             |  |     |     |        |
            XX            A  C   VAXA  VENERA Mockapetris

Each node in the tree has a domain name. For example, the node labeled ISI has the name isi.edu.

Here's an alloy model.

sig DomainName {}

sig Node {
	domainName: disj DomainName,
	children: set Node
}

fun parent[n : Node] : Node  {
	n.~children
}

Let's make sure the nodes form a tree:

fact "nodes from a tree" {

	// no cycles
	no iden & ^children

	// fully connected
	// there's node from which every node is reachable (the root)
	some root : Node | Node in root.*children
}

Records

The DNS system enables clients to retrieve records by domain name. Think of it as a key-value system where the keys are domain names and the values are record of different types. Let's model a record.

abstract sig Record {
	name: DomainName
}

There are many different DNS record types, let's model some common ones:

sig A extends Record {
	value: set IPv4Address
}

sig IPv4Address {}

sig AAAA extends Record {
	value: set IPv6Address
}

sig IPv6Address {}

sig CNAME extends Record {
	value: DomainName
}

pred noTwoRecordsHaveSameName[r: set Record] {
	all disj r1, r2: Record | no r1.name & r2.name
}

fact {
	noTwoRecordsHaveSameName[A]
	noTwoRecordsHaveSameName[AAAA]
	noTwoRecordsHaveSameName[CNAME]
}

Zones

From RFC1034:

every zone has at least one node, and hence domain name, for which it is authoritative, and all of the nodes in a particular zone are connected.

The domain name space is carved up into regions called zones. Each zone is contiguous. This means that the nodes in a graph make up a connected graph. Since the entire graph is a tree, this means that every zone is also a tree. The root of the tree in a zone is called the apex node.

Here's an example of a portion of the domain name space subdivided into zones, indicated by red circles.

Note: this diagram assumes that the root, com, and org nodes are all in separate zones, but I don't actually know, and I'm not sure how to check.

Observe that:

  • example.com and www.example.com are in the same zone
  • acme.com and internal.acme.com are in separate zones

Note: because com and org are in separate zones, it isn't possible for example.com and example.org to be in the same zone.

Modeling it in Alloy:

sig Zone {
	nodes: disj set Node,
	records: disj set Record
} {
	// at least one node
	some nodes

	// the nodes in a zone make upa connected graph
	all disj n1, n2 : nodes | (n1->n2 in ^children) or (n2->n1 in ^children)

	// all records are in a zone
	// All records are associated withe one of the owned nodes
	all r: records | r.name in nodes.domainName
}

fact "Every record is associated with exactly one zone" {
	all r: Record | one records.r
}

Apex of a zone:

fun root[nodes: set Node] : Node {
	let r = children & (nodes -> nodes) |  {n : nodes | no (n & n.^r)}
}


fun apex[z: Zone] : Node {
	root[z.nodes]
}

Name servers

A name server is associated with one or more zones.

abstract sig NameServer {
	zones: set Zone
}

fact "every zone is associated with at least one name server" {
	all z : Zone | some zones.z
}

There are several kinds of name servers:

// These are the servers that DNS clients talk to directly,
// e.g., your ISP's DNS server, Google's 8.8.8.8, Cloudflare's 1.1.1.1
// They resolve by calling other servers
sig RecursiveResolver extends NameServer {} {
	// these resolvers are not authoritative for any zones
	no zones
}

// Root name servers are associated with the root zone
sig RootNameServer extends NameServer {} {
	one zones
	zones.nodes = {n : Node | no parent[n] }
}


fun tldNodes[] : Node {
	// TLD nodes have parents bu
	{ n: Node | some parent[n] and no parent[parent[n]] }
}

// TLD name servers own TLD zones
sig TldNameServer extends NameServer {} {
	some zones
	zones.nodes in tldNodes[]
}

fact "TLD nodes are only in the zones of TLD name servers" {
	tldNodes[].~nodes in TldNameServer.zones
}


// Authoritative records for domains other than root and TLDs
sig AuthoritativeNameServer extends NameServer {} {
	some zones
	no zones & RootNameServer.@zones
	no zones & TldNameServer.@zones
}

We can now model SOA records, which contain references to name servers.

sig SOA extends Record {
	mname: NameServer
} {
	// name server must have the zone associated with this record
	mname.zones in this.~records
}

fact "every zone has exactly one SOA record" {
	// all zones have one SOA record
	all z : Zone | one SOA & z.records

	// All SOA records are in one zone
	all r : SOA | one r.~records
}

Visualizing

We can run this to generate a visualization of an instance of our model.

run {
	some RootNameServer
	some TldNameServer
	some AuthoritativeNameServer

	all z : Zone | some z.records
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK