Skip to content

ansible-easy-vpn Testing #455

ansible-easy-vpn Testing

ansible-easy-vpn Testing #455

Workflow file for this run

---
name: CI
run-name: ansible-easy-vpn Testing
on:
push:
pull_request_review:
workflow_dispatch:
inputs:
only_ubuntu_22:
description: "Only run on Ubuntu 22.04"
required: false
type: boolean
default: false
only_ubuntu_20:
description: "Only run on Ubuntu 20.04"
required: false
type: boolean
default: false
only_debian_11:
description: "Only run on Debian 11"
required: false
type: boolean
default: false
only_debian_12:
description: "Only run on Debian 12"
required: false
type: boolean
default: false
manual_mode:
description: "Don't destroy the server after the setup is complete"
required: false
type: boolean
default: false
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout the current branch
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install ansible-lint
run: pip install ansible ansible-lint
- name: Comment out the ask_vault_password bit
run: "sed -i'.bak' -e 's/ask_vault_pass/#ask_vault_pass/' ansible.cfg"
- name: Ansible-lint
run: ansible-lint
matrix_prep:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
letsencrypt_staging: ${{ steps.set-matrix.outputs.letsencrypt_staging }}
steps:
- name: Checkout the code
uses: actions/checkout@v2
- id: set-matrix
run: |
if [[ ${ONLY_UBUNTU_22} == 'true' ]]; then
# Only deploy on Ubuntu 22.04, don't use Letsencrypt Staging
matrix=$(jq 'map(. | select((.os=="ubuntu-22.04")) )' .github/workflows/matrix_includes.json)
elif [[ ${ONLY_UBUNTU_20} == 'true' ]]; then
# Only deploy on Ubuntu 20.04, don't use Letsencrypt Staging
matrix=$(jq 'map(. | select((.os=="ubuntu-20.04")) )' .github/workflows/matrix_includes.json)
elif [[ ${ONLY_DEBIAN_11} == 'true' ]]; then
# Only deploy on Debian 11, don't use Letsencrypt Staging
matrix=$(jq 'map(. | select((.os=="debian-11")) )' .github/workflows/matrix_includes.json)
elif [[ ${ONLY_DEBIAN_12} == 'true' ]]; then
# Only deploy on Debian 11, don't use Letsencrypt Staging
matrix=$(jq 'map(. | select((.os=="debian-12")) )' .github/workflows/matrix_includes.json)
else
# Deploy on all supported OSes, use Letsencrypt Staging to avoid rate-limiting
matrix=$(jq 'map(.)' .github/workflows/matrix_includes.json)
fi
echo "matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT
echo "letsencrypt_staging='yes'" >> $GITHUB_OUTPUT
env:
ONLY_UBUNTU_22: ${{ inputs.only_ubuntu_22 }}
ONLY_UBUNTU_20: ${{ inputs.only_ubuntu_20 }}
ONLY_DEBIAN_11: ${{ inputs.only_debian_11 }}
ONLY_DEBIAN_12: ${{ inputs.only_debian_12 }}
build:
runs-on: ubuntu-latest
needs: matrix_prep
environment: cicd
strategy:
fail-fast: false
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
steps:
- name: Initialize the ssh-agent
uses: webfactory/[email protected]
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Checkout the branch locally
uses: actions/checkout@v3
- name: Update apt cache on the runner and install dependencies
run: sudo apt update && sudo apt install sshpass expect wamerican
- name: Generate a random username and password
run: |
echo "EASYVPN_USERNAME=$(shuf -n 1 /usr/share/dict/words | tr '[:upper:]' '[:lower:]' | tr -dc '[:alnum:]' )" >> $GITHUB_ENV &&
echo "EASYVPN_PASSWORD=$(openssl rand -base64 20)" >> $GITHUB_ENV
- name: Set the username and password outputs
id: random_username
run: |
echo "EASYVPN_USERNAME_$INDEX=$EASYVPN_USERNAME" >> $GITHUB_OUTPUT
echo "EASYVPN_PASSWORD_$INDEX=$EASYVPN_PASSWORD" >> $GITHUB_OUTPUT
env:
INDEX: ${{ matrix.index }}
- name: Spawn a Hetzner node
uses: TimDaub/hetzner-cloud-deploy-server-action@v2
with:
server-name: ansible-easy-vpn-${{ env.EASYVPN_USERNAME }}
server-image: ${{ matrix.os }}
server-type: "cx11"
ssh-key-name: "github-actions"
hcloud-token: ${{ secrets.HCLOUD_TOKEN }}
delete-server: false
- name: Add the host key to known hosts
run: mkdir -p ~/.ssh/ && ssh-keyscan -H $SERVER_IPV4 >> ~/.ssh/known_hosts
- name: Update apt cache
run: ssh root@$SERVER_IPV4 apt update
- name: Install git and expect (Debian-based)
run: ssh root@$SERVER_IPV4 apt install -y git expect wamerican
- uses: infraway/[email protected]
with:
type: "A"
name: "${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}"
content: "${{ env.SERVER_IPV4 }}"
proxied: false
token: ${{ secrets.CLOUDFLARE_TOKEN }}
zone: ${{ secrets.CLOUDFLARE_ZONE }}
- uses: infraway/[email protected]
with:
type: "CNAME"
name: "auth.${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}"
content: "${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}"
proxied: false
token: ${{ secrets.CLOUDFLARE_TOKEN }}
zone: ${{ secrets.CLOUDFLARE_ZONE }}
- uses: infraway/[email protected]
with:
type: "CNAME"
name: "wg.${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}"
content: "${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}"
proxied: false
token: ${{ secrets.CLOUDFLARE_TOKEN }}
zone: ${{ secrets.CLOUDFLARE_ZONE }}
- uses: infraway/[email protected]
with:
type: "CNAME"
name: "adguard.${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}"
content: "${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}"
proxied: false
token: ${{ secrets.CLOUDFLARE_TOKEN }}
zone: ${{ secrets.CLOUDFLARE_ZONE }}
- name: Clone the branch to the Hetzner instance
run: ssh root@$SERVER_IPV4 git clone https://github.com/notthebee/ansible-easy-vpn -b ${{ github.head_ref || github.ref_name }}
env:
GIT_SSL_NO_VERIFY: "true"
- name: Execute the automated bootstrap script
run: >-
ssh -t -t root@$SERVER_IPV4
"export LETSENCRYPT_STAGING=$LETSENCRYPT_STAGING &&
ansible-easy-vpn/testing/expect/defaults.exp
--username $EASYVPN_USERNAME
--password $EASYVPN_PASSWORD
--smtp_server $SMTP_SERVER
--smtp_login $SMTP_LOGIN
--smtp_password $SMTP_PASSWORD
--domain $DOMAIN"
env:
LETSENCRYPT_STAGING: ${{ needs.matrix_prep.outputs.letsencrypt_staging }}
SMTP_SERVER: ${{ secrets.SMTP_SERVER }}
SMTP_LOGIN: ${{ secrets.SMTP_LOGIN }}
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
DOMAIN: ${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}
- name: Copy the private key from the Hetzner host
run: sshpass -p$EASYVPN_PASSWORD scp root@$SERVER_IPV4:/tmp/id_ssh_ed25519 ~/.ssh/id_vpn
- name: Initialize the ssh-agent
uses: webfactory/[email protected]
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Regsiter the private key with the ssh-agent
run: expect -c "spawn ssh-add $HOME/.ssh/id_vpn; expect -re \"Enter passphrase.*\"; send -- \"$EASYVPN_PASSWORD\r\"; expect -re \"Identity added.*\""
- name: Reboot the server
run: |
ssh -t -t $EASYVPN_USERNAME@$SERVER_IPV4 "sudo reboot" || true
- name: Wait for the server to reboot
run: >-
sleep 60s
- name: Copy the private key to the Github root
run: cp $HOME/.ssh/id_vpn id_vpn
- name: Log into the server and execute the script again for idempotency
run: >-
ssh -t -t $EASYVPN_USERNAME@$SERVER_IPV4 "export LETSENCRYPT_STAGING=$LETSENCRYPT_STAGING; ansible-easy-vpn/testing/expect/idempotency.exp --password $EASYVPN_PASSWORD"
env:
LETSENCRYPT_STAGING: ${{ needs.matrix_prep.outputs.letsencrypt_staging }}
- name: Add the personal SSH key for debugging
run: >-
curl https://github.com/notthebee.keys > notthebee.pub &&
ssh-copy-id -f -i notthebee.pub $EASYVPN_USERNAME@$SERVER_IPV4
- name: Archive the private SSH key
uses: actions/upload-artifact@v3
with:
name: "private-ssh-key-${{ matrix.index }}"
path: "id_vpn"
outputs:
EASYVPN_USERNAME_1: "${{ steps.random_username.outputs.EASYVPN_USERNAME_1 }}"
EASYVPN_USERNAME_2: "${{ steps.random_username.outputs.EASYVPN_USERNAME_2 }}"
EASYVPN_USERNAME_3: "${{ steps.random_username.outputs.EASYVPN_USERNAME_3 }}"
EASYVPN_USERNAME_4: "${{ steps.random_username.outputs.EASYVPN_USERNAME_4 }}"
EASYVPN_PASSWORD_1: "${{ steps.random_username.outputs.EASYVPN_PASSWORD_1 }}"
EASYVPN_PASSWORD_2: "${{ steps.random_username.outputs.EASYVPN_PASSWORD_2 }}"
EASYVPN_PASSWORD_3: "${{ steps.random_username.outputs.EASYVPN_PASSWORD_3 }}"
EASYVPN_PASSWORD_4: "${{ steps.random_username.outputs.EASYVPN_PASSWORD_4 }}"
debug:
runs-on: ubuntu-latest
environment: cicd
strategy:
fail-fast: false
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
needs:
- matrix_prep
- build
steps:
- name: Wait for the server to reboot
if: inputs.manual_mode
run: >-
sleep infinity
fetch_config:
runs-on: ubuntu-latest
environment: cicd
strategy:
fail-fast: false
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
needs:
- matrix_prep
- debug
- build
steps:
- name: Check out this repo
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install expect
run: >-
sudo apt install expect
- name: Create the .ssh folder
run: >-
mkdir /home/runner/.ssh
- name: Get the private SSH key artifact
uses: actions/download-artifact@v3
with:
name: "private-ssh-key-${{ matrix.index }}"
path: /home/runner/.ssh
- name: Set the correct permissions for the SSH key
run: |
chmod 700 $HOME/.ssh
chmod 600 $HOME/.ssh/*
- name: Initialize the ssh-agent
uses: webfactory/[email protected]
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Regsiter the private key with the ssh-agent
run: expect -c "spawn ssh-add $HOME/.ssh/id_vpn; expect -re \"Enter passphrase.*\"; send -- \"$EASYVPN_PASSWORD\r\"; expect -re \"Identity added.*\""
env:
EASYVPN_PASSWORD: "${{ needs.build.outputs[format('EASYVPN_PASSWORD_{0}', matrix.index)] }}"
- name: Remove Chrome
run: sudo apt purge google-chrome-stable
- name: Remove default Chromium
run: sudo apt purge chromium-browser
- name: Install snapd
run: sudo apt install snapd
- name: Install chromium
run: sudo snap install chromium
- name: Install all necessary packages
run: pip install webdriver-manager selenium pyotp pexpect
- name: Create the config dir
run: mkdir /home/runner/wireguard
- name: Run the testing script
run: python testing/selenium/acceptance.py --username $EASYVPN_USERNAME --password $EASYVPN_PASSWORD --base_url $DOMAIN --ssh_agent $SSH_AUTH_SOCK
env:
EASYVPN_USERNAME: "${{ needs.build.outputs[format('EASYVPN_USERNAME_{0}', matrix.index)] }}"
EASYVPN_PASSWORD: "${{ needs.build.outputs[format('EASYVPN_PASSWORD_{0}', matrix.index)] }}"
DOMAIN: "${{ needs.build.outputs[format('EASYVPN_USERNAME_{0}', matrix.index)] }}.${{ secrets.CLOUDFLARE_DOMAIN }}"
- name: Upload Selenium testing screenshots
if: always()
uses: actions/upload-artifact@v4
with:
name: "Screenshots-${{ matrix.index }}"
path: "/home/runner/screenshots"
- name: Archive the Wireguard config
uses: actions/upload-artifact@v3
with:
name: "wireguard-${{ matrix.index }}.conf"
path: "*.conf"
destroy:
runs-on: ubuntu-latest
environment: cicd
strategy:
fail-fast: false
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
if: always()
needs:
- matrix_prep
- build
- debug
- fetch_config
steps:
- name: Destroy the Hetzner instances
run: >-
export SERVER_ID=$(curl -H
"Authorization: Bearer $HCLOUD_TOKEN" https://api.hetzner.cloud/v1/servers |
jq --arg SERVER_NAME "$SERVER_NAME" '.servers[] | select(.name==$SERVER_NAME) | .id') &&
curl -X DELETE -H "Authorization: Bearer $HCLOUD_TOKEN"
https://api.hetzner.cloud/v1/servers/$SERVER_ID
env:
HCLOUD_TOKEN: ${{ secrets.HCLOUD_TOKEN }}
SERVER_NAME: ansible-easy-vpn-${{ needs.build.outputs[format('EASYVPN_USERNAME_{0}', matrix.index)] }}
- name: Delete all Cloudflare domains
run: |
curl --silent "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?per_page=50000" \
--header "Authorization: Bearer $CLOUDFLARE_TOKEN" \
| jq --raw-output '.result[].id' | while read id
do
curl --silent --request DELETE "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$id" \
--header "Authorization: Bearer $CLOUDFLARE_TOKEN"
done
env:
CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }}
ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE }}