Skip to content

Instantly share code, notes, and snippets.

@steelbrain
Created December 29, 2023 21:27
Show Gist options
  • Save steelbrain/e57bff5c422b050d50bfa8f4c2c9048a to your computer and use it in GitHub Desktop.
Save steelbrain/e57bff5c422b050d50bfa8f4c2c9048a to your computer and use it in GitHub Desktop.

Github Self Hosted Runner Provisioning

This is the script that I use to provision Github's self hosted runners for my usecase. You may have to make modifications to it to fit your usecase. The script expects to run as root and expects a non-root "administrator" account for the system.

The goal of the provisioning is to bring this setup as close to the real thing as possible. You may have to make some modifications to your github actions config to make the most out of this. Most important of which would be the removal of setup buildx action.

#!/bin/bash
# NOTES: Run this as root
set -euo pipefail
# Variables to fill out
GITHUB_ORG_NAME="Capfolio-com"
LINUX_USER="" # Linux user that's used to sign in (ie admin/sudo user)
PASSWORD_NEW=""
PRIVATE_NETWORK_IP="" # Eg 10.88.88.1
PRIVATE_NETWORK_REGISTRY="" # Eg 10.88.88.2:5000
ACTIONS_RUNNER_TOKEN=""
DOCKER_BUILDX_VERSION="v0.12.0" # https://github.com/docker/buildx/releases
# Validation for said variables
if [ -z "${GITHUB_ORG_NAME-}" ]; then echo "ACTIONS_RUNNER_TOKEN is not set"; exit 1; fi
if [ -z "${LINUX_USER-}" ]; then echo "LINUX_USER is not set"; exit 1; fi
if [ -z "${PASSWORD_NEW-}" ]; then echo "PASSWORD_NEW is not set"; exit 1; fi
if [ -z "${PRIVATE_NETWORK_IP-}" ]; then echo "PRIVATE_NETWORK_IP is not set"; exit 1; fi
if [ -z "${PRIVATE_NETWORK_REGISTRY-}" ]; then echo "PRIVATE_NETWORK_IP is not set"; exit 1; fi
if [ -z "${ACTIONS_RUNNER_TOKEN-}" ]; then echo "ACTIONS_RUNNER_TOKEN is not set"; exit 1; fi
if [ -z "${DOCKER_BUILDX_VERSION-}" ]; then echo "DOCKER_BUILDX_VERSION is not set"; exit 1; fi
# Set global context
export NEEDRESTART_MODE="a"
# Change password
echo -e "$PASSWORD_NEW\n$PASSWORD_NEW" | passwd $LINUX_USER
# Base utils
apt update
apt dist-upgrade -y
apt install -y nano wget curl screen build-essential pwgen
# Create the user
PASSWORD_ACTIONS="$(pwgen 16 1)"
useradd -m -s /usr/sbin/nologin actions
echo -e "$PASSWORD_ACTIONS\n$PASSWORD_ACTIONS" | passwd actions
# Setup the actions runner
cat <<EOF > /home/actions/provision.sh
#!/bin/bash
set -euo pipefail
cd ~
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.311.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz
echo "29fc8cf2dab4c195bb147384e7e2c94cfd4d4022c793b346a6175435265aa278 actions-runner-linux-x64-2.311.0.tar.gz" | shasum -a 256 -c
tar xzf ./actions-runner-linux-x64-2.311.0.tar.gz
./config.sh --url https://github.com/$GITHUB_ORG_NAME --token "$ACTIONS_RUNNER_TOKEN"
EOF
chown actions.actions /home/actions/provision.sh
chmod +x /home/actions/provision.sh
su -s /bin/bash -c '/home/actions/provision.sh' actions
rm /home/actions/provision.sh
# Node.js -- needed by the actions
apt install -y ca-certificates curl gnupg
mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
apt update
apt install nodejs -y
# Yarn -- needed by the actions
corepack enable
# Docker plugins -- needed by actions
mkdir -p /home/actions/.docker/cli-plugins
wget "https://github.com/docker/buildx/releases/download/${DOCKER_BUILDX_VERSION}/buildx-${DOCKER_BUILDX_VERSION}.linux-amd64" -O /home/actions/.docker/cli-plugins/docker-buildx
chmod +x /home/actions/.docker/cli-plugins/docker-buildx
chown -R actions.actions /home/actions/.docker
# Docker -- needed by actions
apt install -y dbus-user-session uidmap iptables systemd-container
machinectl shell actions@ /bin/bash -c 'curl -fsSL https://get.docker.com/rootless | sh'
loginctl enable-linger actions
systemctl --user -M actions@ enable docker
# Automatic updates
apt install -y unattended-upgrades apt-listchanges
# Setup IP address for private network interface
NETPLAN_CONFIG="/etc/netplan/00-installer-config.yaml"
cat <<EOF > /tmp/provision-ens4_config.tmp
ens4:
dhcp4: false
addresses:
- ${PRIVATE_NETWORK_IP}/24
EOF
cp $NETPLAN_CONFIG "${NETPLAN_CONFIG}.bak"
sed -i '/ethernets:/r /tmp/provision-ens4_config.tmp' /etc/netplan/00-installer-config.yaml
# ^ Storing in a temp file because sed doesn't like inline multiline inputs
netplan apply
# Pre-emptively setup the systemd service file for Github Actions
cat <<EOF > /etc/systemd/system/actions-runner.service
[Unit]
Description="GitHub Actions Runner"
Wants=network-online.target
After=network-online.target
[Service]
Environment=PATH=/home/actions/bin:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/local/games:/snap/bin
Environment=XDG_RUNTIME_DIR=/run/user/$(id -u actions)
Environment=DOCKER_HOST=unix:///run/user/$(id -u actions)/docker.sock
ExecStart=/home/actions/actions-runner/bin/runsvc.sh
WorkingDirectory=/home/actions/actions-runner
User=actions
Group=actions
KillMode=process
KillSignal=SIGTERM
TimeoutStopSec=5min
Restart=always
RestartSec=3s
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable actions-runner
# Setup env variables
cat <<EOF > /home/actions/actions-runner/.env
LANG=C.UTF-8
XDG_RUNTIME_DIR=/run/user/$(id -u actions)
DOCKER_HOST=unix:///run/user/$(id -u actions)/docker.sock
EOF
cat << 'EOF' > /home/actions/actions-runner/.path
/home/actions/bin:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/local/games:/snap/bin
EOF
chown actions.actions /home/actions/actions-runner/.env
# Harden ssh
cat <<EOF >> /etc/ssh/sshd_config
AllowUsers $LINUX_USER
PermitRootLogin no
ForceCommand /bin/false
Match User $LINUX_USER
ForceCommand /bin/bash
EOF
sudo systemctl restart ssh
# Tell docker to allow insecure registry for local registry host
mkdir -p /home/actions/.config/docker
cat <<EOF > /home/actions/.config/docker/daemon.json
{
"insecure-registries": ["$PRIVATE_NETWORK_REGISTRY"]
}
EOF
chown -R actions.actions /home/actions/.config
# Start the thing
systemctl restart actions-runner
systemctl --user -M actions@ restart docker
systemctl status actions-runner
systemctl --user -M actions@ status docker
# Firewall
apt install -y ufw
ufw allow ssh
ufw default reject
ufw --force enable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment