Cloudflare maintenance page using Cloudflare Workers and Gitlab-ci

Description:

 The objective is to be able to put a Cloudflare managed domain into maintenance mode in order to safely perform updates by avoiding client requests using Cloudflare Workers technology combined with Gitlab-ci.

Implementation Options:

  • First Option: use Wrangler (Workers Cli) to automatically manage the entire process.
    •  Advantage: pro solution, not susceptible to errors due to human interventions.
    •  Disvantage: requires more resources on Gitlab-ci and takes longer to run.
  • Second Option: configure (manually or not) the requirements and then handle the process through simple API calls.
    • Advantage: quick and simple solution, almost no resources needed on Gitlab-ci.
    • Disvantage: requires manual configuration and is susceptible to errors due to human interventions.

First Option – How To:

  • Project Structure:
.
|-- .gitlab-ci.yml
|-- README.md
|-- wrangler.toml # Wrangler configuration.
|-- src/
|    |-- worker.js # Maintenance Page script generation.
  • .gitlab-ci.yml:
# Cloudflare Maintenance Page using Cloudflare Workers Example.
# List of stages for jobs, and their order of execution

stages:
  - build

# This job could be splitted into Maintenance On / Off
# 1. Installs Wrangler (Workers CLI)
# 2. Creates a new Worker called "cf-worker"
# 3. Deploy the Worker using the src/worker.js page template
# 4. Pause in order to manually check if site is on Maintenance Mode
# 5. Delete the Worker returning site to Normal Mode

build-job:
  image: node:18-alpine
  stage: build
  script:
    - npm install -g wrangler
    - npm create cloudflare@2 -- cf-worker
    - wrangler deploy src/worker.js
    - sleep 30
    - wrangler delete src/worker.js
  • wrangler.toml:
name = "cf-worker"
compatibility_date = "2023-08-29"
workers_dev = false
route = { pattern = "example.com/*", zone_name = "example.com" }
  • src/worker.js:
export default {
  async fetch(request) {
    const html = `<!DOCTYPE html>
		<body>
		  <h1>Site on Maintenance!</h1>
		  <p>This markup was generated by a Cloudflare Worker.</p>
		</body>`;

    return new Response(html, {
      headers: {
        "content-type": "text/html;charset=UTF-8",
      },
    });
  },
};

Second Option – How To:

  • Access Cloudflare Account and get Zone ID of the selected domain.
  • Create an API Token for the selected domain with the required permissions to handle Workers.
    • Account: Cloudflare Pages: Edit
    • Account: Workers RD2 Storage: Edit
    • Account: Workers Tail: Read
    • Account: Workers KV Storage: Edit
    • Account: Workers Scripts: Edit
    • Account: Account Settings: Read
    • User: Memberships: Read
    • User: User Details: Read
    • Zone: Workers Routes: Edit
    • Account Resources: Include: Account
    • Zone Resources: Include: Specific Zone: example.com
  • Create a maintenance subdomain for the selected domain (mp.example.com).
  • Create the Worker with the code to generate the maintenance page.
  • Add a new route associated with the created worker using the maintenance subdomain as pattern.
  • Get the Route ID of that route and save it to a variable.

Getting Zone ID:

curl --url https://api.cloudflare.com/client/v4/zones/"$ZONE_ID"/workers/routes \
--header "Authorization: Bearer $API_TOKEN"

Use an API call to modify that route and put the main domain instead on maintenance mode:

# Maintenance Mode On
curl --request PUT \
--url https://api.cloudflare.com/client/v4/zones/"$ZONE_ID"/workers/routes/"$ROUTE_ID" \
--header "Authorization: Bearer $API_TOKEN" \
--header "Content-Type: application/json" \
--data '{"pattern":"example.com/*","script":"cf-worker"}'

Use an API call to modify that route and put back the subdomain on maintenance mode:

# Maintenance Mode Off
curl --request PUT \
--url https://api.cloudflare.com/client/v4/zones/"$ZONE_ID"/workers/routes/"$ROUTE_ID" \
--header "Authorization: Bearer $API_TOKEN" \
--header "Content-Type: application/json" \
--data '{"pattern":"mp.example.com/*","script":"cf-worker"}'

References:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.