matchd 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +3 -0
  4. data/.rubocop-https---relaxed-ruby-style-rubocop-yml +166 -0
  5. data/.rubocop.yml +20 -0
  6. data/.travis.yml +18 -0
  7. data/DEVELOPMENT.md +5 -0
  8. data/Gemfile +8 -0
  9. data/License.txt +373 -0
  10. data/README.md +473 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/matchd +4 -0
  14. data/bin/rake +29 -0
  15. data/bin/rspec +29 -0
  16. data/bin/setup +8 -0
  17. data/examples/registry.yml +30 -0
  18. data/exe/matchd +6 -0
  19. data/lib/matchd.rb +31 -0
  20. data/lib/matchd/cli.rb +10 -0
  21. data/lib/matchd/cli/config.rb +36 -0
  22. data/lib/matchd/cli/config_file_option.rb +23 -0
  23. data/lib/matchd/cli/main.rb +45 -0
  24. data/lib/matchd/config.rb +39 -0
  25. data/lib/matchd/control.rb +56 -0
  26. data/lib/matchd/glue.rb +6 -0
  27. data/lib/matchd/glue/async_endpoint.rb +93 -0
  28. data/lib/matchd/helpers.rb +14 -0
  29. data/lib/matchd/registry.rb +66 -0
  30. data/lib/matchd/response.rb +72 -0
  31. data/lib/matchd/response/a.rb +12 -0
  32. data/lib/matchd/response/aaaa.rb +5 -0
  33. data/lib/matchd/response/cname.rb +12 -0
  34. data/lib/matchd/response/mx.rb +16 -0
  35. data/lib/matchd/response/ns.rb +12 -0
  36. data/lib/matchd/response/ptr.rb +12 -0
  37. data/lib/matchd/response/soa.rb +28 -0
  38. data/lib/matchd/response/srv.rb +20 -0
  39. data/lib/matchd/response/txt.rb +12 -0
  40. data/lib/matchd/rule.rb +116 -0
  41. data/lib/matchd/rule/append.rb +23 -0
  42. data/lib/matchd/rule/fail.rb +18 -0
  43. data/lib/matchd/rule/invalid.rb +24 -0
  44. data/lib/matchd/rule/passthrough.rb +50 -0
  45. data/lib/matchd/rule/respond.rb +16 -0
  46. data/lib/matchd/server.rb +44 -0
  47. data/lib/matchd/version.rb +3 -0
  48. data/matchd.gemspec +31 -0
  49. metadata +191 -0
@@ -0,0 +1,473 @@
1
+ # Matchd
2
+
3
+ Matchd is a DNS server which can be configured via YAML files.
4
+
5
+ Like it's example [`RubyDNS`](https://github.com/socketry/rubydns), it uses a pattern matching rule model but does not require you to write any ruby.
6
+
7
+ **This is work in progress and not yet released. I will break things all the time.**
8
+
9
+ [![Build Status](https://travis-ci.org/fnordfish/matchd.svg?branch=master)](https://travis-ci.org/fnordfish/matchd)
10
+ [![Maintainability](https://api.codeclimate.com/v1/badges/9791617021f321155cb0/maintainability)](https://codeclimate.com/github/fnordfish/matchd/maintainability)
11
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/9791617021f321155cb0/test_coverage)](https://codeclimate.com/github/fnordfish/matchd/test_coverage)
12
+ [![Inline docs](https://inch-ci.org/github/fnordfish/matchd.svg?branch=master)](http://inch-ci.org/github/fnordfish/matchd)
13
+
14
+ ## Table of Contents
15
+
16
+ - [Matchd](#matchd)
17
+ - [Table of Contents](#table-of-contents)
18
+ - [Installation](#installation)
19
+ - [Intended use](#intended-use)
20
+ - [Usage](#usage)
21
+ - [Configure the Server/Daemon (`config.yml`)](#configure-the-serverdaemon-configyml)
22
+ - [Configure the Rules registry (`registry.yml`)](#configure-the-rules-registry-registryyml)
23
+ - [Matching the hostname](#matching-the-hostname)
24
+ - [Matching the resource_class](#matching-the-resourceclass)
25
+ - [respond](#respond)
26
+ - [`A`](#a)
27
+ - [`AAAA`](#aaaa)
28
+ - [`CNAME`](#cname)
29
+ - [`MX`](#mx)
30
+ - [`NS`](#ns)
31
+ - [`PTR`](#ptr)
32
+ - [`SOA`](#soa)
33
+ - [`SRV`](#srv)
34
+ - [`TXT`](#txt)
35
+ - [append](#append)
36
+ - [passthrough](#passthrough)
37
+ - [fail](#fail)
38
+ - [Using as a local Dev DNS](#using-as-a-local-dev-dns)
39
+ - [macOS / resolver](#macos--resolver)
40
+ - [Using as a library](#using-as-a-library)
41
+ - [Development](#development)
42
+ - [Contributing](#contributing)
43
+ - [License](#license)
44
+ - [Previous Work](#previous-work)
45
+
46
+ ## Installation
47
+
48
+ $ gem install matchd
49
+
50
+ ## Intended use
51
+
52
+ Although it's probably possible to use as a public/private DNS, Matchd is intended to be used in a development environment where using "localhost" or "editing /etc/hosts" isn't feasible anymore. Such as:
53
+
54
+ - large list of sub-domains (use regular expressions to match them all)
55
+ - DNS record types like TXT, MX, SRV etc. for integration testing or development
56
+
57
+ The idea is to use a globally non-route-able top level domain like `.test` and configure your local system in a way, that it will use Matchd as a resolver for such domains.
58
+
59
+ You can use Matchd to hook into global top level domain such as `.com` if you wish, and Matchd will forward everything it can't match to an upstream resolver. But keep in mind that doing so can have unexpected or even catastrophic results when expediently matching a domain you actually need for work.
60
+
61
+ Please refer to the section "[Using as a local Dev DNS](#using-as-a-local-dev-dns)" on how to make your system use Matchd as a resolver.
62
+
63
+ ## Usage
64
+
65
+ Print available commands and sub-commands:
66
+
67
+ $ matchd help
68
+ $ matchd help start # to display details for a specific command
69
+ $ matchd help config # ... or sub-command
70
+ $ matchd config help setup # prints details for a specific sub-commands command
71
+
72
+
73
+ Create the default configuration directory within your `$HOME`
74
+
75
+ $ matchd config setup
76
+
77
+ - `.matchd`: This is also the home for log and pid files
78
+ - `.matchd/config.yml`: Base configurations of the server
79
+ - `.matchd/registry.yml`: The registry for all of your rules
80
+
81
+ Please also see [`examples`](../examples/) for more.
82
+
83
+ ## Configure the Server/Daemon (`config.yml`)
84
+
85
+ - `dot_dir`: Path to the daemons base directory. This is where log and pid files are written to.
86
+ - `listen`: Collection of interface where the server will listen on.
87
+ This setting supports multiple formats:
88
+ - Array/Collection form:
89
+ ```yaml
90
+ listen:
91
+ - ["udp", "127.0.0.1", 15353]
92
+ ```
93
+ - Hash/Structure form:
94
+ ```yaml
95
+ listen:
96
+ - protocol: udp
97
+ ip: 127.0.0.1
98
+ port: 15353
99
+ ```
100
+ - URL form:
101
+ ```yaml
102
+ listen:
103
+ - "udp://127.0.0.1:15353"
104
+ ```
105
+ - `resolver`: Collection of upstream resolvers for "passthrough" requests. (If a query could net been matched, we'll forward that request to this server)
106
+ Supports the same formats as `listen`:
107
+ - Array/Collection form:
108
+ ```yaml
109
+ resolver:
110
+ - ["udp", "1.1.1.1", 53]
111
+ ```
112
+ - Hash/Structure form:
113
+ ```yaml
114
+ resolver:
115
+ - protocol: udp
116
+ ip: 1.1.1.1
117
+ port: 53
118
+ ```
119
+ - URL form:
120
+ ```yaml
121
+ resolver:
122
+ - "udp://1.1.1.1:53"
123
+ ```
124
+ You can also default to your system settings (NOTE that this is a single value):
125
+ ```yaml
126
+ resolver: system
127
+ ```
128
+
129
+ ## Configure the Rules registry (`registry.yml`)
130
+
131
+ This file uses a single key `rules` which lists all the rules. Each rule is a structure with three keys.
132
+
133
+ ```yaml
134
+ - rules:
135
+ - match: ... # String or Regexp
136
+ resource_class: ... # A IN::* resource record name
137
+ _action_: ... # How to answer this match
138
+ ```
139
+
140
+ The first two options, `match` and `resource_class` describe which query to match. The third describes how to respond and should be one of: `append_question`, `respond`, `passthrough` or `fail`.
141
+
142
+ Rules will get evaluated top-to-bottom. Once a rule matches, it will get executed and processing stops.
143
+ A match always considers the name _and_ resource class of the query.
144
+
145
+ The query name can be matched against a specific string or regular expression.
146
+ The query resource class can be matched against a single or a list of targets. When a list is used, any of the targets in the list will trigger.
147
+
148
+ When no rule matches, the query will be forwarded to the configured resolvers (see `config.yml` and it's `resolver` key).
149
+
150
+ ### Matching the hostname
151
+
152
+ To match against a specific name use the string notation.
153
+
154
+ ```yaml
155
+ # This will only match where the requested domain is exactly `mydomain.test`, sub-domains like `sub.mydomain.test` will not match.
156
+ match: mydomain.test
157
+ ```
158
+
159
+ To match all sub-domains, use a regular expression.
160
+
161
+ ```yaml
162
+ # This will only match sub-domain queries like `sub.mydomain.test` or `bus.mydomain.test`, but not `mydomain.de`
163
+ match: /^\w+\.mydomain\.test$/
164
+ # This will only match sub-domain queries like `sub.mydomain.test` or `bus.mydomain.test`, and also `mydomain.de`
165
+ match: /(^\w+\.)?mydomain\.test$/
166
+ # This will also match any depth of sub-domain queries like `bus.sub.mydomain.test`:
167
+ match: /(^\w+\.)*mydomain\.test$/
168
+ ```
169
+
170
+ Under the hood, Matchd uses [ruby regular expressions](https://ruby-doc.org/core-2.5.1/Regexp.html).
171
+ For the YAML configuration format only the `/.../` syntax is supported, optionally using the `m`, `x` and `i` modifiers.
172
+
173
+ When using regular expressions, it's recommended to use `^` and `$` anchors to have better control over what you want to match. Also keep in mind, that you'll need to escape dots (`.` -> `\.`).
174
+
175
+ ### Matching the resource_class
176
+
177
+ A single or a list of ARPA Internet specific resource record (RRs) names:
178
+ `A`, `AAAA`, `ANY`, `CNAME`, `HINFO`, `LOC`, `MINFO`, `MX`, `NS`, `PTR`, `SOA`, `SRV`, `TXT`, `WKS`
179
+
180
+ To give some most common samples:
181
+
182
+ ```yaml
183
+ # IPv4 record
184
+ resource_class: A
185
+ # IPv6 record
186
+ resource_class: AAAA
187
+ # I'm funny:
188
+ resource_class:
189
+ - TXT
190
+ - CNAME
191
+ ```
192
+
193
+ ### respond
194
+
195
+ To let a rule respond, include the `respond` key.
196
+
197
+ Supported response types are: `A`, `AAAA`, `CNAME`, `MX`, `NS`, `PTR`, `SOA`, `SRV`, `TXT`
198
+
199
+ You can configure multiple responses per rule. Each rule has it's own configuration keys (see below) but they all support a common set of options:
200
+
201
+
202
+ ```yaml
203
+ ttl: 86400 # The Time-To-Live of the record (default: 86400 seconds == 24h)
204
+ name: "other.sample.test." # The absolute DNS name (needs to end with a dot). Default is the question name.
205
+ section: answer # The answer section. One of "answer", "additional", "authority" (default: "answer")
206
+ ```
207
+
208
+ To avoid clutter, there are some shortcuts when defining a rule:
209
+
210
+ 1. Omit the List for single responses
211
+ 2. Omit the Collection for responses that take only one argument
212
+ 3. Omit the responds `resource_class` when it's the same as the queries `resource_class`
213
+
214
+ Example:
215
+
216
+ One single value response:
217
+
218
+ ```yaml
219
+ - match: sample.test
220
+ resource_class: A
221
+ respond: 10.0.0.1
222
+
223
+ # is the same as:
224
+ - match: sample.test
225
+ resource_class: A
226
+ respond:
227
+ - resource_class: A
228
+ ip: 10.0.0.1
229
+ ```
230
+
231
+ Multiple single value responses:
232
+
233
+ ```yaml
234
+ - match: sample.test
235
+ resource_class: NS
236
+ respond:
237
+ - 'ns1.sample.test.'
238
+ - 'ns2.sample.test.'
239
+
240
+ # is the same as:
241
+ - match: sample.test
242
+ resource_class: NS
243
+ respond:
244
+ - resource_class: NS
245
+ host: 'ns1.sample.test.'
246
+ - resource_class: NS
247
+ host: 'ns2.sample.test.'
248
+ ```
249
+
250
+ #### `A`
251
+
252
+ ```yaml
253
+ resource_class: A
254
+ ip: "127.0.0.1"
255
+ ```
256
+
257
+ #### `AAAA`
258
+
259
+ ```yaml
260
+ resource_class: AAAA
261
+ ip: "::1"
262
+ ```
263
+
264
+ #### `CNAME`
265
+
266
+ ```yaml
267
+ resource_class: CNAME
268
+ alias: "sample.test"
269
+ ```
270
+
271
+ #### `MX`
272
+
273
+ ```yaml
274
+ resource_class: MX
275
+ preference: 10
276
+ host: "mail.sample.test"
277
+ ```
278
+
279
+ #### `NS`
280
+
281
+ ```yaml
282
+ resource_class: NS
283
+ host: "ns1.sample.test"
284
+ ```
285
+
286
+ #### `PTR`
287
+
288
+ ```yaml
289
+ resource_class: PTR
290
+ host: "host1.sample.test"
291
+ ```
292
+
293
+ #### `SOA`
294
+
295
+ ```yaml
296
+ resource_class: SOA
297
+ mname: "ns1.sample.test." # master zone name
298
+ rname: "admin.sample.test." # Responsible Name
299
+ serial: "1533038712" # Serial Number
300
+ refresh: 1200 # Refresh Time
301
+ retry: 900 # Retry Time
302
+ expire: 3600000 # Maximum TTL / Expiry Time
303
+ minimum_ttl: 172800 # Minimum TTL
304
+ ```
305
+
306
+ #### `SRV`
307
+
308
+ ```yaml
309
+ resource_class: SRV
310
+ target: jabber
311
+ priority: 10
312
+ weight: 0
313
+ port: 5269
314
+ # To make this meaningful, we need to provide a name:
315
+ name: "_xmpp-server._tcp.sample.test"
316
+ ```
317
+
318
+ #### `TXT`
319
+
320
+ ```yaml
321
+ resource_class: TXT
322
+ txt: "some-token=value"
323
+ ```
324
+
325
+ ### append
326
+
327
+ Append the results of another query to the response.
328
+
329
+ ```yaml
330
+ append_question: A
331
+ # is the same as:
332
+ append_question:
333
+ - A
334
+ ```
335
+
336
+ Append rules accept the same common options as Respond rules. The Response rules options will take precedence if configured.
337
+
338
+ ```yaml
339
+ append_question:
340
+ ttl: 86400 # The Time-To-Live of the record (default: 86400 seconds == 24h)
341
+ name: "other.sample.test." # The absolute DNS name (needs to end with a dot). Default is the question name.
342
+ section: answer # The answer section. One of "answer", "additional", "authority" (default: "answer")
343
+ resource_class:
344
+ - A
345
+ - AAAA
346
+ ```
347
+
348
+ Example:
349
+
350
+ ```yaml
351
+ - match: dev.sample.test
352
+ resource_class: ANY
353
+ append_question:
354
+ ttl: 86400
355
+ resource_class:
356
+ - A
357
+ - CNAME
358
+ - MX
359
+ - NS
360
+ ```
361
+
362
+ ### passthrough
363
+
364
+ Query an upstream resolver and use its response.
365
+
366
+ Takes the same parameters as the server config's `resolver`:
367
+
368
+ ```yaml
369
+ # Mixing IPv4 and IPv6 in different formats:
370
+ passthrough:
371
+ - ["udp", "1.1.1.1", 53]
372
+ - protocol: udp
373
+ ip: 1.0.0.1
374
+ port: 53
375
+ - "tcp://[2606:4700:4700::1111]:53"
376
+ - system
377
+
378
+ # Shortcut, if you need only one:
379
+ passthrough: system # or any other version above
380
+
381
+ # Long format with added options
382
+ passthrough:
383
+ force: true
384
+ resolver:
385
+ - ["udp", "1.1.1.1", 53]
386
+ - protocol: udp
387
+ ip: 1.0.0.1
388
+ port: 53
389
+ - "tcp://[2606:4700:4700::1111]:53"
390
+ - system
391
+ ```
392
+
393
+
394
+
395
+ ### fail
396
+
397
+ Indicate an error processing the query.
398
+
399
+ To cite `Async::DNS` list of list of the most commonly used errors:
400
+
401
+ - `NoError`: No error occurred.
402
+ - `FormErr`: The incoming data was not formatted correctly.
403
+ - `ServFail`: The operation caused a server failure (internal error, etc).
404
+ - `NXDomain`: Non-eXistant Domain (domain record does not exist).
405
+ - `NotImp`: The operation requested is not implemented.
406
+ - `Refused`: The operation was refused by the server.
407
+ - `NotAuth`: The server is not authoritive for the zone.
408
+
409
+ Note that the above values are all case __sensitive__!
410
+
411
+ Example:
412
+
413
+ ```yaml
414
+ - match: not-there.sample.test
415
+ resource_class: A
416
+ fail: NXDomain
417
+
418
+ # a catchall rule:
419
+ - match: /^(\w+\.)?sample.test$/
420
+ resource_class: A
421
+ respond: "127.0.0.1"
422
+ ```
423
+
424
+ ## Using as a local Dev DNS
425
+
426
+ When using Matchd as a local dns server for development purposes, it's recommended to:
427
+
428
+ 1. use non-route-able top level domains, such as `.test`
429
+ 2. use a non-privileged port (usually < 1024), such as 15353 (the default)
430
+
431
+ ### macOS / resolver
432
+
433
+ Create a file under `/etc/resolver/` which is named like the top level domain you'd like to specify a different resolver (Matchd) for.
434
+
435
+ ```bash
436
+ cat <<EOF | sudo tee /etc/resolver/test
437
+ nameserver 127.0.0.1
438
+ nameserver ::1
439
+ port 15353
440
+ EOF
441
+ ```
442
+
443
+
444
+
445
+ ## Using as a library
446
+
447
+ While Matchd is intended to be used as a demonizing CLI tool, you can use it as a library.
448
+
449
+ TODO: Docs
450
+
451
+ ## Development
452
+
453
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
454
+
455
+ To install this gem onto your local machine, run `bin/rake install`. To release a new version, update the version number in `version.rb`, and then run `bin/rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
456
+
457
+
458
+ ## Contributing
459
+
460
+ Bug reports and pull requests are welcome on GitHub at https://github.com/fnordfish/matchd
461
+
462
+ ## License
463
+
464
+ This software is distributed under the Mozilla Public License Version 2.0
465
+
466
+ A copy of the license text can be found in this repository under `License.txt` or at http://mozilla.org/MPL/2.0/
467
+
468
+ ### Previous Work
469
+
470
+ This software is based on the ideas of:
471
+
472
+ - [vagrant-dns](https://github.com/BerlinVagrant/vagrant-dns), created by Florian Gilcher and released under the MIT License
473
+ - [rubydns](https://github.com/socketry/rubydns), created by Samuel Williams and released under the MIT License.