Pi-hole v6.0 API description for /api/dns

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2020-06-16 20:03:41 +02:00
parent fcf54e0717
commit 6446bcb131
8 changed files with 621 additions and 284 deletions
+6 -3
View File
@@ -1,15 +1,16 @@
*[API]: Application Programming Interface (a set of subroutine definitions, protocols, and tools for building application software)
*[DNS]: Domain Name Service (decentralized naming system for computers, services, or other resources connected to the Internet)
*[FTL]: Pi-hole's Faster Than Light daemon
*[DnyDNS]: Dynamic DNS record pointing to a frequently changing IP address
*[DHCP]: Dynamic Host Configuration Protocol (network management protocol for configuring Internet Protocol version 4 (IPv4) hosts with IP addresses)
*[DHCPv6]: Dynamic Host Configuration Protocol version 6 (a network protocol for configuring Internet Protocol version 6 (IPv6) hosts with IP addresses)
*[FTL]: Pi-hole's Faster Than Light daemon
*[IPv4]: Internet Protocol version 4 (addresses like 192.168.0.1)
*[IPv6]: Internet Protocol version 6 (addresses like 2001:db8::ff00:42:8329)
*[HTTP]: Hypertext Transfer Protocol (HTTP), an application protocol for distributed, collaborative, and hypermedia information systems
*[HTTPS]: HTTP Secure (HTTPS), an extension of the Hypertext Transfer Protocol (HTTP) for secure communication over a computer network
*[TCP]: Transmission Control Protocol (protocol providing reliable, ordered, and error-checked delivery of data between applications running on hosts communicating via an IP network)
*[UDP]: User Datagram Protocol (a network communications method for sending messages as datagrams)
*[API]: Application Programming Interface (a set of subroutine definitions, protocols, and tools for building application software)
*[PE]: Privacy Extension
*[PID]: Process identifier (a number used to identify a process)
*[HOSTS]: The computer file /etc/hosts is an operating system file that maps hostnames to IP addresses
@@ -39,4 +40,6 @@
*[TFTP]: Trivial File Transfer Protocol is a simple lockstep File Transfer Protocol which allows a client to get a file from or put a file onto a remote host
*[TTL]: Time-To-Live is a mechanism that limits the lifespan or lifetime of data in a computer or network
*[NAT]: Network address translation
*[DnyDNS]: Dynamic DNS record pointing to a frequently changing IP address
*[UTF-8]: 8-bit Unicode Transformation Format - a character encoding format capable of encoding all known 1,112,064 valid Unicode characters
*[URL]: Uniform Ressource Locator, commonly known as "web address"
*[REST]: Representational State Transfer - a software architecture for distributed systems like the World Wide Web (WWW)
+56
View File
@@ -0,0 +1,56 @@
# DNS - Cache Info
## GET: Obtain cache information
Resource: `GET /admin/api/dns/cacheinfo`
Requires authorization: No
### Parameters
None
### Example
<!-- markdownlint-disable code-block-style -->
!!! example "Request"
=== "cURL"
``` bash
curl -H "Authorization: Token <your-access-token>" \
http://pi.hole:8080/admin/api/dns/cacheinfo
```
=== "Python 3"
``` python
import requests
URL = 'http://pi.hole:8080/admin/api/dns/cacheinfo'
TOKEN = '<your-access-token>'
HEADERS = {'Authorization': f'Token {TOKEN}'}
response = requests.get(URL, headers=HEADERS)
print(response.json())
```
!!! success "Response"
Response code: `HTTP/1.1 200 OK`
``` json
{
"cache_size": 10000,
"cache_inserted": 509,
"cache_evicted": 0
}
```
<!-- markdownlint-enable code-block-style -->
See [DNS cache details](../../ftldns/dns-cache.md) for further information about the returned quantities.
This endpoint cannot fail.
{!abbreviations.md!}
+273
View File
@@ -0,0 +1,273 @@
# DNS - Domain Lists
## GET: List all items
Resources:
- `GET /admin/api/dns/whitelist/exact`
- `GET /admin/api/dns/whitelist/regex`
- `GET /admin/api/dns/blacklist/exact`
- `GET /admin/api/dns/blacklist/regex`
Requires authorization: Yes
### Parameters
None
### Example
<!-- markdownlint-disable code-block-style -->
!!! example "Request"
=== "cURL"
``` bash
curl -H "Authorization: Token <your-access-token>" \
http://pi.hole:8080/admin/api/dns/whitelist/exact
```
=== "Python 3"
``` python
import requests
URL = 'http://pi.hole:8080/admin/api/dns/whitelist/exact'
TOKEN = '<your-access-token>'
HEADERS = {'Authorization': f'Token {TOKEN}'}
response = requests.get(URL, headers=HEADERS)
print(response.json())
```
!!! success "Success response"
Response code: `HTTP/1.1 200 OK`
``` json
[
{
"domain": "whitelisted.com",
"enabled": true,
"date_added": 1589108911,
"date_modified": 1589108911,
"comment": ""
}
]
```
!!! danger "Error response (database not available)"
Response code: `HTTP/1.1 402 - Request failed`
``` json
{
"error": {
"key": "database_error",
"message": "Could not remove domain to gravity database",
"data": {
"sql_msg": "Database not available"
}
}
}
```
<!-- markdownlint-enable code-block-style -->
## PUT: Add item
Resources:
- `PUT /admin/api/dns/whitelist/exact`
- `PUT /admin/api/dns/whitelist/regex`
- `PUT /admin/api/dns/blacklist/exact`
- `PUT /admin/api/dns/blacklist/regex`
Requires authorization: Yes
### Parameters
Name | Required | Type | Description | Default | Example
---- | -------- | ---- | ----------- | ------- | -------
`domain` | Yes | String | Domain to be added | |`whitelisted.com`
`enabled` | Optional | Boolean | Should this domain be used? | `true` | `true`
`comment` | Optional | String | Comment for this domain | `null` | `Some text`
### Example
<!-- markdownlint-disable code-block-style -->
!!! example "Request"
=== "cURL"
``` bash
curl -X PUT \
-H "Authorization: Token <your-access-token>" \
http://pi.hole:8080/admin/api/dns/whitelist/exact \
-H "Content-Type: application/json" \
-d @body.json
```
=== "Python 3"
``` python
import requests
URL = 'http://pi.hole:8080/admin/api/dns/whitelist/exact'
TOKEN = '<your-access-token>'
HEADERS = {'Authorization': f'Token {TOKEN}'}
data = json.load(open('body.json', 'rb'))
response = requests.put(
URL,
json=data,
headers=HEADERS,
)
print(response.json())
```
The content of `body.json` is
``` json
{
"domain": "whitelisted.com",
"enabled": true,
"comment": "Some text"
}
```
!!! success "Success response"
Response code: `HTTP/1.1 201 Created`
``` json
{
"key": "added",
"domain": "whitelisted.com"
}
```
!!! danger "Error response (duplicated domain)"
Response code: `HTTP/1.1 402 - Request failed`
``` json
{
"error": {
"key": "database_error",
"message": "Could not add domain to gravity database",
"data": {
"domain": "whitelisted.com",
"enabled": true,
"comment": "Some text",
"sql_msg": "UNIQUE constraint failed: domainlist.domain"
}
}
}
```
<!-- markdownlint-enable code-block-style -->
---
## DELETE: Remove item
Resources:
- `DELETE /admin/api/dns/whitelist/exact/<domain>`
- `DELETE /admin/api/dns/whitelist/regex/<domain>`
- `DELETE /admin/api/dns/blacklist/exact/<domain>`
- `DELETE /admin/api/dns/blacklist/regex/<domain>`
Requires authorization: Yes
### Parameters
The domain/regex to be removed is specified through the URL (`<domain>`).
### Example request
<!-- markdownlint-disable code-block-style -->
!!! example "Request"
=== "cURL"
**Domain**
``` bash
curl -X DELETE \
-H "Authorization: Token <your-access-token>" \
http://pi.hole:8080/admin/api/dns/whitelist/exact/whitelisted.com
```
**Regular expression**
``` bash
regex="$(echo "(^|\\.)facebook.com$" | jq -sRr '@uri')"
curl -X DELETE \
-H "Authorization: Token <your-access-token>" \
http://pi.hole:8080/admin/api/dns/whitelist/exact/${regex}
```
=== "Python"
**Domain**
``` python
import requests
URL = 'http://pi.hole:8080/admin/api/dns/whitelist/exact/whitelisted.com'
TOKEN = '<your-access-token>'
HEADERS = {'Authorization': f'Token {TOKEN}'}
response = requests.delete(URL, headers=HEADERS)
print(response.json())
```
**Regular expression**
``` python
import requests
import urllib
regex = urllib.parse.quote("(^|\\.)facebook.com$")
URL = 'http://pi.hole:8080/admin/api/dns/whitelist/exact/'
TOKEN = '<your-access-token>'
HEADERS = {'Authorization': f'Token {TOKEN}'}
response = requests.delete(URL + regex, headers=HEADERS)
print(response.json())
```
!!! success "Success response"
Response code: `HTTP/1.1 200 OK`
``` json
{
"key": "removed",
"domain": "whitelisted.com"
}
```
!!! danger "Error response (database permission error)"
Response code: `HTTP/1.1 402 - Request failed`
```json
{
"error": {
"key": "database_error",
"message": "attempt to write a readonly databas",
"data": {
"domain": "whitelisted.com",
"sql_msg": "Database not available"
}
}
}
```
<!-- markdownlint-enable code-block-style -->
{!abbreviations.md!}
+123
View File
@@ -0,0 +1,123 @@
# DNS - Status
## GET: Obtain current blocking status
Resource: `GET /admin/api/dns/status`
Requires authorization: No
### Parameters
None
### Examples
<!-- markdownlint-disable code-block-style -->
!!! example "Request"
=== "cURL"
``` bash
curl http://pi.hole:8080/admin/api/dns/status
```
=== "Python 3"
``` python
import requests
URL = 'http://pi.hole:8080/admin/api/dns/status'
response = requests.get(URL)
print(response.json())
```
!!! success "Response"
Response code: `HTTP/1.1 200 OK`
``` json
{
"status": "enabled"
}
```
<!-- markdownlint-enable code-block-style -->
## `POST`: Set/change blocking status
Resource: `POST /admin/api/dns/status`
Requires authorization: Yes
### Parameters
Name | Required | Type | Description | Default | Example
---- | -------- | ---- | ----------- | ------- | -------
`action` | Yes | String | Requested status | | `enable` or `disable`
`time` | Optional | Number | Requested delay until opposite status is enabled | `0` | `100` (seconds)
### Example
<!-- markdownlint-disable code-block-style -->
!!! example "Request"
=== "cURL"
``` bash
curl -X POST \
-H "Authorization: Token <your-access-token>" \
http://pi.hole:8080/admin/api/dns/status \
-H "Content-Type: application/json" \
-d @body.json
```
The content of `body.json` is like,
``` json
{
"action": "enable",
"time": 30
}
```
=== "Python 3"
``` python
import requests
URL = 'http://pi.hole:8080/admin/api/dns/status'
TOKEN = '<your-access-token>'
HEADERS = {'Authorization': f'Token {TOKEN}'}
data = json.load(open('body.json', 'rb'))
response = requests.post(
URL,
json=data,
headers=HEADERS,
)
print(response.json())
```
The content of `body.json` is like,
``` json
{
"action": "enable",
"time": 30
}
```
!!! success "Response"
Response code: `HTTP/1.1 200 OK`
``` json
{
"key": "enabled"
}
```
<!-- markdownlint-enable code-block-style -->
{!abbreviations.md!}
+126
View File
@@ -0,0 +1,126 @@
# API Reference
The Pi-hole API is organized around [REST](http://en.wikipedia.org/wiki/Representational_State_Transfer). Our API has predictable resource-oriented URLs, accepts [form-encoded](https://en.wikipedia.org/wiki/POST_(HTTP)#Use_for_submitting_web_forms) request bodies, returns reliable UTF-8 [JSON-encoded](http://www.json.org/) data for all API responses, and uses standard HTTP response codes, authentication, and verbs.
## Authentication
The Pi-hole API uses API keys to authenticate requests. You can view your API key in the Pi-hole Dashboard (**TODO: Link**).
!!! warning
Your API key carries many privileges, so be sure to keep it secure!
Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth if your Pi-hole is reachable from the outside.
The Authorization HTTP header can be specified with `Token <your-access-token>` to authenticate as a user and have the same permissions that the user itself.
<!-- markdownlint-disable code-block-style -->
!!! example active "Example request"
=== "cURL"
``` bash
curl -H "Authorization: Token <your-access-token>" \
http://pi.hole/admin/api/dns/status
```
=== "Python 3"
``` python
import requests
URL = 'http://pi.hole/admin/api/dns/status'
TOKEN = '<your-access-token>'
HEADERS = {'Authorization': f'Token {TOKEN}'}
response = requests.get(URL, headers=HEADERS)
print(response.json())
```
!!! success "Example reply: Success"
Response code: `HTTP/1.1 200 OK`
``` json
{
"status": "enabled"
}
```
!!! danger "Example reply: Error (unauthorized access)"
Response code: `HTTP/1.1 401 Unauthorized`
``` json
{
"error": {
"key": "unauthorized",
"message": "Unauthorized",
"data": null
}
}
```
<!-- markdownlint-enable code-block-style -->
Most but not all endpoints require authentication. API requests requiring authentication will also fail if no key is supplied.
## Errors
Pi-hole uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the `2xx` range indicate success. Codes in the `4xx` range indicate an error that failed given the information provided (e.g., a required parameter was omitted, missing authentication, etc.). Codes in the `5xx` range indicate an error with Pi-hole's API (these are rare).
Some `4xx` errors that could be handled programmatically include an error code that briefly explains the error reported.
### HTTP code summary
Code | Description | Interpretation
---- | ----------- | --------------
`200` | `OK` | Everything worked as expected.
`400` | `Bad Request` | The request was unacceptable, often due to a missing required parameter.
`401` | `Unauthorized` | No valid API key provided for endpoint requiring authorization.
`402` | `Request Failed` | The parameters were valid but the request failed.
`403` | `Forbidden` | The API key doesn't have permissions to perform the request.
`404` | `Not Found` | The requested resource doesn't exist.
`429` | `Too Many Requests` | Too many requests hit the API too quickly.
`500`, `502`, `503`, `504` | `Server Errors` | Something went wrong on Pi-hole's end. (These are rare.)
### JSON response
The form of replies to successful requests strongly depends on the selected endpoint, e.g.,
<!-- markdownlint-disable code-block-style -->
!!! success "Example reply: Success"
Response code: `HTTP/1.1 200 OK`
``` json
{
"status": "enabled"
}
```
In contrast, errors have a uniform appearance to ease a programatic treatment:
!!! danger "Example reply: Error (unauthorized access)"
``` json
{
"error": {
"key": "unauthorized",
"message": "Unauthorized",
"data": null
}
}
```
<!-- markdownlint-enable code-block-style -->
The items of the `error` object are always as follows:
Field | Type | Description
----- | ---- | -----------
`key` | String | Standardized key describing the error
`message` | String | Description of the error, may be shown to the user
In addition, `data` may contain a JSON object. This depends on the error itself and may contain further details such as the interpreted user data. If no additional data is available for this endpoint, `null` is returned instead of an object.
We recommend writing code that gracefully handles all possible API exceptions.
{!abbreviations.md!}
+5
View File
@@ -0,0 +1,5 @@
# API Documentation
This topic is still to be written...
{!abbreviations.md!}
-281
View File
@@ -1,281 +0,0 @@
Connect via e.g. `telnet 127.0.0.1 4711` or use `echo ">command" | nc 127.0.0.1 4711`
#### `>quit` {data-toc-label='quit'}
Closes the connection to the client
---
#### `>stats` {data-toc-label='stats'}
Get current statistics
```text
domains_being_blocked 116007
dns_queries_today 30163
ads_blocked_today 5650
ads_percentage_today 18.731558
unique_domains 1056
queries_forwarded 4275
queries_cached 20238
clients_ever_seen 11
unique_clients 9
status enabled
```
---
#### `>overTime` {data-toc-label='overTime'}
Get over time data (10 min intervals)
```text
1525546500 163 0
1525547100 154 1
1525547700 164 0
1525548300 167 0
1525548900 151 0
1525549500 143 0
[...]
```
---
#### `>top-domains` {data-toc-label='top-domains'}
Get top domains
```text
0 8462 x.y.z.de
1 236 safebrowsing-cache.google.com
2 116 pi.hole
3 109 z.y.x.de
4 93 safebrowsing.google.com
5 96 plus.google.com
[...]
```
Variant: `>top-domains (15)` to show (up to) 15 entries
---
#### `>top-ads` {data-toc-label='top-ads'}
Get top ad domains
```text
0 8 googleads.g.doubleclick.net
1 6 www.googleadservices.com
2 1 cdn.mxpnl.com
3 1 collector.githubapp.com
4 1 www.googletagmanager.com
5 1 s.zkcdn.net
[...]
```
Variant: `>top-ads (14)` to show (up to) 14 entries
---
#### `>top-clients` {data-toc-label='top-clients'}
Get recently active top clients (IP addresses + hostnames (if available))
```text
0 9373 192.168.2.1 router
1 484 192.168.2.2 work-machine
2 8 127.0.0.1 localhost
```
Variant: `>top-clients (9)` to show (up to) 9 client entries or `>top-clients withzero (15)` to show (up to) 15 clients even if they have not been active recently (see PR #124 for further details)
---
#### `>forward-dest` {data-toc-label='forward-dest'}
Get forward destinations (IP addresses + hostnames (if available)) along with the percentage. The first result (ID -2) will always be the percentage of domains answered from blocklists, whereas the second result (ID -1) will be the queries answered from the cache
```text
-2 18.70 blocklist blocklist
-1 67.10 cache cache
0 14.20 127.0.0.1 localhost
```
Variant: `>forward-dest unsorted` to show forward destinations in unsorted order (equivalent to using `>forward-names`)
---
#### `>querytypes` {data-toc-label='querytypes'}
Get collected query types percentage
```text
A (IPv4): 53.45
AAAA (IPv6): 45.32
ANY: 0.00
SRV: 0.64
SOA: 0.05
PTR: 0.54
TXT: 0.00
```
---
#### `>getallqueries` {data-toc-label='getallqueries'}
Get all queries that FTL has in memory
```text
1525554586 A fonts.googleapis.com 192.168.2.100 3 0 4 6
1525554586 AAAA fonts.googleapis.com 192.168.2.100 3 0 4 5
1525554586 A www.mkdocs.org 192.168.2.100 3 0 4 7
1525554586 AAAA www.mkdocs.org 192.168.2.100 2 0 3 21
1525554586 A squidfunk.github.io 192.168.2.100 2 0 3 20
1525554586 A pi-hole.net 192.168.2.100 3 0 4 5
1525554586 AAAA squidfunk.github.io 192.168.2.100 3 0 1 6
1525554586 AAAA pi-hole.net 192.168.2.100 2 0 1 18
1525554586 A github.com 192.168.2.100 3 0 4 5
1525554586 AAAA github.com 192.168.2.100 2 0 1 18
```
Variants:
- `>getallqueries (37)` show (up to) 37 latest entries,
- `>getallqueries-time 1483964295 1483964312` gets all queries that FTL has in its database in a limited time interval,
- `>getallqueries-time 1483964295 1483964312 (17)` show matches in the (up to) 17 latest entries,
- `>getallqueries-domain www.google.com` gets all queries that FTL has in its database for a specific domain name,
- `>getallqueries-client 2.3.4.5`: gets all queries that FTL has in its database for a specific client name *or* IP
---
#### `>recentBlocked` {data-toc-label='recentBlocked'}
Get most recently pi-holed domain name
```text
www.googleadservices.com
```
Variant: `>recentBlocked (4)` show the four most recent blocked domains
---
#### `>clientID` {data-toc-label='clientID'}
Get ID of currently connected client
```text
6
```
---
#### `>version` {data-toc-label='version'}
Get version information of the currently running FTL instance
```text
version v1.6-3-g106498d-dirty
tag v1.6
branch master
hash 106498d
date 2017-03-26 13:10:43 +0200
```
---
#### `>dbstats` {data-toc-label='dbstats'}
Get some statistics about `FTL`'s' long-term storage database (this request may take some time for processing in case of a large database file)
```text
queries in database: 2700304
database filesize: 199.20 MB
SQLite version: 3.23.1
```
---
#### `>domain pi-hole.net` {data-toc-label='domain'}
Get detailed information about domain (if available)
```text
Domain "pi-hole.net", ID: 254
Total: 179
Blocked: 0
Wildcard blocked: false
```
---
#### `>cacheinfo` {data-toc-label='cacheinfo'}
Get DNS server cache size and usage information
```text
cache-size: 500000
cache-live-freed: 0
cache-inserted: 15529
```
---
#### `>dns-port` {data-toc-label='dns-port'}
Get DNS port FTL is listening on
```text
53
```
Note that the port can also be `0` if someone decides to disable the DNS server part of Pi-hole
---
#### `>maxlogage` {data-toc-label='maxlogage'}
Get timespan of the statistics shown on the dashboard (in seconds)
```text
86400
```
---
#### `>gateway` {data-toc-label='gateway'}
Get the IP of the gateway of the default route and the corresponding interface
```text
192.168.0.1 enp2s0
```
Note that if no non-default route could be found, `0.0.0.0` and an empty interface string is returned
---
#### `>interfaces` {data-toc-label='interfaces'}
Get extended information of the interfaces of th Pi-hole device
```text
eth0 UP 1000 2.2GB 5.6GB 10.0.1.5 fd00:e57b:XXXX:210e:1a1,2a01:XXXX:c15b,fe80::2e5c:XXXX:4060
wlan0 DOWN -1 0.0B 0.0B - -
docker0 UP 10000 837.6MB 300.7MB 172.17.0.1,169.254.241.237 -
lo UP -1 48.0MB 48.0MB 127.0.0.1 -
wg0 UP -1 1.0GB 141.2MB 10.0.40.1 -
sum UP 0 4.2GB 6.1GB - -
```
Column definitions are:
1. Interface name
2. UP/DOWN status
3. Link speed in MBit/s (-1 means "Not available" (like link down) or "Not applicable" (like virtual interface))
4. TX bytes
5. RX bytes
6. Associated IPv4 addresses
7. Associated IPv6 addresses
The default interface (the one connected to the gateway) will always be the first. The sum will always be the last one - even if you have (for whatever reason) an interface called sum. Regarding the link speed: It won't work for most WiFi interfaces as the speed is not known at the kernel level. Instead, the drivers manage them dynamically depending on package loss, signal strength, etc. - in this case, you'll see link speed -1 as well
+32
View File
@@ -85,6 +85,15 @@ markdown_extensions:
# https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-a-glossary
auto_append:
- docs/abbreviations.md
# Include files in other documents like {!some/dir/in/docs/filename.md!}
- markdown_include.include:
base_path: docs
# Metadata support in pages
# (https://squidfunk.github.io/mkdocs-material/extensions/metadata/)
- meta
# Tabbed provides a syntax to easily add tabbed Markdown content.
# (https://facelessuser.github.io/pymdown-extensions/extensions/tabbed/)
- pymdownx.tabbed
nav:
- Overview: index.md
@@ -107,6 +116,25 @@ nav:
- 'Overview': database/gravity/index.md
- 'Group management': database/gravity/groups.md
- 'Database recovery': database/gravity/recovery.md
- 'Examples': database/gravity/example.md
- 'Pi-hole API':
- 'Overview': api/index.md
- 'DNS':
- 'Status': api/dns/status.md
- 'Domain Lists': api/dns/lists.md
- 'Cache Info': api/dns/cacheinfo.md
- 'FTL':
- 'Network': api/tbd.md
- 'Logs': api/tbd.md
- 'Statistics':
- 'Summary': api/tbd.md
- 'Over time': api/tbd.md
- 'Upstreams': api/tbd.md
- 'Top Items': api/tbd.md
- 'History': api/tbd.md
- 'Version': api/tbd.md
- 'Authentication': api/tbd.md
- 'Settings': api/tbd.md
- 'FTLDNS':
- 'Overview': ftldns/index.md
- 'Configuration': ftldns/configfile.md
@@ -135,6 +163,10 @@ nav:
- "Tutorial": regex/tutorial.md
- "Pi-hole extensions": regex/pi-hole.md
- "Approximate matching": regex/approximate.md
- 'Compatibility': ftldns/compatibility.md
- 'Install from source': ftldns/compile.md
- 'Debugging FTLDNS': ftldns/debugging.md
- 'In-depth manual': ftldns/in-depth.md
- 'Docker':
- 'DHCP': docker/DHCP.md
- 'Contributing':