From d3a6647c49157ebbb8be2b567ddcc4a330005dc2 Mon Sep 17 00:00:00 2001 From: Antoine Augusti Date: Mon, 1 Jan 2024 17:03:40 +0100 Subject: [PATCH 1/4] Work on ops tests --- .github/workflows/ops_tests.yml | 13 ++++++ ops_tests/ops_tests.exs | 70 +++++++++++++++++++++++++-------- 2 files changed, 66 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/ops_tests.yml diff --git a/.github/workflows/ops_tests.yml b/.github/workflows/ops_tests.yml new file mode 100644 index 0000000000..d9fd5a9e2d --- /dev/null +++ b/.github/workflows/ops_tests.yml @@ -0,0 +1,13 @@ +on: push + +jobs: + test: + runs-on: ubuntu-latest + name: Run ops tests + steps: + - uses: actions/checkout@v4 + - uses: erlef/setup-beam@v1 + with: + version-file: .tool-versions + version-type: strict + - run: elixir ops_tests/ops_tests.exs diff --git a/ops_tests/ops_tests.exs b/ops_tests/ops_tests.exs index a01d5d563f..322e7683a1 100644 --- a/ops_tests/ops_tests.exs +++ b/ops_tests/ops_tests.exs @@ -3,27 +3,21 @@ ExUnit.start() Mix.install([ - {:req, "~> 0.2.1"} + {:req, "~> 0.4.8"}, + {:dns, "~> 2.4.0"} ]) defmodule Transport.OpsTests do - use ExUnit.Case + use ExUnit.Case, async: true - def get_header!(headers, header) do - {_header, value} = - headers - |> Enum.find(fn {k, _} -> k == header end) - - value - end - - def assert_redirect(from: url, to: target_url) do - %{status: 301, headers: headers} = - Req.build(:get, url) - |> Req.run!() - - assert get_header!(headers, "location") == target_url - end + # See https://developers.clever-cloud.com/doc/administrate/domain-names/#your-application-runs-in-the-europeparis-par-zone + @domain_name "transport.data.gouv.fr" + @clever_cloud_ip_addresses [ + {46, 252, 181, 103}, + {46, 252, 181, 104}, + {185, 42, 117, 108}, + {185, 42, 117, 109} + ] test "correct DOMAIN_NAME for prod-worker" do assert_redirect( @@ -38,4 +32,46 @@ defmodule Transport.OpsTests do to: "https://workers.prochainement.transport.data.gouv.fr/" ) end + + test "redirects from www to non-www" do + assert_redirect(from: "https://www.#{@domain_name}", to: "https://#{@domain_name}/") + end + + describe "Check DNS records" do + test "main A/CNAME records" do + {:ok, ips} = DNS.resolve(@domain_name, :a) + assert MapSet.new(ips) == MapSet.new(@clever_cloud_ip_addresses) + + # CNAMEs to Clever Cloud + [ + "prochainement", + "proxy", + "proxy.prochainement", + "validation", + "workers", + "workers.prochainement", + "www" + ] + |> Enum.each(fn subdomain -> + record = "#{subdomain}.#{@domain_name}" + assert {:ok, [~c"domain.par.clever-cloud.com"]} == DNS.resolve(record, :cname), "Wrong DNS record for #{record}" + end) + + # Satellite websites + assert {:ok, [~c"transport-blog.netlify.app"]} == DNS.resolve("blog.#{@domain_name}", :cname) + assert {:ok, [~c"transport-contribuer.netlify.app"]} == DNS.resolve("contribuer.#{@domain_name}", :cname) + assert {:ok, [~c"hosting.gitbook.com"]} == DNS.resolve("doc.#{@domain_name}", :cname) + assert {:ok, [~c"stats.uptimerobot.com"]} == DNS.resolve("status.#{@domain_name}", :cname) + end + end + + def get_header!(headers, header) do + {_header, [value]} = Enum.find(headers, fn {k, _} -> k == header end) + value + end + + def assert_redirect(from: url, to: target_url) do + %Req.Response{status: 301, headers: headers} = Req.get!(url, redirect: false) + assert get_header!(headers, "location") == target_url + end end From c9db7b33d230a8a99b2acebb415feabc2fa27119 Mon Sep 17 00:00:00 2001 From: Antoine Augusti Date: Mon, 1 Jan 2024 17:13:24 +0100 Subject: [PATCH 2/4] Add tests for email records --- ops_tests/ops_tests.exs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/ops_tests/ops_tests.exs b/ops_tests/ops_tests.exs index 322e7683a1..134d5998c4 100644 --- a/ops_tests/ops_tests.exs +++ b/ops_tests/ops_tests.exs @@ -63,6 +63,31 @@ defmodule Transport.OpsTests do assert {:ok, [~c"hosting.gitbook.com"]} == DNS.resolve("doc.#{@domain_name}", :cname) assert {:ok, [~c"stats.uptimerobot.com"]} == DNS.resolve("status.#{@domain_name}", :cname) end + + test "MX records" do + {:ok, records} = DNS.resolve(@domain_name, :mx) + assert MapSet.new([{10, ~c"mx1.alwaysdata.com"}, {20, ~c"mx2.alwaysdata.com"}]) == MapSet.new(records) + assert {:ok, [{100, ~c"mx.sendgrid.net"}]} = DNS.resolve("front-mail.#{@domain_name}", :mx) + end + + test "SPF, DKIM and DMARC" do + # SPF + {:ok, records} = DNS.resolve(@domain_name, :txt) + + assert Enum.member?(records, [ + ~c"v=spf1 include:spf.mailjet.com include:_spf.alwaysdata.com include:_spf.scw-tem.cloud include:servers.mcsv.net -all" + ]) + + assert {:ok, [[~c"v=spf1 include:sendgrid.net ~all"]]} = DNS.resolve("front-mail.#{@domain_name}", :txt) + + # DKIM + assert {:ok, _} = DNS.resolve("37d278a7-e548-4029-a58d-111bdcf23d46._domainkey.#{@domain_name}", :txt) + assert {:ok, [~c"dkim2.mcsv.net"]} == DNS.resolve("k2._domainkey.#{@domain_name}", :cname) + assert {:ok, [~c"dkim3.mcsv.net"]} == DNS.resolve("k3._domainkey.#{@domain_name}", :cname) + + # DMARC + assert {:ok, [[~c"v=DMARC1;p=quarantine;"]]} == DNS.resolve("_dmarc.#{@domain_name}", :txt) + end end def get_header!(headers, header) do From 3266095630b82e91e99d31d4215d43b6086d6a43 Mon Sep 17 00:00:00 2001 From: Antoine Augusti Date: Mon, 1 Jan 2024 11:16:53 -0500 Subject: [PATCH 3/4] CI job: add name --- .github/workflows/ops_tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ops_tests.yml b/.github/workflows/ops_tests.yml index d9fd5a9e2d..a0af9d8663 100644 --- a/.github/workflows/ops_tests.yml +++ b/.github/workflows/ops_tests.yml @@ -1,3 +1,4 @@ +name: CI ops tests on: push jobs: From 1ca50c8dbabe558ac5159764f7cc294da871644c Mon Sep 17 00:00:00 2001 From: Antoine Augusti Date: Mon, 1 Jan 2024 17:19:44 +0100 Subject: [PATCH 4/4] Add more DKIM records --- ops_tests/ops_tests.exs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ops_tests/ops_tests.exs b/ops_tests/ops_tests.exs index 134d5998c4..fd27355d4e 100644 --- a/ops_tests/ops_tests.exs +++ b/ops_tests/ops_tests.exs @@ -82,6 +82,9 @@ defmodule Transport.OpsTests do # DKIM assert {:ok, _} = DNS.resolve("37d278a7-e548-4029-a58d-111bdcf23d46._domainkey.#{@domain_name}", :txt) + assert {:ok, _} = DNS.resolve("default._domainkey.#{@domain_name}", :txt) + assert {:ok, _} = DNS.resolve("fnt._domainkey.#{@domain_name}", :txt) + assert {:ok, _} = DNS.resolve("mailjet._domainkey.#{@domain_name}", :txt) assert {:ok, [~c"dkim2.mcsv.net"]} == DNS.resolve("k2._domainkey.#{@domain_name}", :cname) assert {:ok, [~c"dkim3.mcsv.net"]} == DNS.resolve("k3._domainkey.#{@domain_name}", :cname)