- About the Project
- Structure
- Environment Configurations
- Entry Point: Makefile
- Launch Instructions
- Playbook: Execution Steps
- Implemented DevSecOps Practices
This project is a production-ready automated configuration management setup for the health-api
web application using Ansible and GitOps powered by Argo CD.
The repository bootstraps a k3s cluster on virtual machines in Yandex Cloud, installs system components (ingress, Argo CD, GHCR secrets), and prepares the environment for further management via GitOps.
Key Objectives:
- full automation of master/worker node setup using Ansible;
- environment preparation for Argo CD and GitOps pattern;
- application of Projects, RBAC, and Git repositories configuration from
argocd-config-health-api
; - Applies Argo CD Applications (
stage
,prod
) fromgitops-apps-health-api
, automatically pulling manifests based on the environment variable. - configuration of access to the private GHCR registry;
- deployment of the ingress controller;
- use of a Makefile and a series of
bash
scripts for reproducible execution; - implementation of up-to-date DevSecOps practices with optimal coverage;
- container delivery via GitOps workflow.
The project implements a stage/prod
pattern with isolated workers and GitOps-driven management:
- The master node has public access, while workers are fully isolated from the outside world.
- Ansible bootstrap performs only the initial setup (installing k3s, ingress, Argo CD, GHCR secrets).
- After that, management is fully delegated to Argo CD: deploying services, rollout strategies (blue/green, canary), and configuration drift control.
- Separation into
stage
andprod
guarantees environment parity: all changes are tested on stage before being promoted to production.
This pattern reflects production-grade systems: security, drift prevention, and reproducibility are ensured by the GitOps approach combined with Argo CD.
Roles and playbooks in this project follow the primary GitOps scenario:
- Workers remain closed but have outbound access (via Yandex Cloud NAT).
- Images are pulled directly from GHCR over HTTPS.
- Access is provided via
imagePullSecrets
(ghcr-secret
). images_*
roles are not used (kept only as fallback).
The project also includes basic roles for a Fallback/Manual scenario:
- Workers are completely closed, with no public IPs and no internet access.
- Images can be built locally using
images_*
roles and transferred as.tar
files to the nodes. - Supports a fully isolated environment.
Detailed description of roles and playbooks for the Fallback/Manual scenario can be found in a separate repository: ansible-manual-health-api.
- ansible/ — boilerplate for reproducible cluster setup/update and application deployment across two environments (stage/prod).
- .ansible-lint — ansible-lint configuration: production profile, exceptions, strict rules.
- .gitignore — excludes artifacts (vault keys, logs, tmp files) from Git.
- .gitleaks.toml — gitleaks rules: secret scanning, ignores for encrypted files and artifacts.
- .pre-commit-config.yaml — hooks for automated checks (ansible-lint, yamllint, shellcheck, gitleaks).
- .yamllint.yml — yamllint rules (YAML style and validation).
- Makefile — single entry point: linters, DevSecOps checks, ansible-playbook, bash scripts.
- README.md — project documentation: structure, launch instructions, DevSecOps practices.
- add_ips_to_hosts.sh — collects node IPs and generates
ansible/inventories/<env>/hosts.yaml
. - sync_to_master.sh — syncs directories (
ansible/
,helm/
,charts/
) to the master node. - run_ansible_on_master.sh — runs the Ansible playbook directly from the master node (used by
make deploy
,make bootstrap-argocd
) and applies the Argo CD configuration (Projects, RBAC, Git repositories) from the cloned GitOps repositories.
- ansible/group_vars/prod — production variables: GHCR access, image tags, deployment parameters.
- ansible/group_vars/stage — exact copy of production for testing in an identical environment.
- ansible/group_vars/vault — encrypted secrets with Ansible Vault (tokens/passwords, etc.).
- ansible/inventories/prod — production cluster inventory (master/workers, connections).
- ansible/inventories/stage — stage cluster inventory.
All environment-specific data is placed in group_vars and inventories. Secrets are handled via Ansible Vault.
group_vars/
— environment-specific and shared variables.prod/
ghcr.yaml
— registry/secret parameters, namespace, pull policy.version.yaml
— image tags and release version for production deploy.
stage/
ghcr.yaml
— same as prod, but for stage.version.yaml
— tags/versions for stage.
vault/
vault.yaml
— encrypted secrets (e.g., GHCR_TOKEN).
master.yaml
— common master variables (paths/settings independent of environment).
inventories/
prod/hosts.yaml
— hosts and connections for production.stage/hosts.yaml
— hosts and connections for stage.
roles/
— roles with idempotent logic.- .vault_pass.txt — file containing the Ansible Vault password (for automatic decryption).
- ansible.cfg — global Ansible configuration (roles path, inventory, ssh settings, retries).
- playbook.yaml — main playbook: calls roles (k3s, docker, deploy, etc.) for installation and deployment.
- requirements.yml — list of external Ansible roles/collections to be installed via
ansible-galaxy
.
- argocd — install and bootstrap Argo CD in the cluster.
- argocd-apps — applies Argo CD Applications (
stage
,prod
) from the pre-clonedgitops-apps-health-api
repository. - argocd-config — configuration of Projects, RBAC, Git repositories, and Argo CD controller parameters from the cloned argocd-config-health-api repository.
- common — base environment setup (packages, ssh, system utilities).
- docker — install and configure docker/ctr utilities needed for working with images.
- ghcr — configure access to GitHub Container Registry (secrets, login).
- ingress — setup ingress-nginx controller and dependencies.
- k3s — install and configure k3s server/agents, basic checks.
- kubernetes-tools — utilities for working with the cluster (kubectl, helm, helmfile, etc.).
roles/argocd/templates:
- argocd-cm.yaml.j2 — base Argo CD ConfigMap (disables anonymous access, enables status badges, disables exec).
- argocd-rbac-cm.yaml.j2 — RBAC policies: admin access, readonly as default.
- ingress.yaml.j2 — Ingress without TLS (http), used for quick bootstrap or when cert-manager is not ready.
- ingress-tls.yaml.j2 — Ingress with TLS (https), used with cert-manager and ClusterIssuer.
- deploy — deploy application via helm/helmfile using VERSION.
- distribute_tar_images — distribute .tar images to nodes.
- images_copy_to_agents — copy prepared images from master to workers.
- images_master — prepare and export images on master.
- import_images_on_agents — import previously copied images on workers.
- nginx_fix — fix nginx issues/patches after installation.
The project uses a Makefile
as a single entry point. All main actions — linters, inventory preparation, sync, and deployment — are run through make
.
make help
— list available targets.make lint ENV=stage
— run DevSecOps checks (ansible-lint
,yamllint
,shellcheck
).make add-ips ENV=stage
— fetch IPs from Yandex Cloud and updateansible/inventories/stage/hosts.yaml
.make sync ENV=stage
— sync theansible/
directory and helper files to the master node.make deploy ENV=stage
— connect to the master and runansible-playbook
for installation and deployment.make all ENV=stage
— run the full deployment pipeline:lint → add-ips → sync → deploy
.make bootstrap-argocd ENV=stage
— one-time bootstrap of Argo CD (runs only theargocd
role).
- First run (bootstrap Argo CD on stage):
make lint ENV=stage
make all ENV=stage
make bootstrap-argocd ENV=stage
- Repeat run (any environment):
make lint ENV=<stage|prod>
make all ENV=<stage|prod>
- Step-by-step (example for prod):
make add-ips ENV=prod && \
make sync ENV=prod && \
make deploy ENV=prod
Without
ENV
, targets will not run (protection against accidental deploy).
-
Install local dependencies:
ansible
,ansible-lint
,yamllint
,shellcheck
,pre-commit
,gitleaks
.Argo CD and other components are installed via playbook, no manual installation needed.
-
Configure Ansible Vault (
.vault_pass.txt
with password). -
Add SSH keys to the cloud (Terraform already did this).
-
Deploy to stage:
make all ENV=stage
- (one-time) bootstrap Argo CD:
make bootstrap-argocd ENV=stage
- After validation — deploy to prod:
make all ENV=prod
- lint — code and config checks.
- add-ips — fetch VM IPs and generate inventory.
- sync — sync ansible/ and dependencies to master.
- deploy — run playbook (k3s, ingress, Argo CD, GHCR secrets).
- Base Setup — install utilities, packages, and the Kubernetes Python module.
- Agents — deploy k3s worker nodes and connect them to the master.
- Namespace — create the
health-api
namespace for applications. - Tools — install Helm, Helmfile, and required plugins on the master node.
- Docker — install Docker if needed for auxiliary tasks.
- Ingress — deploy ingress-nginx for external traffic access.
- Argo CD — install Argo CD into the
argocd
namespace. - GHCR Secrets — create
ghcr-secret
in bothargocd
andhealth-api
namespaces for private image access. - Checks — verify that all nodes are in the Ready state.
- Argo CD Config — apply Argo CD configuration (Projects, RBAC, Repos) from the
argocd-config-health-api
repository. - Argo CD Applications — applies
Application
manifests (stage
,prod
) from thegitops-apps-health-api
repository using theargocd-apps
role. - NodePort (optional) — patch ingress service to
NodePort
mode for external access without a load balancer.
The project is organized around a secure stage/prod
pattern with closed worker ports; DevSecOps practices are built in as a mandatory control and validation layer.
Required for checks: ansible-lint, yamllint, shellcheck, pre-commit, gitleaks.
The project supports three authentication modes for Argo CD:
oidc
— direct GitHub OAuth integration (primary)dex
— via the Dex OIDC proxylogin
— basic login/password using bcrypt
Production uses GitHub SSO:
- Direct integration via
oidc.config
- Uses GitHub organization groups:
g:devops
,g:qa
- Security settings (RBAC, AppProjects) are based on these groups
The Argo CD UI shows a "Login with GitHub" button.
The project also supports Dex-based SSO:
- Dex acts as an OIDC proxy between Argo CD and GitHub
- Uses the same GitHub groups:
g:devops
,g:qa
- RBAC and AppProject permissions are configured the same as in
oidc
mode
To generate a bcrypt hash for the UI password:
htpasswd -nbBC 10 admin 'MyStrongPass123' | cut -d: -f2
- admin — username
- MyStrongPass123 — password
Insert the result into group_vars/master.yaml
as admin_password_bcrypt
.
After deployment, access will be:
- username:
admin
- password:
MyStrongPass123
This mode is used for local debugging or isolated environments without external SSO.
- RBAC based on groups is enabled (defined in
argocd/cm/argocd-rbac-cm.yaml
) - In
oidc
anddex
modes, roles are assigned automatically via GitHub login - In
login
mode, access is restricted by IP using Ingress annotations
Argo CD implements centralized access control via an RBAC policy defined in the argocd-config-health-api
repository (argocd/cm/argocd-rbac-cm.yaml
):
admin
role — full access to all applications and projectsstage-admin
role — limited access to thestage
environment only- Role assignment is based on groups (
g:devops
,g:qa
) prod
environment is protected by sync windows that restrict deployment time
The RBAC policy is managed via GitOps and applied automatically from the configuration repository. This eliminates manual errors and ensures change auditability.
- .pre-commit-config.yaml — hooks for running ansible-lint, yamllint, shellcheck, and gitleaks on every commit.
- .ansible-lint — profile and exceptions for strict validation of roles and playbooks.
- .yamllint.yml — YAML style and syntax rules (two environments, vault ignored).
- .gitignore — excludes artifacts (tar-images, retry files, vault password).
- .gitleaks.toml — secret scanning rules, exclusions for encrypted files and binaries.
- Principles for bash scripts — standards: shebang
#!/usr/bin/env bash
,set -Eeuo pipefail
, safe IFS, argument validation,exit 1
on errors. - Makefile — targets
lint
andsecrets-scan
to run DevSecOps checks with one command. - .vault_pass.txt + group_vars/vault/ — secrets encrypted via Ansible Vault, password stored outside Git.
- yamllint — unified style and YAML syntax validation.
- ansible-lint — role errors, best practices. → Secure SDLC principle: early error detection.
- shellcheck — bash script analysis, protection against common mistakes.
- ansible-lint — idempotency check, prohibition of unsafe constructs. → Compliance with OWASP IaC Security and CIS Benchmarks: avoidance of unsafe practices.
-
ansible-lint with production profile: ban on unsafe tasks (
command
withoutcreates
,shell
withoutchanged_when
, etc.). → OWASP Top-10:- A5 Security Misconfiguration,
- A4 Insecure Design.
-
Ansible Vault — storing tokens and passwords only in encrypted form.
-
.vault_pass.txt — password not stored in Git, used only on the server. → OWASP A2 Cryptographic Failures: secret protection. → OWASP A3 Injection: exclusion of secrets from code.
-
Gitleaks — scanning for secrets in code and commits, prevents leaks of access tokens and passwords. → OWASP A2 Cryptographic Failures: prevention of storing secrets in plaintext. → OWASP A3 Injection: protection against accidental hardcoded credentials. → OWASP A5 Security Misconfiguration: no sensitive data in the repository.
- Create the file
ansible/group_vars/vault/vault.yaml
with the registry token:
vault_ghcr_token: ghp_xxxxxxxxxxxxxxxxxxxxxx
- Encrypt it:
ansible-vault encrypt ansible/group_vars/vault/vault.yaml
- Verify the file is encrypted — it now looks like:
$ANSIBLE_VAULT;1.1;AES256
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
- Create
.vault_pass.txt
in the root ofansible/
and put the Vault password there. This file is used when running the playbook so the password is automatically supplied.
- pre-commit — ensures linters run locally and in CI. Runs ansible-lint, yamllint, shellcheck, gitleaks on git commit and in CI, blocking errors and secrets from entering the repository.
- stage/prod separation — testing on identical environments without impacting production. → OWASP A1 Broken Access Control: minimal external entry points. → OWASP A5 Security Misconfiguration: deny by default.
- Key DevSecOps practices implemented for Ansible: linters, SAST, secret scanning, secret management.
- Protection ensured against core OWASP Top-10 categories (Security Misconfiguration, Insecure Design, Cryptographic Failures, Broken Access Control, Secrets Management).
- Configuration is reproducible and secure: no secrets or artifacts end up in Git.
All checks are consolidated into commands:
make lint
make secrets-scan
make lint-security
Detailed mapping of project practices to the OWASP Top-10:
- A1 Broken Access Control → isolated workers, access only through the master node, stage/prod environment pattern, centralized RBAC in Argo CD with role and namespace separation, enforced strictly via GitHub SSO.
- A2 Cryptographic Failures → all secrets are stored in Ansible Vault, bcrypt hash used for
login
mode password,.vault_pass.txt
excluded from Git. - A3 Injection → no secrets in the codebase, enforced linting with ansible-lint, yamllint, and shellcheck.
- A4 Insecure Design → strict stage/prod separation, ansible-lint with production profile, sync window control in production.
- A5 Security Misconfiguration → closed ports, deny-by-default access model, strict RBAC in Argo CD, configuration validation in CI.
- A6 Vulnerable and Outdated Components → partially covered via role checks with ansible-lint, regular system package updates through the
common
role. - A7 Identification and Authentication Failures → centralized authentication via GitHub SSO, role assignment via GitHub groups (
g:devops
,g:qa
), bcrypt used inlogin
mode, GHCR tokens stored securely in Vault. - A8 Software and Data Integrity Failures → pre-commit hooks, image builds and delivery are centralized via the master node.
- A9 Security Logging and Monitoring Failures → partially implemented: centralized logging with Loki and Promtail is planned.
- A10 SSRF → restrictions on unsafe
get_url
usage, source validation in Ansible tasks.