diff --git a/.github/workflows/dns.yml b/.github/workflows/dns.yml index 4576296..6fb3696 100644 --- a/.github/workflows/dns.yml +++ b/.github/workflows/dns.yml @@ -3,13 +3,13 @@ name: dns on: pull_request: paths: - - "dns/**" + - "dns/rubyci.org/**" - ".github/workflows/dns.yml" push: branches: - master paths: - - "dns/**" + - "dns/rubyci.org/**" - ".github/workflows/dns.yml" permissions: @@ -19,13 +19,14 @@ env: DNSCONTROL_VERSION: 4.44.1 jobs: + # Only rubyci.org is applied from CI. ruby-lang.org is pushed by hand; see dns/README.md. dns: # Secrets are unavailable to fork PRs, so run only for same-repo events if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest defaults: run: - working-directory: dns + working-directory: dns/rubyci.org steps: - uses: actions/checkout@v5 - name: Install dnscontrol @@ -36,9 +37,9 @@ jobs: if: github.event_name == 'pull_request' run: dnscontrol preview --creds creds.json env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_API_TOKEN_RUBYCI: ${{ secrets.CLOUDFLARE_API_TOKEN_RUBYCI }} - name: Push if: github.event_name == 'push' run: dnscontrol push --creds creds.json env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_API_TOKEN_RUBYCI: ${{ secrets.CLOUDFLARE_API_TOKEN_RUBYCI }} diff --git a/README.md b/README.md index 6b182d5..2813c1e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ### Prepare environment for hocho apply -After launching a VM and assigning the Elastic IP (DNS records are managed under `dns/`), bootstrap the host with the cloud image's default user: +After launching a VM and assigning the Elastic IP (DNS records are managed under `dns/rubyci.org/`, see `dns/README.md`), bootstrap the host with the cloud image's default user: ```bash bin/bootstrap -i ~/.ssh/aws-keypair.pem fedora@fedora44-arm.rubyci.org diff --git a/cdn/README.md b/cdn/README.md index 26748e1..9211ae6 100644 --- a/cdn/README.md +++ b/cdn/README.md @@ -6,7 +6,7 @@ The `ftp` service has been dormant since 2018 and is kept with `activate = false The `logs.rubyci.org` service fronts the public `rubyci` S3 bucket (chkbuild logs). Switching rubyci.org log links over to it happens in the ruby/rubyci repository. -Bootstrapping a domain on a new TLS subscription takes three steps, because the ACME challenge has to resolve before Fastly will issue: `terraform apply`, then add the challenge CNAME from `terraform output logs_rubyci_managed_dns_challenges` to `dns/dnsconfig.js` and push it, then add the domain CNAME once the subscription reaches `issued`. The challenge record stays in `dnsconfig.js` afterwards, since renewal reuses it. +Bootstrapping a domain on a new TLS subscription takes three steps, because the ACME challenge has to resolve before Fastly will issue: `terraform apply`, then add the challenge CNAME from `terraform output logs_rubyci_managed_dns_challenges` to `dns/rubyci.org/dnsconfig.js` and push it, then add the domain CNAME once the subscription reaches `issued`. The challenge record stays in `dnsconfig.js` afterwards, since renewal reuses it. Two things about the Fastly API token. It needs the TLS management permission, which is only settable when the automation token is created; without it `fastly_tls_subscription` fails with a bare `403 - Forbidden`. And this account has no default TLS configuration, so `configuration_id` is required, which also decides the CNAME target the domain points at. diff --git a/dns/README.md b/dns/README.md new file mode 100644 index 0000000..ebab34a --- /dev/null +++ b/dns/README.md @@ -0,0 +1,31 @@ +# dns + +DNSControl configuration for the Cloudflare zones. One directory per zone, each holding its own `dnsconfig.js` and `creds.json`. + +- `rubyci.org/` — the CI hosts. Every rubyci host is resolved through DNS rather than `~/.ssh/config`, so a new host needs its record here before `bin/hocho apply`. The apex uses Cloudflare's CNAME flattening, which DNSControl expresses as `ALIAS`. +- `ruby-lang.org/` — the project zone. + +The API tokens are zone-scoped and therefore different per zone, so each `creds.json` reads an environment variable named after its zone. `rubyci.org/creds.json` reads `CLOUDFLARE_API_TOKEN_RUBYCI` and `ruby-lang.org/creds.json` reads `CLOUDFLARE_API_TOKEN_RUBY_LANG_ORG`. + +## rubyci.org + +Applied from CI by `.github/workflows/dns.yml`: a pull request runs `dnscontrol preview`, and merging to `master` runs `dnscontrol push`. The token comes from the repository secret `CLOUDFLARE_API_TOKEN_RUBYCI`. + +To preview locally: + +``` +source ~/.config/cloudflare/rubyci.org/token.sh +cd dns/rubyci.org +dnscontrol preview --creds creds.json +``` + +## ruby-lang.org + +Not applied from CI. The zone is shared with other maintainers, so `push` is run by hand after the change is reviewed: + +``` +source ~/.config/cloudflare/ruby-lang.org/token.sh +cd dns/ruby-lang.org +dnscontrol preview --creds creds.json +dnscontrol push --creds creds.json +``` diff --git a/dns/ruby-lang.org/creds.json b/dns/ruby-lang.org/creds.json new file mode 100644 index 0000000..3342229 --- /dev/null +++ b/dns/ruby-lang.org/creds.json @@ -0,0 +1,6 @@ +{ + "cloudflare": { + "TYPE": "CLOUDFLAREAPI", + "apitoken": "$CLOUDFLARE_API_TOKEN_RUBY_LANG_ORG" + } +} diff --git a/dns/ruby-lang.org/dnsconfig.js b/dns/ruby-lang.org/dnsconfig.js new file mode 100644 index 0000000..3a2a953 --- /dev/null +++ b/dns/ruby-lang.org/dnsconfig.js @@ -0,0 +1,59 @@ +var DSP_CLOUDFLARE = NewDnsProvider("cloudflare"); +var REG_NONE = NewRegistrar("none"); + +D("ruby-lang.org", REG_NONE, + DnsProvider(DSP_CLOUDFLARE), + DefaultTTL(1), + A("doc", "18.178.72.192"), + A("docs-2020", "18.178.72.192"), + A("docs-origin", "18.178.72.192"), + A("gem-codesearch.dev", "18.180.198.97"), + A("git", "52.192.80.219"), + A("mame.dev", "18.180.198.97"), + A("ml", "94.130.110.93"), + // GitHub Pages apex addresses for www.ruby-lang.org. + A("@", "185.199.111.153"), + A("@", "185.199.110.153"), + A("@", "185.199.109.153"), + A("@", "185.199.108.153"), + AAAA("ml", "2a01:4f8:1c0c:6b10::1"), + CNAME("_acme-challenge", "rxvnnzqvc8fpc7ws1w.fastly-validations.com."), + CNAME("archive", "dualstack.m.shared.global.fastly.net."), + CNAME("blade", "infinite-mayflower-abjfcuhrpvgf9g7g8a4rmwq8.herokudns.com."), + CNAME("bugs", "bugs.ruby-lang.org.herokudns.com."), + CNAME("cache", "dualstack.m.shared.global.fastly.net."), + CNAME("ci", "git.ruby-lang.org."), + CNAME("cvs", "git.ruby-lang.org."), + CNAME("docs", "dualstack.m.shared.global.fastly.net."), + CNAME("em5188", "u313651.wl152.sendgrid.net."), + CNAME("ftp", "dualstack.m.shared.global.fastly.net."), + CNAME("lists", "ext-sq.squarespace.com."), + CNAME("redmine", "bugs.ruby-lang.org.herokudns.com."), + CNAME("s1._domainkey", "s1.domainkey.u313651.wl152.sendgrid.net."), + CNAME("s2._domainkey", "s2.domainkey.u313651.wl152.sendgrid.net."), + CNAME("speed", "rubybench.github.io."), + CNAME("svn", "git.ruby-lang.org."), + CNAME("try", "ruby.github.io."), + CNAME("www", "ruby.github.io."), + MX("git", 10, "git.ruby-lang.org."), + MX("g", 10, "alt4.aspmx.l.google.com."), + MX("g", 10, "alt3.aspmx.l.google.com."), + MX("g", 5, "alt2.aspmx.l.google.com."), + MX("g", 5, "alt1.aspmx.l.google.com."), + MX("g", 1, "aspmx.l.google.com."), + MX("ml", 10, "nue.mailmanlists.eu."), + TXT("_dmarc.ml", "v=DMARC1; p=quarantine; rua=mailto:dmarc@nue.mailmanlists.eu; ruf=mailto:dmarc@nue.mailmanlists.eu"), + TXT("_dmarc", "v=DMARC1;p=none"), + TXT("_github-challenge-ruby", "d1d1ebcf87"), + TXT("_github-challenge-ruby.www", "130b9370c3"), + TXT("_github-pages-challenge-rubybench.speed", "bb3aa7c4fca5a93c67c29e37e31a28"), + TXT("_github-pages-challenge-ruby", "7945b3c0777b14c533ce1a058058ea"), + TXT("_github-pages-challenge-ruby.try", "0c8a06ccf14e008aa2120723c94bd3"), + TXT("google._domainkey.g", "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnQOXdwH6epkMSB4stgVaQpx+Hx+pcvNkot5b+14uEjN0aueiGzN+ut78QNqWzE8zdJH+oIaV2CPf4MKP5dzwS/wexm50ZUGGcMnMEMXqihTBnxKEOLz0M9Z3XZ1r059dLCtDizFNvVDKlDrFiS0UTqgJxHQ98T+Kh6gwEnaNvktoz2riEXlvVET8tet0FDCXQN+re4MaJkhGgiezezZpfW0Rpjn9WrS60i2YlUYf7qEhS6mqdF7f0GxO6wVz7sfNDfb70m8S+KLV3C5LoiiKhKGk+pBByV2ybVmYx4WaCVnvrYGVf8yLLoxxG3pLGx+/qeTkbRTyY9dn1mfh/6KDuQIDAQAB"), + TXT("google._domainkey", "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAooSJ0AtdVuBRVrS7D+I9nYTppJFeRgtAlpbJufWZLPNg9lu1k5PZnbIvxxP/KcMcTtxch2X2Lfg+JCdn3NSJRx9Tx9DM0p9/1odOxotNiED1g6bDrUwjPKhMqkUgx+jJ/W8NmJGGItYe50Nr2ADgsUczd7YWVnnNEU2EZTglD1/tE5eQsA0/9XEFi8mugbTN2Bme/VVu81X1r04xd6D3a95b3bTXRBEX8S/4T3O2d4MoVmEyBBzfMJ4CQmcgxTFLgMKqoKwT4OzOu2JFbTeisAMox89XtX11jnyYa2M2gSH15ovgWM+pow6eGOgHUWBbTNKB/ssLhKVrbJbeedGPBQIDAQAB"), + TXT("mail._domainkey.ml", "v=DKIM1; h=sha256; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDa9srBE8/dR7UT1VuXn7RD/MrtShhXQVBugaw8vpD0BJYQ3zsq7O+pF7EVMKwIAoPZeXFLA1eLpihhFExGrOFit+XEz54Le1Fvsnw3Blcc8Ul4UXjpb7NJfS8CcMUXiJFEZVanjs3A1d8BCedFRYN1kCXNqTtQ7LK/2xBJNCyKDwIDAQAB"), + TXT("ml", "v=spf1 a mx ip4:94.130.110.93 ip6:2a01:4f8:1c0c:6b10::1 ~all"), + TXT("@", "TICKET-9336"), + TXT("@", "twufyqeoih"), + TXT("@", "v=spf1 include:_spf.mx.cloudflare.net include:_spf.google.com ~all"), +); diff --git a/dns/creds.json b/dns/rubyci.org/creds.json similarity index 53% rename from dns/creds.json rename to dns/rubyci.org/creds.json index 7d36fbf..b362166 100644 --- a/dns/creds.json +++ b/dns/rubyci.org/creds.json @@ -1,6 +1,6 @@ { "cloudflare": { "TYPE": "CLOUDFLAREAPI", - "apitoken": "$CLOUDFLARE_API_TOKEN" + "apitoken": "$CLOUDFLARE_API_TOKEN_RUBYCI" } } diff --git a/dns/dnsconfig.js b/dns/rubyci.org/dnsconfig.js similarity index 85% rename from dns/dnsconfig.js rename to dns/rubyci.org/dnsconfig.js index ed38ce7..8207d66 100644 --- a/dns/dnsconfig.js +++ b/dns/rubyci.org/dnsconfig.js @@ -1,9 +1,7 @@ -// generated by get-zones. This is 'a decent first draft' and requires editing. +var DSP_CLOUDFLARE = NewDnsProvider("cloudflare"); +var REG_NONE = NewRegistrar("none"); -var DSP_CLOUDFLARE = NewDnsProvider("cloudflare", "CLOUDFLAREAPI"); -var REG_CHANGEME = NewRegistrar("none"); - -D("rubyci.org", REG_CHANGEME, +D("rubyci.org", REG_NONE, DnsProvider(DSP_CLOUDFLARE), DefaultTTL(1), A("amazon2023", "35.72.97.116"),