{"id":28762826,"url":"https://github.com/yogesht22/project-1-ansible-server","last_synced_at":"2026-05-08T04:37:25.206Z","repository":{"id":299176128,"uuid":"998994410","full_name":"YogeshT22/project-1-ansible-server","owner":"YogeshT22","description":"This project demonstrates a complete Infrastructure as Code (IaC) workflow for automatically provisioning a bare Ubuntu server into a fully functional web server. It utilizes Ansible to perform configuration management, ensuring the process is repeatable, reliable, and idempotent.","archived":false,"fork":false,"pushed_at":"2025-06-15T05:18:33.000Z","size":11,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-15T06:24:53.834Z","etag":null,"topics":["ansible","ansible-role","docker","iac","idempotency","wsl2"],"latest_commit_sha":null,"homepage":"","language":"Jinja","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/YogeshT22.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-09T15:17:23.000Z","updated_at":"2025-06-15T05:18:36.000Z","dependencies_parsed_at":"2025-06-15T06:35:35.515Z","dependency_job_id":null,"html_url":"https://github.com/YogeshT22/project-1-ansible-server","commit_stats":null,"previous_names":["yogesht22/project-1-ansible-server"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/YogeshT22/project-1-ansible-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YogeshT22%2Fproject-1-ansible-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YogeshT22%2Fproject-1-ansible-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YogeshT22%2Fproject-1-ansible-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YogeshT22%2Fproject-1-ansible-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/YogeshT22","download_url":"https://codeload.github.com/YogeshT22/project-1-ansible-server/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YogeshT22%2Fproject-1-ansible-server/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260326516,"owners_count":22992368,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","ansible-role","docker","iac","idempotency","wsl2"],"created_at":"2025-06-17T09:00:25.494Z","updated_at":"2026-05-08T04:37:25.200Z","avatar_url":"https://github.com/YogeshT22.png","language":"Jinja","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Automated \u0026 Secure Server Provisioning with Ansible\n\n![CI](https://github.com/YogeshT22/project-1-ansible-server/actions/workflows/ci.yml/badge.svg)\n\n![Ansible](https://img.shields.io/badge/ansible-2.17-blue) ![Docker](https://img.shields.io/badge/docker-compose-lightblue) ![Molecule](https://img.shields.io/badge/tested%20with-molecule-brightgreen) ![License: MIT](https://img.shields.io/badge/license-MIT-green)\n\nA production-style Infrastructure as Code project that automatically provisions a bare Ubuntu server into a fully configured, hardened web server using **Ansible**. The entire setup — packages, Nginx config, firewall rules, kernel hardening, secrets, and automated tests — is defined in version-controlled YAML.\n\n![Logo](assets/logo.png)\n\n---\n\n## What This Project Does\n\nRunning a single command:\n\n```bash\nansible-playbook playbook.yml\n```\n\nTakes a fresh Ubuntu server and:\n\n1. **Hardens the OS** — applies 10 sysctl kernel parameters (ASLR, SYN-flood protection, ICMP spoofing prevention), installs `auditd` for system-call auditing, deploys a login warning banner, locks down sensitive file permissions\n2. **Installs and configures Nginx** — deploys a hardened `nginx.conf` with 6 OWASP security headers, TLS 1.2/1.3 only, `server_tokens off`, gzip tuning, and hidden-file blocking\n3. **Configures the firewall** — UFW with deny-by-default policy, only ports 22/80/443 open\n4. **Installs fail2ban** — brute-force SSH/HTTP protection\n5. **Schedules daily security updates** — unattended `apt dist-upgrade` via cron at 02:00\n\n---\n\n## Skills \u0026 Concepts Demonstrated\n\n| Area                         | What's in this project                                                                     |\n| ---------------------------- | ------------------------------------------------------------------------------------------ |\n| **Configuration Management** | Ansible 2.17, two roles (`hardening` + `webserver`), idempotent tasks throughout           |\n| **Security Hardening**       | SSH key-only auth, explicit `sshd_config`, UFW, fail2ban, sysctl, auditd, OWASP headers    |\n| **Secrets Management**       | Ansible Vault (AES-256), `vault_` indirection pattern, `vault.yml.example` template        |\n| **Automated Testing**        | Molecule + Docker driver, `prepare → converge → verify`, 10 assertions                     |\n| **CI/CD**                    | GitHub Actions: `yamllint → ansible-lint → molecule` pipeline, free on public repos        |\n| **IaC Best Practices**       | Role `defaults` vs `vars`, variable priority, tags, handlers (`reload` not `restart`)      |\n| **Template Engine**          | Jinja2 templates for `nginx.conf` and `index.html`, variables injected at render time      |\n| **Docker**                   | Multi-layer Dockerfile, health checks, `restart: unless-stopped`, `docker exec` connection |\n\n---\n\n## Project Structure\n\n```text\n.\n├── ansible.cfg                  ← Pipelining, ControlMaster, diff, YAML output\n├── playbook.yml                 ← Entry point: hardening role → webserver role\n├── inventory.ini                ← Target hosts (Docker container on port 2222)\n├── requirements.yml             ← Galaxy collections: ansible.posix, community.general\n├── docker-compose.yml           ← Target server container (port 2222→22, 8080→80)\n├── Dockerfile.server            ← Ubuntu + sshd + ansible user + your public key\n├── sshd_config                  ← Hardened SSH config (PermitRootLogin no, key-only)\n├── .ansible-lint                ← profile: production\n├── .github/workflows/ci.yml     ← yamllint → ansible-lint → molecule\n├── group_vars/\n│   ├── webservers.yml           ← Non-secret group vars\n│   ├── vault.yml                ← Encrypted secrets (gitignored)\n│   └── vault.yml.example        ← Committed template showing vault structure\n└── roles/\n    ├── hardening/               ← OS hardening (sysctl, auditd, MOTD, file perms)\n    │   ├── defaults/main.yml    ← sysctl params dict, MOTD banner text\n    │   ├── tasks/main.yml       ← auditd, sysctl loop, banner, disable services, perms\n    │   ├── templates/motd.j2    ← Login warning banner\n    │   └── meta/main.yml        ← Galaxy metadata\n    └── webserver/               ← Application layer\n        ├── defaults/main.yml    ← server_name, ports, worker_connections, cron schedule\n        ├── vars/main.yml        ← Internal package list (high-priority)\n        ├── tasks/main.yml       ← apt, nginx, UFW, fail2ban, cron (all tagged)\n        ├── handlers/main.yml    ← Reload Nginx (SIGHUP — zero downtime)\n        ├── templates/\n        │   ├── nginx.conf.j2    ← Hardened Nginx: security headers, TLS, gzip\n        │   └── index.html.j2    ← Served web page\n        ├── meta/main.yml        ← Galaxy metadata\n        └── molecule/default/    ← Automated tests\n            ├── molecule.yml     ← Docker driver, roles_path, skip-tags\n            ├── prepare.yml      ← Bootstrap python3+sudo into container\n            ├── converge.yml     ← Apply webserver role\n            └── verify.yml       ← 10 assertions (Nginx, headers, packages, cron)\n```\n\n---\n\n## Prerequisites\n\n- **WSL2** on Windows with Ubuntu\n- **Docker Desktop** with WSL2 integration enabled\n\nRun once inside WSL2:\n\n```bash\n# Upgrade Ansible (Ubuntu apt ships an outdated 2.10 — this project needs 2.12+)\npip3 install --upgrade ansible\necho 'export PATH=$HOME/.local/bin:$PATH' \u003e\u003e ~/.bashrc\nsource ~/.bashrc\n\n# Install required Galaxy collections\nansible-galaxy collection install -r requirements.yml\n\n# Install Molecule for testing\npip3 install molecule molecule-plugins[docker]\n```\n\n---\n\n## Quick Start\n\n### 1 — Generate your SSH key (first time only)\n\n```bash\nssh-keygen -t rsa -b 4096\ncp ~/.ssh/id_rsa.pub .\n```\n\n### 2 — Start the target server\n\n```bash\ndocker-compose up -d --build\n```\n\nVerify it's healthy:\n\n```bash\ndocker ps   # STATUS should show \"healthy\" after ~15 seconds\n```\n\n### 3 — Set up Ansible Vault (optional for local dev)\n\nThe playbook works out of the box with a plain `webserver_admin_email` in `group_vars/webservers.yml`.\nTo use encrypted secrets instead:\n\n```bash\ncp group_vars/vault.yml.example group_vars/vault.yml\n# Edit vault.yml with your real values, then encrypt it:\nansible-vault encrypt group_vars/vault.yml\n# Add --ask-vault-pass to every ansible-playbook command\n```\n\n### 4 — Fix WSL2 world-writable warning (one-time setup)\n\n\u003e **Why this is needed:** WSL2 mounts Windows drives (`/mnt/d/`) as world-writable (`chmod 777`).\n\u003e Ansible refuses to load `ansible.cfg` from world-writable directories as a security measure.\n\u003e You'll see this warning if not fixed:\n\u003e\n\u003e ```\n\u003e [WARNING]: Ansible is being run in a world writable directory, ignoring it as an ansible.cfg source.\n\u003e [WARNING]: No inventory was parsed, only implicit localhost is available.\n\u003e ```\n\nAdd these lines to `~/.bashrc` once — then it just works:\n\n```bash\ncat \u003e\u003e ~/.bashrc \u003c\u003c 'EOF'\nexport ANSIBLE_PROJECT=\"/mnt/path/to/your/project\"\nalias ansible-run='ANSIBLE_CONFIG=\"$ANSIBLE_PROJECT/ansible.cfg\" ansible-playbook \"$ANSIBLE_PROJECT/playbook.yml\"'\nEOF\nsource ~/.bashrc\n```\n\nVerify it worked:\n\n```bash\nansible --version | grep \"config file\"\n# Should show the project path, not \"None\"\n```\n\n### 5 — Run the playbook\n\n```bash\n# Full run — from anywhere, no cd needed\nansible-run\n\n# With extra flags\nansible-run --check --diff\nansible-run --tags hardening\nansible-run --tags nginx\n\n# Or the traditional way (must be inside the project dir):\nansible-playbook playbook.yml\n\n# Dry-run — see exactly what would change without touching the server\nansible-playbook playbook.yml --check --diff\n\n# Run only a specific layer\nansible-playbook playbook.yml --tags hardening\nansible-playbook playbook.yml --tags nginx\nansible-playbook playbook.yml --tags security\n```\n\n### 6 — Verify in browser\n\nOpen **http://localhost:8080** — you should see the Ansible-configured welcome page.\n\n### 7 — Run automated tests (Molecule)\n\n```bash\ncd roles/webserver\nmolecule test\n```\n\nOr step by step:\n\n```bash\nmolecule converge   # apply the role to a fresh container\nmolecule verify     # run the 10 assertions\nmolecule destroy    # tear down the test container\n```\n\n### 8 — Clean up\n\n```bash\ndocker-compose down\n```\n\n---\n\n## Available Tags\n\nRun any subset of the playbook with `--tags`:\n\n| Tag         | What it runs                    |\n| ----------- | ------------------------------- |\n| `hardening` | Entire hardening role           |\n| `auditd`    | Install + enable auditd only    |\n| `sysctl`    | Kernel parameter hardening only |\n| `banner`    | MOTD + issue.net banner only    |\n| `webserver` | Entire webserver role           |\n| `packages`  | APT install tasks only          |\n| `nginx`     | Nginx config + service tasks    |\n| `security`  | UFW + fail2ban + cron tasks     |\n| `firewall`  | UFW rules only                  |\n| `fail2ban`  | fail2ban service only           |\n| `updates`   | Security update cron job only   |\n\n---\n\n## Troubleshooting\n\n### ❌ ansible.cfg ignored / \"no hosts matched\"\n\n```\n[WARNING]: Ansible is being run in a world writable directory, ignoring it as an ansible.cfg source.\n[WARNING]: No inventory was parsed, only implicit localhost is available.\n```\n\nWSL2 mounts Windows drives as `chmod 777`. See **Step 4** in Quick Start above — the `ansible-run` alias fixes this permanently.\n\n---\n\n### ❌ Port 2222 bind error on Windows\n\n```\nError: An attempt was made to access a socket in a way forbidden by its access permissions\n```\n\nOpen **PowerShell as Administrator**:\n\n```powershell\nnet stop winnat\n# then:\nnet start winnat\n```\n\nThen re-run `docker-compose up -d`.\n\n---\n\n### ❌ SSH Permission denied (publickey)\n\nYour container was built with a different key than the one in `~/.ssh/id_rsa.pub`.\n\n```bash\ncp ~/.ssh/id_rsa.pub .\ndocker-compose down\ndocker-compose up -d --build\n```\n\nManually test SSH to confirm:\n\n```bash\nssh -i ~/.ssh/id_rsa ansible@127.0.0.1 -p 2222\n```\n\n---\n\n### ❌ `ufw` or `ansible.posix.sysctl` module not found\n\nUbuntu's `apt` ships Ansible 2.10 (from 2021). This project requires 2.12+:\n\n```bash\npip3 install --upgrade ansible\nansible-galaxy collection install -r requirements.yml\n```\n\n---\n\n### ❌ Molecule: \"Failed to create temporary directory\"\n\nConfirm you're inside `roles/webserver/` before running Molecule:\n\n```bash\ncd roles/webserver \u0026\u0026 molecule test\n```\n\n---\n\n### ❌ Molecule: WARNING 1 missing files\n\n```\nWARNING  Molecule executed 1 scenario (1 missing files)\n```\n\nHarmless — refers to the optional `cleanup.yml` not being present. The scenario still passed.\n\n---\n\n### ❌ Clean reset (when everything is broken)\n\n```bash\ndocker-compose down -v\ndocker system prune -af\ndocker-compose up -d --build\n```\n\n---\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyogesht22%2Fproject-1-ansible-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyogesht22%2Fproject-1-ansible-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyogesht22%2Fproject-1-ansible-server/lists"}