├── creds.json ├── dnsconfig.js ├── .github └── workflows │ ├── deploy.yml │ └── preview.yml ├── LICENSE ├── zones └── example.com.js └── README.md /creds.json: -------------------------------------------------------------------------------- 1 | { 2 | "cloudflare": { 3 | "TYPE": "CLOUDFLAREAPI", 4 | "apitoken": "$CLOUDFLARE_APITOKEN" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /dnsconfig.js: -------------------------------------------------------------------------------- 1 | // Author: Luka Kladaric @allixsenos 2 | 3 | // include, recursively, any js files in the zones directory 4 | require_glob("./zones/"); 5 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # Author: Luka Kladaric @allixsenos 2 | 3 | name: Deploy DNS changes 4 | 5 | on: 6 | push: 7 | # Run for pushes to master only 8 | branches: [master] 9 | 10 | jobs: 11 | push: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: DNSControl push 17 | id: dnscontrol_preview 18 | uses: is-cool-me/dnscontrol-action@v4.8.2 19 | with: 20 | args: push 21 | env: 22 | CLOUDFLARE_APITOKEN: ${{ secrets.CLOUDFLARE_APITOKEN }} 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Luka Kladaric, Chaos Guru 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/preview.yml: -------------------------------------------------------------------------------- 1 | # Author: Luka Kladaric @allixsenos 2 | 3 | name: Preview 4 | 5 | on: pull_request 6 | 7 | permissions: 8 | contents: read 9 | pull-requests: write # Needed to comment on PRs 10 | 11 | jobs: 12 | preview: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | 17 | - name: DNSControl check 18 | id: dnscontrol_check 19 | uses: is-cool-me/dnscontrol-action@v4.8.2 20 | with: 21 | args: check 22 | env: 23 | NO_COLOR: yes # disable colors 24 | 25 | - name: DNSControl preview 26 | id: dnscontrol_preview 27 | uses: is-cool-me/dnscontrol-action@v4.8.2 28 | with: 29 | args: preview 30 | env: 31 | NO_COLOR: yes # disable colors 32 | CLOUDFLARE_APITOKEN: ${{ secrets.CLOUDFLARE_APITOKEN }} 33 | 34 | - name: Preview pull request comment 35 | uses: thollander/actions-comment-pull-request@v2 36 | if: success() || failure() 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | with: 40 | message: | 41 | ## DNSControl check 42 | 43 | ``` 44 | ${{ steps.dnscontrol_check.outputs.output }} 45 | ``` 46 | 47 | ## DNSControl preview 48 | 49 | ``` 50 | ${{ steps.dnscontrol_preview.outputs.output }} 51 | ``` 52 | comment_tag: preview 53 | mode: recreate 54 | -------------------------------------------------------------------------------- /zones/example.com.js: -------------------------------------------------------------------------------- 1 | // Author: Luka Kladaric @allixsenos 2 | 3 | // set manage_redirects to true to enable redirect management via Page Rules 4 | // set to false or remove to disable redirect management via Page Rules 5 | var DNS_CFLARE = NewDnsProvider("cloudflare", {"manage_redirects": true}); 6 | 7 | D("example.com", NewRegistrar("none"), DnsProvider(DNS_CFLARE), 8 | DefaultTTL(1), // Cloudflare special "auto TTL" https://docs.dnscontrol.org/service-providers/providers/cloudflareapi#cloudflare-special-ttls 9 | 10 | // EXAMPLE: SPF record to allow Google to send email on behalf of your domain 11 | TXT("@", "v=spf1 include:_spf.google.com -all"), 12 | 13 | // EXAMPLE: website is hosted on GitHub Pages 14 | ALIAS("@", "myusername.github.io.", CF_PROXY_ON), // domain apex has to be ALIAS, not CNAME, on Cloudflare 15 | CNAME("www", "myusername.github.io.", CF_PROXY_ON), 16 | 17 | // EXAMPLE: redirect wip site to main site 18 | CNAME("wip", "myusername.github.io.", CF_PROXY_ON), // this can be pointed at anything, as long as CF_PROXY_ON is set, otherwise the redirect won't work 19 | CF_REDIRECT("wip.example.com/*", "https://example.com/$1"), // redirect all paths under wip.example.com to example.com 20 | 21 | // EXAMPLE: verification records 22 | TXT("@", "google-site-verification=asdfblahblah"), 23 | TXT("@", "google-gws-recovery-domain-verification=asdfblahblah"), 24 | TXT("_github-pages-challenge-allixsenos", "asdfblahblah"), 25 | 26 | // G Suite setup 27 | MX('@', 1, 'smtp.google.com.'), 28 | TXT("google._domainkey", "v=DKIM1; k=rsa; p=asdfblahblah"), 29 | 30 | END // alias to fix trailing comma issue because javascript is a silly language choice for a configuration file 31 | ) 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DNS management with dnscontrol 2 | 3 | This repository contains the DNS zones for domains managed with [dnscontrol](https://github.com/StackExchange/dnscontrol) by StackExchange. 4 | 5 | It uses Github Actions to execute everything and requires no installation on your local machine. You can even manage your DNS directly on github using the web based editor. 6 | 7 | Opening a PR will run `dnscontrol check` to perform a syntax check, followed by a `dnscontrol preview` to compare the file contents with the real world and produce a report of changes that will be executed. The report is then posted as a comment on the PR for inspection. 8 | 9 | Merging the PR to master will execute `dnscontrol push` which will sync the managed zones to the state found in the files. 10 | 11 | The actual DNS services used for each zone are queried for the state on each command. 12 | 13 | Any changed to the DNS that are not tracked in the files will be overwritten on the next deploy. 14 | 15 | ## Importing an existing zone 16 | 17 | The quickest way to get started is to import an existing DNS zone like so: 18 | 19 | ``` 20 | export CLOUDFLARE_APITOKEN=asdfblahblah 21 | dnscontrol get-zone --format=js cloudflare - mydomain.com > zones/mydomain.com.js 22 | ``` 23 | 24 | To import ALL zones from your account to their individual zone files, run: 25 | 26 | ``` 27 | dnscontrol get-zone --format=nameonly cloudflare - all | xargs -n1 -I@ dnscontrol get-zone --format=js --out zones/@.js cloudflare - @ 28 | ``` 29 | 30 | To import just newly added zones that are not yet managed via this repository, run: 31 | 32 | ``` 33 | dnscontrol get-zone --format=nameonly cloudflare - all | sort > all_zones.list 34 | ls zones/*.js | sed 's|zones/||' | sed 's|.js$||' | sort > managed_zones.list 35 | comm -23 all_zones_sorted.list managed_zones.list | xargs -n1 -I@ dnscontrol get-zone --format=js --out zones/@.js cloudflare - @ 36 | rm *.list 37 | ``` 38 | 39 | ## Technical implementation details 40 | 41 | Use the `zones/` folder to keep your DNS zones. 42 | 43 | By convention, each file is named `domain.name.js` and only contains a single zone, but the filename has no special meaning and nothing bad happens if you name it something else. Similarly, nothing bad happens if you put multiple zones in one file. 44 | 45 | The credentials are defined as Github Actions Secrets. 46 | 47 | ## Cloudflare setup 48 | 49 | 1. Create an API token 50 | 1. Go to your Cloudflare dash -> My Profile -> API Tokens https://dash.cloudflare.com/profile/api-tokens 51 | 2. Create a token with a useful name 52 | 3. Give it the following permissions 53 | * Zone -> Zone -> Read 54 | * Zone -> DNS -> Edit 55 | * Zone -> Page Rules -> Edit 56 | 4. Scope it to specific zones, or the entire account, or however you see fit 57 | 2. Add the newly created token as a Github Actions Repository Secret, under name `CLOUDFLARE_APITOKEN` 58 | 3. Done 59 | 60 | ## Why not Terraform? 61 | 62 | This got raised enough times that I wrote up an explanation, but would love to hear what people think. Head over to [the Why not Terraform? discussion](https://github.com/awesome-foundation/dns/discussions/3) 63 | 64 | ## Alternatives 65 | 66 | This is going to be a growing list so let's keep it centralized. Please bring all your alternatives to the [Alternatives discussion](https://github.com/awesome-foundation/dns/discussions/4). 67 | 68 | ## Using a new provider 69 | 70 | DNSControl supports many providers, and there will be zero effort to support or document any of them except for Cloudflare which by pure coincidence is the one I use. 71 | 72 | Unfortunately, any new credentials will need to be added in 3 places: 73 | 1. Github Actions Repository Secrets 74 | 2. `creds.json` where you just reference the environment variable name in the config block for your provider 75 | 3. `.github/workflows/deploy.yml` and `preview.yml` where they are passed into the `push` and `preview` commands 76 | 77 | There does not appear to be a way to configure dnscontrol entirely through envvars, and there does not seem to be a way to expose all (including future) secrets to a job without some significant security risks. 78 | 79 | ## Testing locally on Mac 80 | 81 | ``` 82 | brew install dnscontrol 83 | export CLOUDFLARE_APITOKEN=xyz123 84 | dnscontrol check 85 | dnscontrol preview 86 | dnscontrol push 87 | ``` 88 | 89 | ## Who built this? 90 | 91 | Luka Kladaric aka [Chaos Guru](https://chaos.guru/?utm_source=gh-af-dns) built this because dnscontrol is awesome but getting the PR previews and stuff to work takes a ton of trial and error and I always wished there was a turn key solution. 92 | --------------------------------------------------------------------------------