diff --git a/Dockerfile b/Dockerfile index ff4ed62..7708939 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,33 +1,26 @@ -### -### BIND -### -FROM debian:stable +FROM debian:buster-slim MAINTAINER "cytopia" -### -### Labels -### -LABEL \ - name="cytopia's Bind Image" \ - image="bind" \ - vendor="cytopia" \ - license="MIT" \ - build-date="2018-01-11" - - ### ### Install ### -RUN apt-get update && apt-get -y install \ - bind9 \ - && rm -r /var/lib/apt/lists/* +RUN set -x \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + bind9 \ + dnsutils \ + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $fetchDeps \ + && rm -r /var/lib/apt/lists/* \ + && mkdir /var/log/named \ + && chown bind:bind /var/log/named \ + && chmod 0755 /var/log/named ### ### Bootstrap Scipts ### -COPY ./scripts/docker-entrypoint.sh / +COPY ./data/docker-entrypoint.sh / ### diff --git a/README.md b/README.md index 44ec11a..8fe793b 100644 --- a/README.md +++ b/README.md @@ -1,102 +1,376 @@ # Bind Docker -**Latest build:** 2018-01-11 +[![Build Status](https://travis-ci.org/cytopia/docker-bind.svg?branch=master)](https://travis-ci.org/cytopia/docker-bind) +[![Join the chat at https://gitter.im/devilbox/Lobby](https://badges.gitter.im/devilbox/Lobby.svg)](https://gitter.im/devilbox/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![](https://images.microbadger.com/badges/version/cytopia/bind.svg)](https://microbadger.com/images/cytopia/bind "bind") +[![](https://images.microbadger.com/badges/image/cytopia/bind.svg)](https://microbadger.com/images/cytopia/bind "bind") +[![](https://images.microbadger.com/badges/license/cytopia/bind.svg)](https://microbadger.com/images/cytopia/bind "bind") + +---- + +Bind caching DNS server based on Debian slim with support for DNS forwarders, infinite wild-card DNS, infinite extra hosts, reverse DNS, DNSSEC timing settings and others. -[![Build Status](https://travis-ci.org/cytopia/docker-bind.svg?branch=master)](https://travis-ci.org/cytopia/docker-bind) [![](https://images.microbadger.com/badges/version/cytopia/bind.svg)](https://microbadger.com/images/cytopia/bind "bind") [![](https://images.microbadger.com/badges/image/cytopia/bind.svg)](https://microbadger.com/images/cytopia/bind "bind") [![](https://images.microbadger.com/badges/license/cytopia/bind.svg)](https://microbadger.com/images/cytopia/bind "bind") -[![cytopia/bind](http://dockeri.co/image/cytopia/bind)](https://hub.docker.com/r/cytopia/bind/) +| Docker Hub | Upstream Project | +|------------|------------------| +| | | ---- -**Bind caching DNS server on Debian with wild-card domain support** +**Table of Contents** + +1. [Environmental variables](#environmental-variables) + 1. [Required environmental variables](#required-environmental-variables) + 2. [Optional environmental variables](#optional-environmental-variables) + 1. [DEBUG_ENTRYPOINT](#debug_entrypoint) + 2. [DOCKER_LOGS](#docker_logs) + 3. [WILDCARD_DNS](#wildcard_dns) + 4. [EXTRA_HOSTS](#extra_hosts) + 5. [DNSSEC_VALIDATE](#dnssec_validate) + 5. [DNS_FORWARDER](#dns_forwarder) + 6. [TTL_TIME](#ttl_time) + 7. [REFRESH_TIME](#refresh_time) + 8. [RETRY_TIME](#retry_time) + 9. [EXPIRY_TIME](#expiry_time) + 10. [MAX_CACHE_TIME](#max_cache_time) +2. [Default mountpoints](#default-mountpoints) +3. [Default ports](#default-ports) +4. [Examples](#examples) + 1. [Default run](#default-run) + 2. [Wildcard domain](#wildcard-domain) + 3. [Wildcard subdomain](#wildcard-subdomain) + 4. [Wildcard TLD](#wildcard-tld) + 5. [Wildcard TLD and reverse DNS entry](#wildcard-tld-and-reverse-dns-entry) + 6. [Wildcard TLD and DNS resolver](#wildcard-tld-and-dns-resolver) + 7. [Wildcard TLD, DNS resolver and extra hosts](#wildcard-tld-dns-resolver-and-extra-hosts) +5. [Host integration](#host-integration) +6. [Support](#support) +7. [License](#license) + +--- + +## Environmental variables + +### Required environmental variables -[![Devilbox](https://raw.githubusercontent.com/cytopia/devilbox/master/.devilbox/www/htdocs/assets/img/devilbox_80.png)](https://github.com/cytopia/devilbox) +- None -This docker image is part of the **[devilbox](https://github.com/cytopia/devilbox)** +### Optional environmental variables ----- +| Variable | Type | Default | Description | +|--------------------|--------|-----------|-------------| +| `DEBUG_ENTRYPOINT` | bool | `0` | Show shell commands executed during start.
Values: `0`, `1` or `2` | +| `DOCKER_LOGS` | bool | `0` | Set to `1` to log info and queries to Docker logs. | +| `WILDCARD_DNS` | string | | Add one or more tld's, domains or subdomains as catch-all for a specific IP address or CNAME. Reverse DNS is optional and can also be specified. | +| `EXTRA_HOSTS` | string | | Add one or more hosts (CNAME: tld's, domains, subdomains) to map to a specific IP address or CNAME. Reverse DNS is optional and can also be specified. | +| `DNSSEC_VALIDATE` | string | `no` | Control the behaviour of DNSSEC validation. The default is to not validate: `no`. Other possible values are: `yes` and `auto`. | +| `DNS_FORWARDER` | string | | Specify a comma separated list of IP addresses as custom DNS resolver. This is useful if your LAN already has a DNS server which adds custom/internal domains and you still want to keep them in this DNS server
Example: `DNS_FORWARDER=8.8.8.8,8.8.4.4` | +| `TTL_TIME` | int | `3600` | (Time in seconds) See [BIND TTL](http://www.zytrax.com/books/dns/apa/ttl.html) and [BIND SOA](http://www.zytrax.com/books/dns/ch8/soa.html)| +| `REFRESH_TIME` | int | `1200` | (Time in seconds) See [BIND SOA](http://www.zytrax.com/books/dns/ch8/soa.html) | +| `RETRY_TIME` | int | `180` | (Time in seconds) See [BIND SOA](http://www.zytrax.com/books/dns/ch8/soa.html) | +| `EXPIRY_TIME` | int | `1209600` | (Time in seconds) See [BIND SOA](http://www.zytrax.com/books/dns/ch8/soa.html) | +| `MAX_CACHE_TIME` | int | `10800` | (Time in seconds) See [BIND SOA](http://www.zytrax.com/books/dns/ch8/soa.html) | -## Options +#### DEBUG_ENTRYPOINT -### Environmental variables +* If set to `0`, only warnings and errors are shown +* If set to `1`, info, warnings and errors are shown +* If set to `2`, info, warnings and errors are shown, as well as commands executed during startup -#### Required environmental variables +#### DOCKER_LOGS -- None +* If set to `0`, no additional logging is done during run-time +* If set to `1`, BIND is more verbose during run-time and shows asked queries as well as general information + +#### WILDCARD_DNS + +The `WILDCARD_DNS` option allows you to specify one or more multiple catch-all DNS zones which can either +be a full TLD, a domain or any kind of subdomain. It allows you to map your catch-all to a specific +IP address or even a CNAME (if it is resolvable by public DNS servers). Optionally you can also assign +the reverse DNS name (PTR record). + +The general format is as follows, whereas the string in square brackets it optional and responsible +for the reverse DNS (PTR records): +```bash +# Structure +WILDCARD_DNS='tld1=1.1.1.1[=tld],tld2=2.2.2.2[=tld2]' +WILDCARD_DNS='tld1=CNAME1[=tld],tld2=CNAME2[=tld2]' +``` + +Some examples: +```bash +# 1. One entry: +# The following catches all queries to *.tld and redirects them to 192.168.0.1 +WILDCARD_DNS='tld=192.168.0.1' + +# 2. Two entries: +# The following catches all queries to *.tld and redirects them to 192.168.0.1 +# As well as all queries from *.example.org and redirects them to 192.168.0.2 +WILDCARD_DNS='tld=192.168.0.1,example.org=192.168.0.2' + +# 3. Using CNAME's for resolving: +# The following catches all queries to *.tld and redirects them to whatever +# IP example.org resolved to +WILDCARD_DNS='tld=example.org' + +# 4. Adding reverse DNS: +# The following catches all queries to *.tld and redirects them to 192.168.0.1 +# As well as adding reverse DNS from 192.168.0.1 to resolve to tld +WILDCARD_DNS='tld=192.168.0.1=tld' + +# 5. Complex example +# The following catches all queries to *.tld and redirects them to whatever +# IP example.org resolved to. Additionally it adds a reverse DNS record from example.org's +# IP to resolve to tld (PTR record) +# It also adds another catch-all for the subdomain of *.cytopia.tld which will point to 192.168.0.1 +# Including a reverse DNS record back to cytopia.tld +WILDCARD_DNS='tld=example.org=tld,cytopia.tld=192.168.0.1=cytopia.tld' +``` + +#### EXTRA_HOSTS -#### Optional environmental variables +The `EXTRA_HOSTS` option almost works like the `WILDCARD_DNS` option, except that no wildcard is added, +but rather exactly the host you have specified. -| Variable | Type | Default | Description | -|----------|------|---------|-------------| -| DEBUG_COMPOSE_ENTRYPOINT | bool | `0` | Show shell commands executed during start.
Value: `0` or `1` | -| WILDCARD_DOMAIN | string | `` | Specify a wild-card domain to add during startup.
Example: `WILDCARD_DOMAIN=example.com` or `WILDCARD_DOMAIN=local` or `WILDCARD_DOMAIN=loc`
**Note:** `$WILDCARD_ADDRESS` must also be specified. | -| WILDCARD_ADDRESS | string | `` | Specify to which IP address the wild-card domain should point to.
Example: `WILDCARD_ADDRESS=192.168.0.1`
**Note:** $WILDCARD_DOMAIN` must also be specidied. | -| DNS_FORWARDER | string| `` | Specify a comma separated list of IP addresses as custom DNS resolver. This is useful if your LAN already has a DNS server which adds custom/internal domains and you still want to keep them in this DNS server
Example: `DNS_FORWARDER=8.8.8.8,8.8.4.4` | +This is useful if you want to add extra hosts to your setup just like the Docker Compose option +[extra_hosts](https://docs.docker.com/compose/compose-file/#extra_hosts) -### Default mount points +```bash +# Structure +EXTRA_HOSTS='host1=1.1.1.1[=host1],host2=2.2.2.2[=host2]' +EXTRA_HOSTS='host1=CNAME1[=host1],host2=CNAME2[=host2]' +``` + +Some examples: +```bash +# 1. One entry: +# The following extra host 'tld' is added and will always point to 192.168.0.1. +# When reverse resolving '192.168.0.1' it will answer with 'tld'. +EXTRA_HOSTS='tld=192.168.0.1' + +# 2. One entry: +# The following extra host 'my.host' is added and will always point to 192.168.0.1. +# When reverse resolving '192.168.0.1' it will answer with 'my.host'. +EXTRA_HOSTS='my.host=192.168.0.1' + +# 3. Two entries: +# The following extra host 'tld' is added and will always point to 192.168.0.1. +# When reverse resolving '192.168.0.1' it will answer with 'tld'. +# A second extra host 'example.org' is added and always redirects to 192.168.0.2 +# When reverse resolving '192.168.0.2' it will answer with 'example.org'. +EXTRA_HOSTS='tld=192.168.0.1,example.org=192.168.0.2' + +# 4. Using CNAME's for resolving: +# The following extra host 'my.host' is added and will always point to whatever +# IP example.org resolves to. +# When reverse resolving '192.168.0.1' it will answer with 'my.host'. +EXTRA_HOSTS='my.host=example.org' + +# 5. Adding reverse DNS: +# The following extra host 'my.host' is added and will always point to whatever +# IP example.org resolves to. +# As well as adding reverse DNS from 192.168.0.1 to resolve to tld +EXTRA_HOSTS='tld=192.168.0.1=tld' +``` + +#### DNSSEC_VALIDATE + +The `DNSSEC_VALIDATE` variable defines the DNSSEC validation. Default is to not validate (`no`). +Possible values are: + +* `yes` - DNSSEC validation is enabled, but a trust anchor must be manually configured. No validation will actually take place. +* `no` - DNSSEC validation is disabled, and recursive server will behave in the "old fashioned" way of performing insecure DNS lookups, until you have manually configured at least one trusted key. +* `auto` - DNSSEC validation is enabled, and a default trust anchor (included as part of BIND) for the DNS root zone is used. + +#### DNS_FORWARDER + +By default this dockerized BIND is not acting as a DNS forwarder, so it will not have any external +DNS available. In order to apply external DNS forwarding, you will have to specify one or more external +DNS server. This could be the one's from google for example (`8.8.8.8` and `8.8.4.4`) or any others +you prefer. In case your LAN has its own DNS server with already defined custom DNS records that you +need to make available, you should use them. + +```bash +# Structure (comma separated list of IP addresses) +DNS_FORWARDER='8.8.8.8,8.8.4.4' +``` + +Some examples +```bash +DNS_FORWARDER='8.8.8.8' +DNS_FORWARDER='8.8.8.8,192.168.0.10' +``` + +#### TTL_TIME +Specify time in seconds. +For more information regarding this setting, see [BIND TTL](http://www.zytrax.com/books/dns/apa/ttl.html) and [BIND SOA](http://www.zytrax.com/books/dns/ch8/soa.html) + +#### REFRESH_TIME +Specify time in seconds. +For more information regarding this setting, see [BIND SOA](http://www.zytrax.com/books/dns/ch8/soa.html) + +#### RETRY_TIME +Specify time in seconds. +For more information regarding this setting, see [BIND SOA](http://www.zytrax.com/books/dns/ch8/soa.html) + +#### EXPIRY_TIME +Specify time in seconds. +For more information regarding this setting, see [BIND SOA](http://www.zytrax.com/books/dns/ch8/soa.html) + +#### MAX_CACHE_TIME +Specify time in seconds. +For more information regarding this setting, see [BIND SOA](http://www.zytrax.com/books/dns/ch8/soa.html) + + +## Default mount points - None -### Default ports +## Default ports | Docker | Description | |--------|--------------| | 53 | DNS Resolver | | 53/udp | DNS Resolver | -## Usage -**1. Start normally (caching DNS only)** +## Examples + +The following examples start the container in foreground and use `-i`, so you can easily stop +it by pressing ` + c`. For a production run, you would rather use `-d` to send it to the +background. + +#### Default run + +Exposing the port is mandatory if you want to use it for your host operating system. ```bash $ docker run -i \ - -p 127.0.0.1:53:53 \ - -p 127.0.0.1:53/udp:53/udp \ + -p 53:53/tcp \ + -p 53:53/udp \ -t cytopia/bind ``` -**2. Add wildcard Domain (*.example.com)** +#### Wildcard domain + +Let's add a wildcard zone for `*.example.com`. All subdomains as well as the main domain will resolve +to `192.168.0.1`. +```bash +$ docker run -i \ + -p 53:53/tcp \ + -p 53:53/udp \ + -e WILDCARD_DNS='example.com=192.168.0.1' \ + -t cytopia/bind +``` -`example.com` and all its subdomains (such as: `whatever.example.com`) will point to `192.168.0.1`: +#### Wildcard subdomain +Let's add a wildcard zone for `*.aws.example.com`. All subdomains as well as the main subdomain will resolve +to `192.168.0.1`. ```bash $ docker run -i \ - -p 127.0.0.1:53:53 \ - -p 127.0.0.1:53/udp:53/udp \ - -e WILDCARD_DOMAIN=example.com \ - -e WILDCARD_ADDRESS=192.168.0.1 \ + -p 53:53/tcp \ + -p 53:53/udp \ + -e WILDCARD_DNS='aws.example.com=192.168.0.1' \ -t cytopia/bind ``` -**3. Add wildcard Domain (TLD)** +#### Wildcard TLD + +Let's add a wildcard zone for `*.loc`. All domains, subdomain as well as the TLD itself will resolve +to `192.168.0.4`. +```bash +$ docker run -i \ + -p 53:53/tcp \ + -p 53:53/udp \ + -e WILDCARD_DNS='loc=192.168.0.4' \ + -t cytopia/bind +``` -`loc` and all its subdomains (such as: `hostname.loc`) will point to `192.168.0.1`: +#### Wildcard TLD and reverse DNS entry +Let's add a wildcard zone for `*.loc`. All domains, subdomain as well as the TLD itself will resolve +to `192.168.0.4`. Additionally we specify that `host.loc` will be the reverse loopup for `192.168.0.4`. ```bash $ docker run -i \ - -p 127.0.0.1:53:53 \ - -p 127.0.0.1:53/udp:53/udp \ - -e WILDCARD_DOMAIN=loc \ - -e WILDCARD_ADDRESS=192.168.0.1 \ + -p 53:53/tcp \ + -p 53:53/udp \ + -e WILDCARD_DNS='loc=192.168.0.4=host.loc' \ -t cytopia/bind ``` -**4. Add wildcard Domain (TLD) and use your corporate DNS server as resolver** +#### Wildcard TLD and DNS resolver + +Let's add a wildcard zone for `*.loc`. All domains, subdomain as well as the TLD itself will resolve +to `192.168.0.4`. + +Let's also hook in our imaginary corporate DNS server into this container, so we can make use of +any already defined custom DNS entries by that nameserver. * `loc` and all its subdomains (such as: `hostname.loc`) will point to `192.168.0.1`: * Your corporate DNS servers are `10.0.15.1` and `10.0.15.2` ```bash $ docker run -i \ - -p 127.0.0.1:53:53 \ - -p 127.0.0.1:53/udp:53/udp \ - -e WILDCARD_DOMAIN=loc \ - -e WILDCARD_ADDRESS=192.168.0.1 \ - -e DNS_FORWARDER=10.0.15.1,10.0.15,2 \ + -p 53:53/tcp \ + -p 53:53/udp \ + -e WILDCARD_DNS='loc=192.168.0.1' \ + -e DNS_FORWARDER=10.0.15.1,10.0.15.2 \ -t cytopia/bind ``` -## Version +#### Wildcard TLD, DNS resolver and extra hosts + +* `loc` and all its subdomains (such as: `hostname.loc`) will point to `192.168.0.1`: +* Your corporate DNS servers are `10.0.15.1` and `10.0.15.2` +* Also add two extra hosts with custom DNS: + - host5.loc -> 192.168.0.2 + - host5.org -> 192.168.0.3 + +```bash +$ docker run -i \ + -p 53:53/tcp \ + -p 53:53/udp \ + -e WILDCARD_DNS='loc=192.168.0.1' \ + -e EXTRA_HOSTS='host5.loc=192.168.0.2,host5.org=192.168.0.3' \ + -e DNS_FORWARDER=10.0.15.1,10.0.15.2 \ + -t cytopia/bind +``` + + +## Host integration + +You can run this DNS container locally without having to worry to affect any corporate DNS server +that are given to you via DHCP. + +Add the following line to the very beginning to `/etc/dhcp/dhclient.conf`: +```bash +prepend domain-name-servers 127.0.0.1; +``` +Restart network manager +```bash +# Via service command +$ sudo service network-manager restart + +# Or the systemd way +$ sudo systemctl restart network-manager +``` + +This will make sure that whenever your `/etc/resolv.conf` is deployed, you will have `127.0.0.1` +as the first entry and also make use of any other DNS server which are deployed via the LAN's DHCP server. + +If `cytopia/bind` is not running, it does not affect the name resolution, because you will still +have entries in `/etc/resolv.conf`. + + +## Support + +If you need support, join the Gitter Chat: [![Join the chat at https://gitter.im/devilbox/Lobby](https://badges.gitter.im/devilbox/Lobby.svg)](https://gitter.im/devilbox/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + + +## License + +**[MIT License](LICENSE.md)** -BIND 9.10.3 +Copyright (c) 2016 [cytopia](https://github.com/cytopia) diff --git a/build/docker-attach.sh b/build/docker-attach.sh deleted file mode 100755 index 6aaa6ac..0000000 --- a/build/docker-attach.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh -eu - - -### -### Globals -### -CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)/.." - - -### -### Funcs -### -run() { - _cmd="${1}" - _red="\033[0;31m" - _green="\033[0;32m" - _reset="\033[0m" - _user="$(whoami)" - - printf "${_red}%s \$ ${_green}${_cmd}${_reset}\n" "${_user}" - sh -c "LANG=C LC_ALL=C ${_cmd}" -} - - -### -### Checks -### - -# Check Dockerfile -if [ ! -f "${CWD}/Dockerfile" ]; then - echo "Dockerfile not found in: ${CWD}/Dockerfile." - exit 1 -fi - -# Get docker Name -if ! grep -q 'image=".*"' "${CWD}/Dockerfile" > /dev/null 2>&1; then - echo "No 'image' LABEL found" - exit -fi - -# Make sure exactly 1 container is running -NAME="$( grep 'image=".*"' "${CWD}/Dockerfile" | sed 's/^[[:space:]]*//g' | awk -F'"' '{print $2}' )" -COUNT="$( docker ps | grep -c "cytopia/${NAME}" || true)" -if [ "${COUNT}" != "1" ]; then - echo "${COUNT} 'cytopia/${NAME}' container running. Unable to attach." - exit 1 -fi - - -### -### Attach -### -DID="$(docker ps | grep "cytopia/${NAME}" | awk '{print $1}')" - -echo "Attaching to: cytopia/${NAME}" -run "docker exec -it ${DID} env TERM=xterm /bin/bash -l" diff --git a/build/docker-build.sh b/build/docker-build.sh index 7d3cd37..b902c94 100755 --- a/build/docker-build.sh +++ b/build/docker-build.sh @@ -32,37 +32,9 @@ if [ ! -f "${CWD}/Dockerfile" ]; then exit 1 fi -# Get docker Name -if ! grep -q 'image=".*"' "${CWD}/Dockerfile" > /dev/null 2>&1; then - echo "No 'image' LABEL found" - exit -fi -NAME="$( grep 'image=".*"' "${CWD}/Dockerfile" | sed 's/^[[:space:]]*//g' | awk -F'"' '{print $2}' )" -DATE="$( date '+%Y-%m-%d' )" - - ### ### Build ### # Build Docker -run "docker build -t cytopia/${NAME} ${CWD}" - -# Update build date -run "sed -i'' 's/\*\*Latest\sbuild.*/**Latest build:** ${DATE}<\/small>/g' ${CWD}/README.md" -run "sed -i'' 's/build-date=\".*\"/build-date=\"${DATE}\"/g' ${CWD}/Dockerfile" - - -### -### Test -### - -docker run -d --rm --name my_tmp_${NAME} -t cytopia/${NAME} -BIND_VERSION="$( docker exec my_tmp_${NAME} named -V | grep -oiE '^BIND[[:space:]]+[0-9.]+' )" -docker stop "$(docker ps | grep "my_tmp_${NAME}" | awk '{print $1}')" - -echo "${BIND_VERSION}" - -sed -i'' '/##[[:space:]]Version/q' "${CWD}/README.md" -echo "" >> "${CWD}/README.md" -echo "${BIND_VERSION}" >> "${CWD}/README.md" +run "docker build -t cytopia/bind ${CWD}" diff --git a/build/docker-enter.sh b/build/docker-enter.sh deleted file mode 100755 index d51036e..0000000 --- a/build/docker-enter.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh -eu - - -### -### Globals -### -CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)/.." - - -### -### Funcs -### -run() { - _cmd="${1}" - _red="\033[0;31m" - _green="\033[0;32m" - _reset="\033[0m" - _user="$(whoami)" - - printf "${_red}%s \$ ${_green}${_cmd}${_reset}\n" "${_user}" - sh -c "LANG=C LC_ALL=C ${_cmd}" -} - - -### -### Checks -### - -# Check Dockerfile -if [ ! -f "${CWD}/Dockerfile" ]; then - echo "Dockerfile not found in: ${CWD}/Dockerfile." - exit 1 -fi - -# Get docker Name -if ! grep -q 'image=".*"' "${CWD}/Dockerfile" > /dev/null 2>&1; then - echo "No 'image' LABEL found" - exit -fi -NAME="$( grep 'image=".*"' "${CWD}/Dockerfile" | sed 's/^[[:space:]]*//g' | awk -F'"' '{print $2}' )" - - -### -### Enter -### -run "docker run -i --entrypoint /bin/bash -t cytopia/${NAME}" diff --git a/build/docker-push.sh b/build/docker-push.sh deleted file mode 100755 index bfd06b6..0000000 --- a/build/docker-push.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/sh -eu - - -### -### Globals -### -CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)/.." - - -### -### Checks -### - -# Check Dockerfile -if [ ! -f "${CWD}/Dockerfile" ]; then - echo "Dockerfile not found in: ${CWD}/Dockerfile." - exit 1 -fi - -# Get docker Name -if ! grep -q 'image=".*"' "${CWD}/Dockerfile" > /dev/null 2>&1; then - echo "No 'image' LABEL found" - exit -fi -NAME="$( grep 'image=".*"' "${CWD}/Dockerfile" | sed 's/^[[:space:]]*//g' | awk -F'"' '{print $2}' )" - - -### -### Docker hub variables -### -USR="cytopia" -IMG="${USR}/${NAME}" -REG="https://index.docker.io/v1/" - - -## -## Functions -## -get_docker_id() { - _did="$( docker images | grep "${IMG}\s" | grep "\slatest\s" | awk '{print $3}' )" - echo "${_did}" -} -is_logged_in() { - _user="$( docker info | grep "${USR}" | awk '{print $2}' )" - _dhub="$( docker info | grep 'Registry' | awk '{print $2}' )" - - if [ "${_user}" = "${USR}" ]; then - if [ "${_dhub}" = "${REG}" ]; then - return 0 - fi - fi - return 1 -} -run() { - _cmd="${1}" - _red="\033[0;31m" - _green="\033[0;32m" - _reset="\033[0m" - _user="$(whoami)" - - printf "${_red}%s \$ ${_green}${_cmd}${_reset}\n" "${_user}" - sh -c "LANG=C LC_ALL=C ${_cmd}" -} - - - -## -## Entrypoint -## -#run "docker build --no-cache -t ${IMG} ." -run "docker tag $( get_docker_id ) ${IMG}:latest" -if ! is_logged_in; then - run "docker login" -fi -run "docker push ${IMG}" diff --git a/build/docker-rebuild.sh b/build/docker-rebuild.sh index 05db456..bd432e5 100755 --- a/build/docker-rebuild.sh +++ b/build/docker-rebuild.sh @@ -32,37 +32,9 @@ if [ ! -f "${CWD}/Dockerfile" ]; then exit 1 fi -# Get docker Name -if ! grep -q 'image=".*"' "${CWD}/Dockerfile" > /dev/null 2>&1; then - echo "No 'image' LABEL found" - exit -fi -NAME="$( grep 'image=".*"' "${CWD}/Dockerfile" | sed 's/^[[:space:]]*//g' | awk -F'"' '{print $2}' )" -DATE="$( date '+%Y-%m-%d' )" - - ### ### Build ### # Build Docker -run "docker build --no-cache -t cytopia/${NAME} ${CWD}" - -# Update build date -run "sed -i'' 's/\*\*Latest\sbuild.*/**Latest build:** ${DATE}<\/small>/g' ${CWD}/README.md" -run "sed -i'' 's/build-date=\".*\"/build-date=\"${DATE}\"/g' ${CWD}/Dockerfile" - - -### -### Test -### - -docker run -d --rm --name my_tmp_${NAME} -t cytopia/${NAME} -BIND_VERSION="$( docker exec my_tmp_${NAME} named -V | grep -oiE '^BIND[[:space:]]+[0-9.]+' )" -docker stop "$(docker ps | grep "my_tmp_${NAME}" | awk '{print $1}')" - -echo "${BIND_VERSION}" - -sed -i'' '/##[[:space:]]Version/q' "${CWD}/README.md" -echo "" >> "${CWD}/README.md" -echo "${BIND_VERSION}" >> "${CWD}/README.md" +run "docker build --no-cache -t cytopia/bind ${CWD}" diff --git a/build/docker-start.sh b/build/docker-start.sh deleted file mode 100755 index be05158..0000000 --- a/build/docker-start.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh -eu - - -### -### Globals -### -CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)/.." - - -### -### Funcs -### -run() { - _cmd="${1}" - _red="\033[0;31m" - _green="\033[0;32m" - _reset="\033[0m" - _user="$(whoami)" - - printf "${_red}%s \$ ${_green}${_cmd}${_reset}\n" "${_user}" - sh -c "LANG=C LC_ALL=C ${_cmd}" -} - -_args="" -if [ "${#}" != "0" ]; then - _args="${*}" -fi - -### -### Checks -### - -# Check Dockerfile -if [ ! -f "${CWD}/Dockerfile" ]; then - echo "Dockerfile not found in: ${CWD}/Dockerfile." - exit 1 -fi - -# Get docker Name -if ! grep -q 'image=".*"' "${CWD}/Dockerfile" > /dev/null 2>&1; then - echo "No 'image' LABEL found" - exit -fi -NAME="$( grep 'image=".*"' "${CWD}/Dockerfile" | sed 's/^[[:space:]]*//g' | awk -F'"' '{print $2}' )" - -### -### Run -### -run "docker run -i ${_args} -t cytopia/${NAME}" diff --git a/data/docker-entrypoint.sh b/data/docker-entrypoint.sh new file mode 100755 index 0000000..b5a74ec --- /dev/null +++ b/data/docker-entrypoint.sh @@ -0,0 +1,621 @@ +#!/usr/bin/env bash + +set -e +set -u +set -o pipefail + + +################################################################################# +# VARIABLES +################################################################################# + +### +### Variables +### + +NAMED_DIR="/etc/bind" +NAMED_CONF="${NAMED_DIR}/named.conf" +NAMED_OPT_CONF="${NAMED_DIR}/named.conf.options" +NAMED_LOG_CONF="${NAMED_DIR}/named.conf.logging" + + +### +### Default values of injectables environment variables +### +DEFAULT_DEBUG_ENTRYPOINT=1 +DEFAULT_DOCKER_LOGS=0 +DEFAULT_DNSSEC_VALIDATE="no" + +### +### Default time variables (time in seconds) +### +DEFAULT_TTL_TIME=3600 +DEFAULT_REFRESH_TIME=1200 +DEFAULT_RETRY_TIME=180 +DEFAULT_EXPIRY_TIME=1209600 +DEFAULT_MAX_CACHE_TIME=10800 + + + +################################################################################# +# HELPER FUNCTIONS +################################################################################# + +### +### Log to stdout/stderr +### +log() { + local type="${1}" # ok, warn or err + local message="${2}" # msg to print + local debug="${3}" # 0: only warn and error, >0: ok and info + + local clr_ok="\033[0;32m" + local clr_info="\033[0;34m" + local clr_warn="\033[0;33m" + local clr_err="\033[0;31m" + local clr_rst="\033[0m" + + if [ "${type}" = "ok" ]; then + if [ "${debug}" -gt "0" ]; then + printf "${clr_ok}[OK] %s${clr_rst}\n" "${message}" + fi + elif [ "${type}" = "info" ]; then + if [ "${debug}" -gt "0" ]; then + printf "${clr_info}[INFO] %s${clr_rst}\n" "${message}" + fi + elif [ "${type}" = "warn" ]; then + printf "${clr_warn}[WARN] %s${clr_rst}\n" "${message}" 1>&2 # stdout -> stderr + elif [ "${type}" = "err" ]; then + printf "${clr_err}[ERR] %s${clr_rst}\n" "${message}" 1>&2 # stdout -> stderr + else + printf "${clr_err}[???] %s${clr_rst}\n" "${message}" 1>&2 # stdout -> stderr + fi +} + + +### +### Wrapper for run_run command +### +run() { + local cmd="${1}" # command to execute + local debug="${2}" # show commands if debug level > 1 + + local clr_red="\033[0;31m" + local clr_green="\033[0;32m" + local clr_reset="\033[0m" + + if [ "${debug}" -gt "1" ]; then + printf "${clr_red}%s \$ ${clr_green}${cmd}${clr_reset}\n" "$( whoami )" + fi + /bin/sh -c "LANG=C LC_ALL=C ${cmd}" +} + + +### +### Check if a value is an integer +### +is_int() { + printf "%d" "${1}" >/dev/null 2>&1 && return 0 || return 1; +} + + +### +### Check if a value is a valid IP address +### +is_ip4() { + # IP is not in correct format + if ! echo "${1}" | grep -Eq '^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$'; then + return 1 + fi + + # 4 IP octets + local o1 + local o2 + local o3 + local o4 + + # Get each octet + o1="$( echo "${1}" | awk -F'.' '{print $1}' )" + o2="$( echo "${1}" | awk -F'.' '{print $2}' )" + o3="$( echo "${1}" | awk -F'.' '{print $3}' )" + o4="$( echo "${1}" | awk -F'.' '{print $4}' )" + + # Cannot start with 0 and all must be below 256 + if [ "${o1}" -lt "1" ] || \ + [ "${o1}" -gt "255" ] || \ + [ "${o2}" -gt "255" ] || \ + [ "${o3}" -gt "255" ] || \ + [ "${o4}" -gt "255" ]; then + return 1 + fi + # All tests passed + return 0 +} + +### +### Check if a value is a valid cname +### +is_cname() { + local string="${1}" + local regex='^([0-9A-Za-z]{2,}\.[0-9A-Za-z]{2,3}\.[0-9A-Za-z]{2,3}|[0-9A-Za-z]{2,}\.[0-9A-Za-z]{2,3})$' + + echo "${string}" | grep -Eq "${regex}" +} + + + +################################################################################# +# ACTION FUNCTIONS +################################################################################# + +# Add Bind options with or without forwarder +# +# @param config_file Where to store this configuration in. +# @param dnssec_validate dnssec-validation setting +# @param forwarders formated (newline separated and trailing semi-colon) string of ip addr +add_options() { + local config_file="${1}" + local dnssec_validate="${2}" + local forwarders="${3}" + + { + echo "options {" + echo " directory \"/var/cache/bind\";" + echo " dnssec-validation ${dnssec_validate};" + echo " auth-nxdomain no; # conform to RFC1035" + echo " listen-on-v6 { any; };" + if [ -n "${forwarders}" ]; then + echo " forwarders {" + printf "${forwarders}" + echo " };" + fi + echo "};" + } > "${config_file}" +} + + +# Add wildcard DNS zone. +# +# @param domain Domain name to create zone for. +# @param address IP address to point all records to. +# @param config_file Configuration file path. +# @param wildcard 1: Enable wildcard, 0: Normal host +# @param reverse String of reverse DNS name or empty for no reverse DNS +# @param debug_level +add_wildcard_zone() { + # DNS setting variables + local domain="${1}" + local address="${2}" + local conf_file="${3}" + local wildcard="${4}" + local reverse="${5}" + # DNS time variables + local ttl_time="${6}" + local refresh_time="${7}" + local retry_time="${8}" + local expiry_time="${9}" + local max_cache_time="${10}" + # Debug level for log function + local debug_level="${11}" + + + local reverse_addr + local reverse_octet + local conf_path + local zone_file + local zone_rev_file + local serial + + # IP address octets + local o1 + local o2 + local o3 + local o4 + + # Extract IP address octets + o1="$( echo "${address}" | awk -F '.' '{print $1}' )" + o2="$( echo "${address}" | awk -F '.' '{print $2}' )" + o3="$( echo "${address}" | awk -F '.' '{print $3}' )" + o4="$( echo "${address}" | awk -F '.' '{print $4}' )" + + reverse_addr="${o3}.${o2}.${o1}" + reverse_octet="${o4}" + conf_path="$( dirname "${conf_file}" )" + zone_file="${conf_file}.zone" + zone_rev_file="${conf_file}.zone.reverse" + serial="$( date +'%s' )" + + # Create config directory if it does not yet exist + if [ ! -d "${conf_path}" ]; then + mkdir -p "${conf_path}" + fi + + # Config + { + echo "zone \"${domain}\" IN {" + echo " type master;" + echo " allow-transfer { any; };" + echo " allow-update { any; };" + echo " file \"${zone_file}\";" + echo "};" + if [ -n "${reverse}" ]; then + echo "zone \"${reverse_addr}.in-addr.arpa\" {" + echo " type master;" + echo " allow-transfer { any; };" + echo " allow-update { any; };" + echo " file \"${zone_rev_file}\";" + echo "};" + fi + } > "${conf_file}" + + # Forward Zone + { + echo "\$TTL ${ttl_time}" + echo "@ IN SOA ${domain}. root.${domain}. (" + echo " ${serial} ; Serial number of zone file" + echo " ${refresh_time} ; Refresh time" + echo " ${retry_time} ; Retry time in case of problem" + echo " ${expiry_time} ; Expiry time" + echo " ${max_cache_time} ) ; Maximum caching time in case of failed lookups" + echo ";" + echo " IN NS ns1.${domain}." + echo " IN NS ns2.${domain}." + echo " IN A ${address}" + echo ";" + echo "ns1 IN A ${address}" + echo "ns2 IN A ${address}" + if [ "${wildcard}" -eq "1" ]; then + echo "* IN A ${address}" + fi + } > "${zone_file}" + + # Reverse Zone + if [ -n "${reverse}" ]; then + { + echo "\$TTL ${ttl_time}" + echo "${reverse_addr}.in-addr.arpa. IN SOA ${domain}. root.${domain}. (" + echo " ${serial} ; Serial number of zone file (yyyymmdd##)" + echo " ${refresh_time} ; Refresh time" + echo " ${retry_time} ; Retry time in case of problem" + echo " ${expiry_time} ; Expiry time" + echo " ${max_cache_time} ) ; Maximum caching time in case of failed lookups" + echo ";" + echo "${reverse_addr}.in-addr.arpa. IN NS ns1.${domain}." + echo "${reverse_addr}.in-addr.arpa. IN NS ns2.${domain}." + echo "${reverse_octet}.${reverse_addr}.in-addr.arpa. IN PTR ${reverse}." + } > "${zone_rev_file}" + fi + + # named.conf + if ! output="$( named-checkconf "${conf_file}" 2>&1 )"; then + log "err" "Configuration failed." "${debug_level}" + echo "${output}" + exit + elif [ "${debug_level}" -gt "1" ]; then + echo "${output}" + fi + # Zone file + if ! output="$( named-checkzone "${domain}" "${zone_file}" 2>&1 )"; then + log "err" "Configuration failed." "${debug_level}" + echo "${output}" + exit + elif [ "${debug_level}" -gt "1" ]; then + echo "${output}" + fi + # Reverse DNS + if [ -n "${reverse}" ]; then + if ! output="$( named-checkzone "${reverse_addr}.in-addr.arpa" "${zone_rev_file}" 2>&1 )"; then + log "err" "Configuration failed." "${debug_level}" + echo "${output}" + exit + elif [ "${debug_level}" -gt "1" ]; then + echo "${output}" + fi + fi +} + + + +################################################################################# +## BOOTSTRAP +################################################################################# + +### +### Set Debug level +### +if printenv DEBUG_ENTRYPOINT >/dev/null 2>&1; then + DEBUG_ENTRYPOINT="$( printenv DEBUG_ENTRYPOINT )" + if ! is_int "${DEBUG_ENTRYPOINT}"; then + log "warn" "Wrong value for DEBUG_ENTRYPOINT: '${DEBUG_ENTRYPOINT}'. Setting to ${DEFAULT_DEBUG_ENTRYPOINT}" "2" + DEBUG_ENTRYPOINT="${DEFAULT_DEBUG_ENTRYPOINT}" + else + if [ "${DEBUG_ENTRYPOINT}" -lt "0" ]; then + log "warn" "Wrong value for DEBUG_ENTRYPOINT: '${DEBUG_ENTRYPOINT}'. Setting to ${DEFAULT_DEBUG_ENTRYPOINT}" "2" + DEBUG_ENTRYPOINT="${DEFAULT_DEBUG_ENTRYPOINT}" + elif [ "${DEBUG_ENTRYPOINT}" -gt "2" ]; then + log "warn" "Wrong value for DEBUG_ENTRYPOINT: '${DEBUG_ENTRYPOINT}'. Setting to ${DEFAULT_DEBUG_ENTRYPOINT}" "2" + DEBUG_ENTRYPOINT="${DEFAULT_DEBUG_ENTRYPOINT}" + else + DEBUG_ENTRYPOINT="${DEBUG_ENTRYPOINT}" + fi + fi +else + DEBUG_ENTRYPOINT="${DEFAULT_DEBUG_ENTRYPOINT}" +fi +log "info" "Debug level: ${DEBUG_ENTRYPOINT}" "${DEBUG_ENTRYPOINT}" + + + +################################################################################# +# ENTRYPOINT +################################################################################# + +### +### Re-create BIND default config +### +{ + echo "include \"${NAMED_LOG_CONF}\";" + echo "include \"${NAMED_OPT_CONF}\";" + echo "include \"/etc/bind/named.conf.local\";" + echo "include \"/etc/bind/named.conf.default-zones\";" +} > "${NAMED_CONF}" + + + +### +### Enable Logging to Docker logs +### https://stackoverflow.com/questions/11153958/how-to-enable-named-bind-dns-full-logging#12114139 +### +echo > "${NAMED_LOG_CONF}" +if printenv DOCKER_LOGS >/dev/null 2>&1; then + DOCKER_LOGS="$( printenv DOCKER_LOGS )" + if [ "${DOCKER_LOGS}" = "1" ]; then + { + echo "logging {" + echo " category default { default_stderr; };" + echo " category queries { default_stderr; };" + echo "};" + } > "${NAMED_LOG_CONF}" + log "info" "BIND logging: to stderr via Docker logs" "${DEBUG_ENTRYPOINT}" + elif [ "${DOCKER_LOGS}" = "0" ]; then + log "info" "BIND logging: disabled explicitly" "${DEBUG_ENTRYPOINT}" + else + log "warn" "Wrong value for \$DOCKER_LOGS: '${DOCKER_LOGS}'. Only supports: '1' or '0'" "${DEBUG_ENTRYPOINT}" + DOCKER_LOGS="${DEFAULT_DOCKER_LOGS}" + fi +fi + + +### +### DNS Time settings +### +if printenv TTL_TIME >/dev/null 2>&1; then + TTL_TIME="$( printenv TTL_TIME )" + if is_int "${TTL_TIME}" && [ "${TTL_TIME}" -gt "0" ]; then + log "info" "Changing DNS TTL time to: ${TTL_TIME} sec" "${DEBUG_ENTRYPOINT}" + else + log "warn" "Wrong value for \$TTL_TIME '${TTL_TIME}', defaultint to: ${DEFAULT_TTL_TIME}" "${DEBUG_ENTRYPOINT}" + TTL_TIME="${DEFAULT_TTL_TIME}" + fi +else + log "info" "Using default DNS TTL time: ${DEFAULT_TTL_TIME} sec" "${DEBUG_ENTRYPOINT}" + TTL_TIME="${DEFAULT_TTL_TIME}" +fi + +if printenv REFRESH_TIME >/dev/null 2>&1; then + REFRESH_TIME="$( printenv REFRESH_TIME )" + if is_int "${REFRESH_TIME}" && [ "${REFRESH_TIME}" -gt "0" ]; then + log "info" "Changing DNS Refresh time to: ${REFRESH_TIME} sec" "${DEBUG_ENTRYPOINT}" + else + log "warn" "Wrong value for \$REFRESH_TIME '${REFRESH_TIME}', defaultint to: ${DEFAULT_REFRESH_TIME}" "${DEBUG_ENTRYPOINT}" + REFRESH_TIME="${DEFAULT_REFRESH_TIME}" + fi +else + log "info" "Using default DNS Refresh time: ${DEFAULT_REFRESH_TIME} sec" "${DEBUG_ENTRYPOINT}" + REFRESH_TIME="${DEFAULT_REFRESH_TIME}" +fi + +if printenv RETRY_TIME >/dev/null 2>&1; then + RETRY_TIME="$( printenv RETRY_TIME )" + if is_int "${RETRY_TIME}" && [ "${RETRY_TIME}" -gt "0" ]; then + log "info" "Changing DNS Retry time to: ${RETRY_TIME} sec" "${DEBUG_ENTRYPOINT}" + else + log "warn" "Wrong value for \$RETRY_TIME '${RETRY_TIME}', defaultint to: ${DEFAULT_RETRY_TIME}" "${DEBUG_ENTRYPOINT}" + RETRY_TIME="${DEFAULT_RETRY_TIME}" + fi +else + log "info" "Using default DNS Retry time: ${DEFAULT_RETRY_TIME} sec" "${DEBUG_ENTRYPOINT}" + RETRY_TIME="${DEFAULT_RETRY_TIME}" +fi + +if printenv EXPIRY_TIME >/dev/null 2>&1; then + EXPIRY_TIME="$( printenv EXPIRY_TIME )" + if is_int "${EXPIRY_TIME}" && [ "${EXPIRY_TIME}" -gt "0" ]; then + log "info" "Changing DNS Expiry time to: ${EXPIRY_TIME} sec" "${DEBUG_ENTRYPOINT}" + else + log "warn" "Wrong value for \$EXPIRY_TIME '${EXPIRY_TIME}', defaultint to: ${DEFAULT_EXPIRY_TIME}" "${DEBUG_ENTRYPOINT}" + EXPIRY_TIME="${DEFAULT_EXPIRY_TIME}" + fi +else + log "info" "Using default DNS Expiry time: ${DEFAULT_EXPIRY_TIME} sec" "${DEBUG_ENTRYPOINT}" + EXPIRY_TIME="${DEFAULT_EXPIRY_TIME}" +fi + +if printenv MAX_CACHE_TIME >/dev/null 2>&1; then + MAX_CACHE_TIME="$( printenv MAX_CACHE_TIME )" + if is_int "${MAX_CACHE_TIME}" && [ "${MAX_CACHE_TIME}" -gt "0" ]; then + log "info" "Changing DNS Max Cache time to: ${MAX_CACHE_TIME} sec" "${DEBUG_ENTRYPOINT}" + else + log "warn" "Wrong value for \$MAX_CACHE_TIME '${MAX_CACHE_TIME}', defaultint to: ${DEFAULT_MAX_CACHE_TIME}" "${DEBUG_ENTRYPOINT}" + MAX_CACHE_TIME="${DEFAULT_MAX_CACHE_TIME}" + fi +else + log "info" "Using default DNS Max Cache time: ${DEFAULT_MAX_CACHE_TIME} sec" "${DEBUG_ENTRYPOINT}" + MAX_CACHE_TIME="${DEFAULT_MAX_CACHE_TIME}" +fi + + + +### +### Add wildcard DNS +### +if printenv WILDCARD_DNS >/dev/null 2>&1; then + + # Convert 'com=1.2.3.4[=com],de=2.3.4.5' into newline separated string: + # com=1.2.3.4[=com] + # de=2.3.4.5 + echo "${WILDCARD_DNS}" | sed 's/,/\n/g' | while read line ; do + my_dom="$( echo "${line}" | awk -F '=' '{print $1}' | xargs )" # domain + my_add="$( echo "${line}" | awk -F '=' '{print $2}' | xargs )" # IP address + my_rev="$( echo "${line}" | awk -F '=' '{print $3}' | xargs )" # Reverse DNS record + my_cfg="${NAMED_DIR}/devilbox-wildcard_dns.${my_dom}.conf" + + # If a CNAME was provided, try to resolve it to an IP address, otherwise skip it + if is_cname "${my_add}"; then + tmp="${my_add}" + my_add="$( dig @8.8.8.8 +short "${my_add}" A )" + if ! is_ip4 "${my_add}"; then + log "warn" "CNAME '${tmp}' could not be resolved. Skipping to add wildcard" "${DEBUG_ENTRYPOINT}" + continue; + fi + log "info" "CNAME '${tmp}' resolved to: ${my_add}" "${DEBUG_ENTRYPOINT}" + fi + + # If specified address is not a valid IPv4 address, skip it + if ! is_ip4 "${my_add}"; then + log "warn" "Invalid IP address '${my_add}': for *.${my_dom} -> ${my_add}. Skipping to add wildcard" "${DEBUG_ENTRYPOINT}" + continue; + fi + + if [ -n "${my_rev}" ]; then + log "info" "Adding wildcard DNS: *.${my_dom} -> ${my_add} (PTR: ${my_rev})" "${DEBUG_ENTRYPOINT}" + else + log "info" "Adding wildcard DNS: *.${my_dom} -> ${my_add}" "${DEBUG_ENTRYPOINT}" + fi + + echo "include \"${my_cfg}\";" >> "${NAMED_CONF}" + add_wildcard_zone "${my_dom}" "${my_add}" "${my_cfg}" "1" "${my_rev}" \ + "${TTL_TIME}" "${REFRESH_TIME}" "${RETRY_TIME}" "${EXPIRY_TIME}" "${MAX_CACHE_TIME}" \ + "${DEBUG_ENTRYPOINT}" + done +fi + + + +### +### Add extra hosts +### +if printenv EXTRA_HOSTS >/dev/null 2>&1; then + + # Convert 'com=1.2.3.4[=com],de=2.3.4.5' into newline separated string: + # com=1.2.3.4 + # de=2.3.4.5 + echo "${EXTRA_HOSTS}" | sed 's/,/\n/g' | while read line ; do + my_dom="$( echo "${line}" | awk -F '=' '{print $1}' | xargs )" # domain + my_add="$( echo "${line}" | awk -F '=' '{print $2}' | xargs )" # IP address + my_rev="$( echo "${line}" | awk -F '=' '{print $3}' | xargs )" # Reverse DNS record + my_cfg="${NAMED_DIR}/devilbox-extra_hosts.${my_dom}.conf" + + # If a CNAME was provided, try to resolve it to an IP address, otherwhise skip it + if is_cname "${my_add}"; then + tmp="${my_add}" + my_add="$( dig @8.8.8.8 +short "${my_add}" A )" + if ! is_ip4 "${my_add}"; then + log "warn" "CNAME '${tmp}' could not be resolved. Skipping to add extra host" "${DEBUG_ENTRYPOINT}" + continue; + fi + log "info" "CNAME '${tmp}' resolved to: ${my_add}" "${DEBUG_ENTRYPOINT}" + fi + + # If specified address is not a valid IPv4 address, skip it + if ! is_ip4 "${my_add}"; then + log "warn" "Invalid IP address '${my_add}': for *.${my_dom} -> ${my_add}. Skipping to add extra host" "${DEBUG_ENTRYPOINT}" + continue; + fi + + if [ -n "${my_rev}" ]; then + log "info" "Adding extra host: ${my_dom} -> ${my_add} (PTR: ${my_rev})" "${DEBUG_ENTRYPOINT}" + else + log "info" "Adding extra host: ${my_dom} -> ${my_add}" "${DEBUG_ENTRYPOINT}" + fi + + echo "include \"${my_cfg}\";" >> "${NAMED_CONF}" + add_wildcard_zone "${my_dom}" "${my_add}" "${my_cfg}" "0" "${my_rev}" \ + "${TTL_TIME}" "${REFRESH_TIME}" "${RETRY_TIME}" "${EXPIRY_TIME}" "${MAX_CACHE_TIME}" \ + "${DEBUG_ENTRYPOINT}" + done +fi + + + +### +### DNSSEC validation +### +if printenv DNSSEC_VALIDATE >/dev/null 2>&1; then + DNSSEC_VALIDATE="$( printenv DNSSEC_VALIDATE )" + if [ "${DNSSEC_VALIDATE}" = "auto" ]; then + DNSSEC_VALIDATE="${DNSSEC_VALIDATE}" + elif [ "${DNSSEC_VALIDATE}" = "yes" ]; then + DNSSEC_VALIDATE="${DNSSEC_VALIDATE}" + elif [ "${DNSSEC_VALIDATE}" = "no" ]; then + DNSSEC_VALIDATE="${DNSSEC_VALIDATE}" + else + log "warning" "Wrong value for DNSSEC_VALIDATE: '${DNSSEC_VALIDATE}'. Setting it to '${DEFAULT_DNSSEC_VALIDATE}'" "${DEBUG_ENTRYPOINT}" + DNSSEC_VALIDATE="${DEFAULT_DNSSEC_VALIDATE}" + fi +else + DNSSEC_VALIDATE="${DEFAULT_DNSSEC_VALIDATE}" +fi +log "info" "DNSSEC Validation: ${DNSSEC_VALIDATE}" "${DEBUG_ENTRYPOINT}" + + + +### +### Forwarder +### +if ! printenv DNS_FORWARDER >/dev/null 2>&1; then + log "info" "\$DNS_FORWARDER not set." "${DEBUG_ENTRYPOINT}" + log "info" "No custom DNS server will be used as forwarder" "${DEBUG_ENTRYPOINT}" + + add_options "${NAMED_OPT_CONF}" "${DNSSEC_VALIDATE}" "" +else + + # To be pupulated + _forwarders_block="" + + # Transform into newline separated forwards and loop over: + # x.x.x.x\n + # y.y.y.y\n + while read ip ; do + ip="$( echo "${ip}" | xargs )" + + if ! is_ip4 "${ip}"; then + log "err" "DNS_FORWARDER error: not a valid IP address: ${ip}" "${DEBUG_ENTRYPOINT}" + exit 1 + fi + + if [ -z "${_forwarders_block}" ]; then + _forwarders_block=" ${ip};" + else + _forwarders_block="${_forwarders_block}\n ${ip};" + fi + done <<< "$(echo "$( printenv DNS_FORWARDER )" | sed 's/,/\n/g' )" + + + if [ -z "${_forwarders_block}" ]; then + log "err" "DNS_FORWARDER error: variable specified, but no IP addresses found." "${DEBUG_ENTRYPOINT}" + exit 1 + fi + + log "info" "Adding custom DNS forwarder: ${DNS_FORWARDER}" "${DEBUG_ENTRYPOINT}" + add_options "${NAMED_OPT_CONF}" "${DNSSEC_VALIDATE}" "${_forwarders_block}" +fi + + + +### +### Start +### +log "info" "Starting $( named -V | grep -oiE '^BIND[[:space:]]+[0-9.]+' )" "${DEBUG_ENTRYPOINT}" +named-checkconf "${NAMED_CONF}" +exec /usr/sbin/named -4 -c /etc/bind/named.conf -u bind -f diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh deleted file mode 100755 index d5f7129..0000000 --- a/scripts/docker-entrypoint.sh +++ /dev/null @@ -1,249 +0,0 @@ -#!/bin/sh -eu - -### -### Variables -### -DEBUG_COMMANDS=0 - - - -### -### Functions -### -run() { - _cmd="${1}" - _debug="0" - - _red="\033[0;31m" - _green="\033[0;32m" - _reset="\033[0m" - _user="$(whoami)" - - - # If 2nd argument is set and enabled, allow debug command - if [ "${#}" = "2" ]; then - if [ "${2}" = "1" ]; then - _debug="1" - fi - fi - - - if [ "${DEBUG_COMMANDS}" = "1" ] || [ "${_debug}" = "1" ]; then - printf "${_red}%s \$ ${_green}${_cmd}${_reset}\n" "${_user}" - fi - sh -c "LANG=C LC_ALL=C ${_cmd}" -} - -log() { - _lvl="${1}" - _msg="${2}" - - _clr_ok="\033[0;32m" - _clr_info="\033[0;34m" - _clr_warn="\033[0;33m" - _clr_err="\033[0;31m" - _clr_rst="\033[0m" - - if [ "${_lvl}" = "ok" ]; then - printf "${_clr_ok}[OK] %s${_clr_rst}\n" "${_msg}" - elif [ "${_lvl}" = "info" ]; then - printf "${_clr_info}[INFO] %s${_clr_rst}\n" "${_msg}" - elif [ "${_lvl}" = "warn" ]; then - printf "${_clr_warn}[WARN] %s${_clr_rst}\n" "${_msg}" 1>&2 # stdout -> stderr - elif [ "${_lvl}" = "err" ]; then - printf "${_clr_err}[ERR] %s${_clr_rst}\n" "${_msg}" 1>&2 # stdout -> stderr - else - printf "${_clr_err}[???] %s${_clr_rst}\n" "${_msg}" 1>&2 # stdout -> stderr - fi -} - -# Test if argument is an integer. -# -# @param mixed -# @return integer 0: is number | 1: not a number -isint(){ - printf "%d" "${1}" >/dev/null 2>&1 && return 0 || return 1; -} -isip() { - # IP is not in correct format - if ! echo "${1}" | grep -Eq '^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$'; then - return 1 - fi - - # Get each octet - o1="$( echo "${1}" | awk -F'.' '{print $1}' )" - o2="$( echo "${1}" | awk -F'.' '{print $2}' )" - o3="$( echo "${1}" | awk -F'.' '{print $3}' )" - o4="$( echo "${1}" | awk -F'.' '{print $4}' )" - - # Cannot start with 0 and all must be below 256 - if [ "${o1}" -lt "1" ] || \ - [ "${o1}" -gt "255" ] || \ - [ "${o2}" -gt "255" ] || \ - [ "${o3}" -gt "255" ] || \ - [ "${o4}" -gt "255" ]; then - return 1 - fi - - # All tests passed - return 0 -} - - - - -################################################################################ -# BOOTSTRAP -################################################################################ - -if set | grep '^DEBUG_COMPOSE_ENTRYPOINT=' >/dev/null 2>&1; then - if [ "${DEBUG_COMPOSE_ENTRYPOINT}" = "1" ]; then - DEBUG_COMMANDS=1 - fi -fi - - -################################################################################ -# MAIN ENTRY POINT -################################################################################ - - - -### -### Add wildcard DNS record? -### -if set | grep '^WILDCARD_DOMAIN=' >/dev/null 2>&1 && set | grep '^WILDCARD_ADDRESS=' >/dev/null 2>&1; then - if ! isip "${WILDCARD_ADDRESS}"; then - log "err" "Value of \$WILDCARD_ADDRESS is not a valid IP Address: ${WILDCARD_ADDRESS}" - exit 1 - fi - - log "info" "Adding wildcard DNS record: '*.${WILDCARD_DOMAIN}' -> '${WILDCARD_ADDRESS}'" - - conf_file="/etc/bind/custom-named.conf.${WILDCARD_DOMAIN}" - zone_file="/etc/bind/custom-db.${WILDCARD_DOMAIN}" - - # Re-create default config - ( - echo "include \"/etc/bind/named.conf.options\";" - echo "include \"/etc/bind/named.conf.local\";" - echo "include \"/etc/bind/named.conf.default-zones\";" - ) > /etc/bind/named.conf - - # Add custom config - run "echo 'include \"${conf_file}\";' >> /etc/bind/named.conf" - - # Config - ( - echo "zone \"${WILDCARD_DOMAIN}\" IN {" - echo " type master;" - echo " allow-transfer { any; };" - echo " file \"${zone_file}\";" - echo "};" - ) > "${conf_file}" - - # Zone - ( - echo "@ IN SOA . root.acmecorp.com. (" - echo " 20130903 ; serial number of zone file (yyyymmdd##)" - echo " 604800 ; refresh time" - echo " 86400 ; retry time in case of problem" - echo " 2419200 ; expiry time" - echo " 604800) ; maximum caching time in case of failed lookups" - echo " IN NS ." - echo " IN A ${WILDCARD_ADDRESS}" - echo "* IN A ${WILDCARD_ADDRESS}" - ) > "${zone_file}" - -else - - if ! set | grep '^WILDCARD_DOMAIN=' >/dev/null 2>&1; then - log "info" "\$WILDCARD_DOMAIN not set, not adding custom DNS record." - fi - if ! set | grep '^WILDCARD_ADDRESS=' >/dev/null 2>&1; then - log "info" "\$WILDCARD_ADDRESS not set, not adding custom DNS record." - fi - - # Re-create default config - ( - echo "include \"/etc/bind/named.conf.options\";" - echo "include \"/etc/bind/named.conf.local\";" - echo "include \"/etc/bind/named.conf.default-zones\";" - ) > /etc/bind/named.conf - -fi - - -### -### Add custom forwarder IP's -### -if ! set | grep '^DNS_FORWARDER=' >/dev/null 2>&1; then - log "warn" "\$DNS_FORWARDER not set." - log "warn" "No custom DNS server will be used as forwarder" - - # Restore defaults - ( - echo "options {" - echo " directory \"/var/cache/bind\";" - echo " dnssec-validation auto;" - echo " auth-nxdomain no; # conform to RFC1035" - echo " listen-on-v6 { any; };" - echo "};" - ) > /etc/bind/named.conf.options -else - - # To be pupulated - _forwarders_block="" - - # Transform into newline separated forwards: - # x.x.x.x\n - # y.y.y.y\n - _forwards="$( echo "${DNS_FORWARDER}" | sed 's/[[:space:]]*//g' | sed 's/,/\n/g' )" - - # loop over them - IFS=' - ' - for ip in ${_forwards}; do - if ! isip "${ip}"; then - log "err" "DNS_FORWARDER error: not a valid IP address: ${ip}" - exit 1 - fi - - if [ "${_forwarders_block}" = "" ]; then - _forwarders_block="\t\t${ip};" - else - _forwarders_block="${_forwarders_block}\n\t\t${ip};" - fi - done - - if [ "${_forwarders_block}" = "" ]; then - log "err" "DNS_FORWARDER error: variable specified, but no IP addresses found." - exit 1 - fi - - log "info" "Adding custom DNS forwarder: ${DNS_FORWARDER}" - - # forwarders { - # x.x.x.x; - # y.y.y.y; - # }; - ( - echo "options {" - echo " directory \"/var/cache/bind\";" - echo " dnssec-validation auto;" - echo " auth-nxdomain no; # conform to RFC1035" - echo " listen-on-v6 { any; };" - echo " forwarders {" - echo "${_forwarders_block}" - echo " };" - echo "};" - ) > /etc/bind/named.conf.options -fi - - - -### -### Start -### -log "info" "Starting $( named -V | grep -oiE '^BIND[[:space:]]+[0-9.]+' )" -exec /usr/sbin/named -4 -c /etc/bind/named.conf -u bind -f