Skip to content

Instantly share code, notes, and snippets.

@jonmbake
Last active December 28, 2022 04:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonmbake/5ec4435b39bec9ad21d34581cf0c93ea to your computer and use it in GitHub Desktop.
Save jonmbake/5ec4435b39bec9ad21d34581cf0c93ea to your computer and use it in GitHub Desktop.
A script to create a SSL-enabled, custom domain, website with Github pages and Cloudflare.
#!/usr/bin/env bash
# ##################################################
# Creates a SSL-enabled, custom domain, website with Github Pages and Cloudflare.
#
# USAGE: ./init-website.sh
# AUTHOR: Jon Bake <jonmbake@gmail.com>
# LICENSE: MIT
# VERSION: 1.0.0
#
# ##################################################
echo "This script requires a Github Personal Access Token with repo scope (see https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token to create one)..."
echo "And a Gloudflare Personal API Token with User:Memberships:Read, Zone:Zone:Edit, Zone:DNS:Edit and Zone:Zone Settings:Edit permissions (see https://developers.cloudflare.com/api/tokens/create). These tokens should be created berfore running this script."
read -p "Press enter to continue"
# Access tokens can set as an environment variable; if not set, prompt for value
if [ -z "$GITHUB_ACCESS_TOKEN" ]; then
echo "Github Personal Access Token (with repo scope): "; stty -echo; read GITHUB_ACCESS_TOKEN; stty echo;
fi
if [ -z "$CLOUDFLARE_ACCESS_TOKEN" ]; then
echo "Gloudflare Personal API Token (with Zone:Edit, DNS:Edit and Memberships:Read permissions): "; stty -echo; read CLOUDFLARE_ACCESS_TOKEN; stty echo;
fi
echo "Name of the github pages repo that you wish to create: "; read GITHUB_REPO_NAME;
echo "Custom domain name, e.g. example.com: "; read DOMAIN_NAME;
echo 'Github pages folder; must be either `/` or `/docs`.'; read GITHUB_PAGES_FOLDER;
echo "Initial markdown content of Github pages index.md, e.g. # My amazing site: "; read GITHUB_INDEX_CONTENT;
echo "Creating github repo with name '$GITHUB_REPO_NAME'..."
repo_creation_response=$(curl --silent --show-error -X POST \
--header "Authorization: token $GITHUB_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--header "Accept: application/vnd.github.v3+json" \
--data '{ "name" : "'"$GITHUB_REPO_NAME"'" }' \
https://api.github.com/user/repos)
echo $repo_creation_response
echo ""
# Parse out login from response
login_username_regex='"login"[[:space:]]*:[[:space:]]*"([0-9a-zA-Z\-]+)"'
[[ "$repo_creation_response" =~ $login_username_regex ]]
GITHUB_USERNAME="${BASH_REMATCH[1]}"
echo "Initializing github pages index.md file..."
init_index_response=$(curl --silent --show-error -X PUT \
--header "Authorization: token $GITHUB_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--header "Accept: application/vnd.github.v3+json" \
--data '{ "message" : "Initialize index.md", "content" : "'"$(echo $GITHUB_INDEX_CONTENT | base64)"'" }' \
"https://api.github.com/repos/$GITHUB_USERNAME/$GITHUB_REPO_NAME/contents$([ "$GITHUB_PAGES_FOLDER" == '/' ] && echo "/" || echo "/docs/")index.md")
echo $init_index_response
echo ""
echo "Enabling github pages..."
enable_gh_pages_response=$(curl --silent --show-error -X POST \
--header "Authorization: token $GITHUB_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--header "Accept: application/vnd.github.switcheroo-preview+json" \
--data '{ "source": { "branch": "main", "path": "'"$GITHUB_PAGES_FOLDER"'" } }' \
"https://api.github.com/repos/$GITHUB_USERNAME/$GITHUB_REPO_NAME/pages")
echo $enable_gh_pages_response
echo ""
echo "Setting github pages custom domain..."
setting_gh_pages_domain_response=$(curl --silent --show-error -X PUT \
--header "Authorization: token $GITHUB_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--header "Accept: application/vnd.github.switcheroo-preview+json" \
--data '{ "cname": "'"$DOMAIN_NAME"'" }' \
"https://api.github.com/repos/$GITHUB_USERNAME/$GITHUB_REPO_NAME/pages")
echo $setting_gh_pages_domain_response
echo ""
echo "Getting cloudflare account id..."
cloudflare_membership_response=$(curl --silent --show-error -X GET \
--header "Authorization: Bearer $CLOUDFLARE_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
https://api.cloudflare.com/client/v4/memberships)
echo $cloudflare_membership_response
echo ""
account_id_regex='"account".+"id"[[:space:]]*:[[:space:]]*"([0-9a-zA-Z]+)"'
[[ "$cloudflare_membership_response" =~ $account_id_regex ]]
CLOUDFLARE_ACCOUNT_ID="${BASH_REMATCH[1]}"
echo "Creating cloudflare zone with name '$DOMAIN_NAME'..."
cloudflare_create_zone_response=$(curl --silent --show-error -X POST \
--header "Authorization: Bearer $CLOUDFLARE_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "name": "'"$DOMAIN_NAME"'", "account" : { "id" : "'"$CLOUDFLARE_ACCOUNT_ID"'" }, "jump_start" : false, "type" : "full" }' \
https://api.cloudflare.com/client/v4/zones)
echo $cloudflare_create_zone_response
echo ""
name_servers_regex='"name_servers"[[:space:]]*:[[:space:]]*\[[[:space:]]*"([0-9a-zA-Z\.]+)"[[:space:]]*,[[:space:]]*"([0-9a-zA-Z\.]+)"[[:space:]]*\]'
[[ "$cloudflare_create_zone_response" =~ $name_servers_regex ]]
CLOUDFLARE_NAME_SERVER_1="${BASH_REMATCH[1]}"
CLOUDFLARE_NAME_SERVER_2="${BASH_REMATCH[2]}"
zone_id_regex='"result"[[:space:]]*:[[:space:]]*\{[[:space:]]*"id"[[:space:]]*:[[:space:]]*"([0-9a-zA-Z]+)"'
[[ "$cloudflare_create_zone_response" =~ $zone_id_regex ]]
CLOUDFLARE_ZONE_ID="${BASH_REMATCH[1]}"
echo "Setting cloudflare dns a record pointing to github pages first nameserver..."
curl --silent --show-error -X POST \
--header "Authorization: Bearer $CLOUDFLARE_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "type": "A", "name": "'"$DOMAIN_NAME"'", "content": "192.30.252.153", "ttl": 1, "proxied": true }' \
"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records"
echo ""
echo "Setting cloudflare dns a record pointing to github pages second nameserver..."
curl --silent --show-error -X POST \
--header "Authorization: Bearer $CLOUDFLARE_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "type": "A", "name": "'"$DOMAIN_NAME"'", "content": "192.30.252.154", "ttl": 1, "proxied": true }' \
"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records"
echo ""
echo ""
echo "*TO FINISH SETUP*: Update the DNS nameservers where $DOMAIN_NAME is registered with the following cloudflare nameservers: $CLOUDFLARE_NAME_SERVER_1, $CLOUDFLARE_NAME_SERVER_2"
echo "*IMPORTANT*: Do not press enter until DNS is resolved, i.e. the webpage is accessible at http://$DOMAIN_NAME"
read -p "Press enter to continue to https/ssl setup"
echo ""
echo "Setting cloudflare ssl settings..."
curl --silent --show-error -X PATCH \
--header "Authorization: Bearer $CLOUDFLARE_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "value": "full" }' \
"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/settings/ssl"
echo ""
echo "Turning on cloudflare automatic HTTPS rewrites..."
curl --silent --show-error -X PATCH \
--header "Authorization: Bearer $CLOUDFLARE_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "value": "on" }' \
"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/settings/automatic_https_rewrites"
echo ""
echo "Turning on cloudflare always use HTTPS..."
curl --silent --show-error -X PATCH \
--header "Authorization: Bearer $CLOUDFLARE_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "enabled": true }' \
"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/ssl/universal/settings"
echo ""
echo ""
@jonmbake
Copy link
Author

This is what the required Gloudflare Personal API Token permissions looks like:

cloudflare-api-token-permissions

This is what the required Github Personal Access Token scope looks like:

github-api-token-permissions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment