Skip to main content

Library and CLI to prepare Linux VPS hosts as GitLab CI runners

Project description

esys-ci-host

Library and CLI to prepare Linux VPS hosts as GitLab CI runners (Docker, privileged DinD, runner registration).

Extracted from the CI-host portions of esysdox-ops so LibESys superbuild and other tooling can depend on a focused package.

Install

pip install esys-ci-host

On a VPS, prefer the dedicated virtualenv used by remote mode (avoids apt/pip conflicts):

sudo apt-get update
sudo apt-get install -y python3-venv python3-pip python3.12-venv   # match your python3 -V
sudo mkdir -p /opt/esys-ci-host
sudo rm -rf /opt/esys-ci-host/venv
sudo python3 -m venv /opt/esys-ci-host/venv
sudo /opt/esys-ci-host/venv/bin/python -m ensurepip --upgrade
sudo /opt/esys-ci-host/venv/bin/python -m pip install esys-ci-host
sudo ln -sf /opt/esys-ci-host/venv/bin/esys-ci-host /usr/local/bin/esys-ci-host

Remote commands (esys-ci-host remote ...) create and use this venv automatically.

Quick start

On a fresh Ubuntu or Debian VPS (Docker executor for LibESys CI):

sudo esys-ci-host host bootstrap --yes --admin-user deploy --ssh-key-file ~/.ssh/id_ed25519.pub
# from your laptop: ssh deploy@<vps-ip>   (confirm login works)
sudo esys-ci-host host harden --yes
sudo esys-ci-host runner prepare --yes
esys-ci-host runner register --url https://gitlab.example.com --profile ci
esys-ci-host doctor

host bootstrap creates the admin user, installs OpenSSH, installs your SSH public key (or generates one under /etc/esys-ci-host/), verifies key setup, and enables UFW with SSH allowed. Root SSH stays enabled until you run host harden after confirming deploy login from your machine. Pass --ssh-key-file ~/.ssh/id_ed25519.pub or set ESYS_CI_HOST_ADMIN_SSH_PUBLIC_KEY / ESYS_CI_HOST_ADMIN_SSH_PUBLIC_KEY_FILE. Use --disable-root-ssh only if you accept lockout risk without external login verification.

The admin user has no login password (SSH key only). Bootstrap grants passwordless sudo via /etc/sudoers.d/99-esys-ci-host-<admin-user> so commands like sudo esys-ci-host host harden work after key login.

Set ESYS_CI_HOST_RUNNER_TOKEN instead of passing --token when registering.

Remote configuration (from your laptop)

Run the same host and runner steps over SSH without logging in manually first. Remote mode installs esys-ci-host into /opt/esys-ci-host/venv on the VPS (from PyPI or a local wheel), links /usr/local/bin/esys-ci-host, uploads your local SSH public key for the admin user, and runs the usual commands there.

Interactive (prompts for the root password when no key is given):

esys-ci-host remote --host 203.0.113.10 host bootstrap --yes --admin-user deploy
ssh -i ~/.ssh/id_ed25519 deploy@203.0.113.10   # verify login (admin has no password)
esys-ci-host remote --host 203.0.113.10 --user deploy --identity-file ~/.ssh/id_ed25519 host harden --yes
esys-ci-host remote --host 203.0.113.10 --user deploy --identity-file ~/.ssh/id_ed25519 runner prepare --yes

After a successful remote bootstrap, the tool can offer to write a managed ~/.ssh/config entry. It suggests a short name (for an IP like 161.97.147.246, default is {admin-user}-vps, e.g. esysadmin-vps; for runner01.example.com, default is runner01) and lets you change it before saving. Then ssh esysadmin-vps (or your chosen name) works. Use --setup-ssh-config to apply without prompts, --ssh-config-host NAME to set the alias directly, or --skip-ssh-config to disable the flow. esys-ci-host remote still needs --user and --identity-file today (Paramiko does not read OpenSSH config).

Non-interactive (CI or scripts — password via flag or env):

export ESYS_CI_HOST_SSH_PASSWORD='...'
esys-ci-host remote --host 203.0.113.10 --non-interactive host bootstrap --yes --admin-user deploy
Option Purpose
--host HOST Remote VPS hostname or IP (required)
--user USER SSH login user (default: root)
--password PASS SSH password (ESYS_CI_HOST_SSH_PASSWORD)
--identity-file PATH SSH private key (ESYS_CI_HOST_SSH_IDENTITY_FILE)
--non-interactive Fail instead of prompting for a password
--wheel PATH Install from a local wheel instead of PyPI
--ssh-key-file PATH Local admin public key (default: ~/.ssh/id_ed25519.pub)

Prefer --identity-file over --password after the first bootstrap. Avoid putting passwords on the command line in shared environments.

Commands still run on the VPS when you SSH in directly (host bootstrap, runner prepare, etc.) — both models are supported.

Host bootstrap

Option Purpose
--admin-user NAME Non-root login account with sudo (default: esysadmin)
--ssh-key KEY OpenSSH public key line for admin login
--ssh-key-file PATH File containing the public key
--ssh-port PORT SSH port used for localhost login verification (default: 22)
--disable-root-ssh Disable root SSH during bootstrap (default: keep root until host harden)
--yes Required to apply host changes
--dry-run Print steps without mutating the host

Admin login is SSH key only (no account password). Bootstrap writes a sudoers drop-in so the admin user can run sudo without a password.

If no key is supplied, an ed25519 key pair is generated at /etc/esys-ci-host/<admin-user>-id_ed25519 — copy the private key securely before closing your root session.

After bootstrap: SSH in as the admin user from your machine, then run sudo esys-ci-host host harden --yes to disable root login.

Runner registration and executors

The GitLab executor is chosen at registration time (runner register), not during runner install or runner prepare.

Option Purpose
--executor shell Run jobs directly on the host (default)
--executor docker Run jobs in containers (requires runner prepare)
--profile ci LibESys CI preset: docker executor, privileged DinD, tags docker-privileged, and a sensible default image
--docker-image IMAGE Override the runner’s default image (fallback only; see note below)
--docker-privileged Privileged docker executor (enabled automatically by --profile ci)
--tags TAGS Comma-separated runner tags (e.g. linux,docker)

Default image vs job image: GitLab Runner’s --docker-image (at register time) is only the fallback written into config.toml. Each CI job uses its own image: from .gitlab-ci.yml when present — you do not need to pass --docker-image for every image your pipeline uses. With --profile ci, a default (python:3.11-bookworm) is applied automatically; override it only if you want a different fallback for jobs that omit image:.

Shell executor (simple jobs on the host):

sudo esys-ci-host runner install --yes
esys-ci-host runner register --url https://gitlab.example.com --executor shell --tags shell

Docker executor (explicit; run prepare first). GitLab Runner requires a default image for non-interactive docker registration, so pass --docker-image here unless you use --profile ci:

sudo esys-ci-host runner prepare --yes
esys-ci-host runner register \
  --url https://gitlab.example.com \
  --executor docker \
  --docker-image python:3.11-bookworm \
  --docker-privileged \
  --tags docker-privileged

LibESys CI profile (recommended; includes docker executor defaults — no need to pass --docker-image unless overriding the fallback):

sudo esys-ci-host runner prepare --yes
esys-ci-host runner register --url https://gitlab.example.com --profile ci

Use --dry-run on runner register to print the gitlab-runner register command without executing it.

Development

Platform-specific host operations (packages, users, firewall, SSH) live under src/esys_ci_host/platform/. DebianAptBackend is implemented today; add new HostPlatformBackend subclasses (for example RhelDnfBackend) and register them in platform/registry.py to support additional distributions later. Runner install/prepare still use apt directly and will migrate to the platform layer in a follow-up.

python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pre-commit install
pre-commit install --hook-type commit-msg
pytest

Release tags vX.Y.Z must match src/esys_ci_host/__version__.py. Use make bump-release locally; CI publishes to GitLab PyPI, TestPyPI, and PyPI (manual).

Integration tests (Docker + systemd, Ubuntu 24.04 by default):

# Full runner workflow
bash tests/integration/run_docker.sh

# Host bootstrap + harden workflow
INTEGRATION_SCRIPT=/usr/local/bin/run-integration-host-bootstrap bash tests/integration/run_docker.sh

# Windows
.\test.ps1 -HostBootstrap

License

Apache-2.0 — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

esys_ci_host-0.3.5.tar.gz (61.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

esys_ci_host-0.3.5-py3-none-any.whl (52.4 kB view details)

Uploaded Python 3

File details

Details for the file esys_ci_host-0.3.5.tar.gz.

File metadata

  • Download URL: esys_ci_host-0.3.5.tar.gz
  • Upload date:
  • Size: 61.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for esys_ci_host-0.3.5.tar.gz
Algorithm Hash digest
SHA256 ba1333ac1343ca587377b7bc68a119a3717edf918f751fe098dc72d5ce34f5c8
MD5 16aa08f6a8aab32be2c062d9c6bcdce5
BLAKE2b-256 510750fb00dc95322c39cb3cb2afac97cd11ba4895fa0ea4f3ceac7bd84d526c

See more details on using hashes here.

File details

Details for the file esys_ci_host-0.3.5-py3-none-any.whl.

File metadata

  • Download URL: esys_ci_host-0.3.5-py3-none-any.whl
  • Upload date:
  • Size: 52.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for esys_ci_host-0.3.5-py3-none-any.whl
Algorithm Hash digest
SHA256 aab086e2dbcab8be49f391e852bf2f346303af18689e2a083e0bc62092181eef
MD5 ed01ee012122fc97f420172e0dd960f6
BLAKE2b-256 32cbd35e2a1e0e2aaf1e4548093eb5f0e1c2d0a3ac085d6cf1643185636fc686

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page