GitLab CI Deploy to Cloudflare Pages

The “Wizard” Way

Cloudflare (CF) Pages allows you to very easily deploy through both GitHub and GitLab through an easy to setup UI process on the CF dashboard, but this requires allowing Cloudflare to authenticate an App with some very large reach/visibility on your entire account (with your repository provider) — both public and private repositories¹. While you can single-out the project you want to automate the deployment on, it doesn’t restrict Cloudflare’s bot access to your other repositories… while I trust Cloudflare not to do anything they shouldn’t — it’s always better to be safe than sorry. It’s also a larger than necessary access vector that needs to be opened² (and CF already has a history of questionable security in this very regard):

After gaining control the researcher was able to specially craft a malicious root_dir to dump the environment variables of the process to a file. Those environment variables contained our GitHub bot’s authorization key. This would have allowed the attacker to read the repositories of other Pages' customers, and many of those repositories are private.

GitLab CI Way

An alternative is to simply create a .gitlab-ci.yml file in the root of your GitLab repository with the following code sample as a starting point. Change XXX to match the pre-created Pages projects name. Change ./src to be the path to the directory you want to deploy.

# The following environment variables are required# - CLOUDFLARE_ACCOUNT_ID# - CLOUDFLARE_API_TOKEN# - CF_ZONE_IDpublish:  stage: deploy  rules: # optional    - if: $CI_COMMIT_BRANCH == "main" # optional  image: node:latest  script:
    - npm install wrangler --location=global
    - wrangler pages publish ./src --project-name "XXX" --branch "main"

Before committing the .gitlab-ci.yml you’ll need to add 3 environment variables (which can be found in the CF Dashboard for your site/domain — not all in the Pages settings). You’ll need to set these in Settings > CI/CD > Variables for the project.

GitHub Actions Way

In a similar you could do something for GitHub Actions (and most other CI tools). Helpfully a few of these have already been documented by Cloudflare in the support page titled Use Direct Upload with continuous integration.

Conclusion

While slightly more time-consuming to initially setup, I prefer this method of deployment to Cloudflare Pages as it gives;

  • greater visibility into what happened while deploying (GitLab CI has great real-time build logs),

  • makes it very simple to make your pipeline more complex (perhaps you want to run tests before deployment — putting the CI into the CI/CD), and

  • all the while limiting a 3rd Parties visibility into stuff you maybe don’t want seen.

[1]: You can restrict access to a subset of GitHub repos, but the default option is to allow access to all your repos. This is not the case with GitLab (yet) — it’s all or nothing.

[2]: I believe this larger-than-necessary vector is due to the GitLab platform, not Cloudflare and other bot/third-parties, as the platform’s initial Auth flow doesn’t allow the user (the repository owner) to reduce the scope of the bot to a single project/repo. As at GitLab v15 — there isn’t a scope option to restrict access by project — it’s all or nothing.

UPDATE: a few days after posting this article, GitHub disclosed a bug in their GitHub Apps permissions model.

While they downplay the exploit’s use, it still shows that by reducing your service dependency scope; you increase security.