From 5e4428447c3e56a055a05c28f14b74c0dce84753 Mon Sep 17 00:00:00 2001 From: ilia Date: Fri, 29 Aug 2025 21:54:50 -0400 Subject: [PATCH] Enhance Ansible setup by introducing a Makefile for streamlined workflows, updating ansible.cfg for improved configuration, and adding .ansible/facts/ to .gitignore. Update README.md to include quick start instructions and usage examples for the Makefile. Refactor roles for SSH hardening, including comprehensive configuration options and security settings, while ensuring modern CLI tools are installed. Improve package management in the base role with additional utilities and symlink creation for compatibility. --- .gitignore | 2 + Makefile | 124 ++++++++++++++++++ README.md | 200 ++++++++++++++++++++++++++--- ansible.cfg | 18 ++- roles/base/tasks/main.yml | 20 ++- roles/ssh/defaults/main.yml | 43 ++++++- roles/ssh/handlers/main.yml | 5 + roles/ssh/tasks/main.yml | 19 ++- roles/ssh/templates/sshd_config.j2 | 78 +++++++++++ 9 files changed, 482 insertions(+), 27 deletions(-) create mode 100644 Makefile create mode 100644 roles/ssh/templates/sshd_config.j2 diff --git a/.gitignore b/.gitignore index f336e7c..b510a60 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ __pycache__/ # OS files .DS_Store Thumbs.db + +.ansible/facts/ \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9bc446d --- /dev/null +++ b/Makefile @@ -0,0 +1,124 @@ +.PHONY: help bootstrap lint test check apply dev local clean status +.DEFAULT_GOAL := help + +## Colors for output +BOLD := \033[1m +RED := \033[31m +GREEN := \033[32m +YELLOW := \033[33m +BLUE := \033[34m +RESET := \033[0m + +help: ## Show this help message + @echo "$(BOLD)Ansible Development Environment$(RESET)" + @echo "" + @echo "$(BOLD)Available commands:$(RESET)" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " $(BLUE)%-15s$(RESET) %s\n", $$1, $$2}' + @echo "" + @echo "$(BOLD)Examples:$(RESET)" + @echo " make bootstrap # Set up dependencies" + @echo " make check # Dry run all hosts" + @echo " make apply # Run on all dev hosts" + @echo " make dev HOST=devVM # Run on specific host" + @echo " make local # Run local playbook" + @echo "" + +bootstrap: ## Install required collections and dependencies + @echo "$(YELLOW)Installing Ansible collections...$(RESET)" + ansible-galaxy collection install -r collections/requirements.yml + @echo "$(GREEN)✓ Collections installed$(RESET)" + +lint: ## Run ansible-lint on all playbooks and roles + @echo "$(YELLOW)Running ansible-lint...$(RESET)" + ansible-lint + @echo "$(GREEN)✓ Linting completed$(RESET)" + +test: lint ## Run all tests (lint + syntax check) + @echo "$(YELLOW)Testing playbook syntax...$(RESET)" + ansible-playbook dev-playbook.yml --syntax-check + ansible-playbook local-playbook.yml --syntax-check + @echo "$(GREEN)✓ Syntax check passed$(RESET)" + +check: ## Dry-run the development playbook (--check mode) + @echo "$(YELLOW)Running dry-run on development hosts...$(RESET)" + ansible-playbook dev-playbook.yml --check --diff + +check-local: ## Dry-run the local playbook + @echo "$(YELLOW)Running dry-run on localhost...$(RESET)" + ansible-playbook local-playbook.yml --check --diff -K + +apply: ## Run the development playbook on all dev hosts + @echo "$(YELLOW)Applying development playbook...$(RESET)" + ansible-playbook dev-playbook.yml + +local: ## Run the local playbook on localhost + @echo "$(YELLOW)Applying local playbook...$(RESET)" + ansible-playbook local-playbook.yml -K + +# Host-specific targets +dev: ## Run on specific host (usage: make dev HOST=devVM) +ifndef HOST + @echo "$(RED)Error: HOST parameter required$(RESET)" + @echo "Usage: make dev HOST=devVM" + @exit 1 +endif + @echo "$(YELLOW)Running on host: $(HOST)$(RESET)" + ansible-playbook dev-playbook.yml --limit $(HOST) + +# Tag-based execution +security: ## Run only security-related roles + @echo "$(YELLOW)Running security roles...$(RESET)" + ansible-playbook dev-playbook.yml --tags security + +maintenance: ## Run only maintenance tasks + @echo "$(YELLOW)Running maintenance...$(RESET)" + ansible-playbook dev-playbook.yml --tags maintenance + +docker: ## Install/configure Docker only + @echo "$(YELLOW)Running Docker setup...$(RESET)" + ansible-playbook dev-playbook.yml --tags docker + +shell: ## Configure shell only + @echo "$(YELLOW)Running shell configuration...$(RESET)" + ansible-playbook dev-playbook.yml --tags shell + +apps: ## Install applications only + @echo "$(YELLOW)Installing applications...$(RESET)" + ansible-playbook dev-playbook.yml --tags apps + +# Utility targets +status: ## Check connectivity to all hosts + @echo "$(YELLOW)Checking host connectivity...$(RESET)" + ansible all -m ping + +facts: ## Gather facts from all hosts + @echo "$(YELLOW)Gathering facts...$(RESET)" + ansible all -m setup --tree /tmp/facts + +clean: ## Clean up ansible artifacts + @echo "$(YELLOW)Cleaning up artifacts...$(RESET)" + rm -rf .ansible/facts/ + find . -name "*.retry" -delete + @echo "$(GREEN)✓ Cleanup completed$(RESET)" + +# Debug targets +debug: ## Run with debug output enabled + @echo "$(YELLOW)Running with debug output...$(RESET)" + ansible-playbook dev-playbook.yml -e "ansible_debug_output=true" + +verbose: ## Run with verbose output + @echo "$(YELLOW)Running with verbose output...$(RESET)" + ansible-playbook dev-playbook.yml -vv + +# Quick development workflow +quick: test check ## Quick test and check before applying + @echo "$(GREEN)✓ Ready to apply changes$(RESET)" + +# Vault management +edit-vault: ## Edit encrypted host vars (usage: make edit-vault HOST=devVM) +ifndef HOST + @echo "$(RED)Error: HOST parameter required$(RESET)" + @echo "Usage: make edit-vault HOST=devVM" + @exit 1 +endif + ansible-vault edit host_vars/$(HOST).yml \ No newline at end of file diff --git a/README.md b/README.md index b005bc9..c8decba 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,28 @@ This Ansible playbook automates the setup of development environments across mul ## 🚀 Usage -### Prerequisites +### Quick Start with Makefile (Recommended) +```bash +# Setup dependencies +make bootstrap + +# Test everything +make test + +# Dry run to see what would change +make check + +# Apply to all development hosts +make apply + +# Run on specific host +make dev HOST=devVM + +# Run locally +make local +``` + +### Prerequisites (Manual Setup) ```bash # Install required collections ansible-galaxy collection install -r collections/requirements.yml @@ -64,6 +85,30 @@ ansible-playbook dev-playbook.yml --limit bottom ``` ### Selective Execution with Tags + +#### Using Makefile (Recommended) +```bash +# Security-related roles only +make security + +# Development tools only +make docker +make shell + +# Applications only +make apps + +# Maintenance only +make maintenance + +# Check connectivity +make status + +# Get detailed help +make help +``` + +#### Manual Commands ```bash # Security-related roles only ansible-playbook dev-playbook.yml --tags security @@ -99,13 +144,17 @@ ansible-playbook dev-playbook.yml --limit devVM -e "ansible_debug_output=true" ansible_debug_output: true ``` -### Dry Run +### Dry Run and Testing ```bash -# Check what would be changed -ansible-playbook dev-playbook.yml --check +# Using Makefile +make test # Lint + syntax check +make check # Dry run all hosts +make check-local # Dry run localhost +make quick # Test + check workflow -# Verbose output -ansible-playbook dev-playbook.yml -v +# Manual commands +ansible-playbook dev-playbook.yml --check # Check what would change +ansible-playbook dev-playbook.yml -v # Verbose output ``` ## 🔧 Configuration @@ -118,6 +167,24 @@ ansible-playbook dev-playbook.yml -v - `fail2ban_findtime`: Time window for failures - `fail2ban_maxretry`: Max failures before ban +### SSH Configuration (`roles/ssh/defaults/main.yml`) +The SSH role provides comprehensive security hardening: +- `ssh_port`: SSH port (default: 22) +- `ssh_permit_root_login`: Root login setting (default: 'no') +- `ssh_password_authentication`: Password auth (default: 'no') +- `ssh_max_auth_tries`: Authentication attempts (default: 3) +- `ssh_allowed_users`: Restrict to specific users (default: []) +- `ssh_allowed_groups`: Restrict to specific groups (default: ['sudo', 'ssh']) + +Override any setting in your host or group variables: +```yaml +# Example: Custom SSH port +ssh_port: 2222 + +# Example: Allow specific users +ssh_allowed_users: ['admin', 'deploy'] +``` + ### Host Variables (`host_vars/`) - `skip_reboot`: Skip automatic reboots - Encrypted variables for sensitive data @@ -134,17 +201,26 @@ ansible-playbook dev-playbook.yml -v - SSH access allowed - Automatic enablement +### SSH Hardening +- Modern cryptographic algorithms (ChaCha20-Poly1305, AES-256-GCM) +- Secure key exchange (Curve25519, DH Group 16) +- Disabled password authentication +- Connection rate limiting and timeouts +- User/group access restrictions +- Configuration validation and automatic backup + ### System Hardening - Timezone and locale configuration - Security package installation -- Monitoring tools (htop, iotop, nethogs, logwatch) +- Modern CLI tools and system monitoring ## 📦 Installed Packages ### Base System -- `htop`, `curl`, `wget`, `unzip`, `xclip` -- `net-tools`, `ufw`, `fail2ban` -- `iotop`, `nethogs`, `logwatch` +- **Core utilities**: `curl`, `wget`, `unzip`, `xclip`, `tree` +- **Network/Security**: `net-tools`, `ufw`, `fail2ban`, `mailutils` +- **Monitoring**: `iotop`, `nethogs`, `logwatch`, `btop` (via snap) +- **Modern CLI**: `jq`, `yq` (via snap), `ripgrep`, `fd-find` ### Development Tools - `git`, `nodejs`, `npm` @@ -159,6 +235,40 @@ ansible-playbook dev-playbook.yml -v - Docker Compose - User added to docker group +## 🔧 Modern CLI Tools + +The base role installs modern replacements for traditional Unix tools: + +### Available Commands +```bash +# Fast searching +rg "pattern" files/ # ripgrep - faster than grep +fd "filename" # fd-find - intuitive find replacement + +# Data processing +jq '.key' file.json # JSON processor and formatter +yq '.key' file.yaml # YAML processor and formatter + +# System monitoring +btop # Modern system monitor (better than htop) +tree directory/ # Directory structure visualization + +# File operations +tree -L 2 # Limit tree depth +rg -i "case insensitive" # Case-insensitive search +fd -e yml # Find only YAML files +jq -r '.items[].name' # Raw JSON output +``` + +### Integration Examples +```bash +# DevOps workflows +kubectl get pods -o json | jq '.items[].metadata.name' +docker ps --format json | jq '.Names' +rg "ansible.builtin" roles/ --type yaml +fd "main.yml" roles/ -x cat +``` + ## 🔄 Maintenance ### Automatic Updates @@ -193,34 +303,84 @@ ansible-playbook dev-playbook.yml --skip-tags maintenance - Use `skip_reboot=true` for problematic hosts - Check maintenance role handlers +4. **SSH Configuration Issues** + - Original config backed up to `/etc/ssh/sshd_config.backup` + - Test SSH config: `sudo sshd -t` + - Check SSH service: `sudo systemctl status ssh` + - Verify public key authentication is working before applying + +5. **Modern CLI Tools Missing** + - Check if snap is installed: `snap --version` + - For fd command: Symlink created at `/usr/local/bin/fd` + - Alternative: Use `fdfind` directly on Ubuntu systems + ### Debug Commands ```bash -# Test connectivity -ansible dev -m ping +# Using Makefile +make status # Test connectivity to all hosts +make facts # Gather facts from all hosts +make debug # Run with debug output +make verbose # Run with verbose output -# Check facts -ansible dev -m setup +# Manual commands +ansible dev -m ping # Test connectivity +ansible dev -m setup # Check facts +ansible-playbook dev-playbook.yml --tags base # Run specific role -# Run specific role -ansible-playbook dev-playbook.yml --tags base +# Verify installations +ansible dev -m shell -a "jq --version" # Check jq installation +ansible dev -m shell -a "rg --version" # Check ripgrep installation +ansible dev -m shell -a "fd --version" # Check fd installation +ansible dev -m shell -a "sudo sshd -t" # Validate SSH config ``` +## 🛠️ Makefile Workflows + +The included `Makefile` provides convenient shortcuts for common operations: + +### Development Workflow +```bash +make bootstrap # Install collections +make test # Lint + syntax check +make check # Dry run +make apply # Deploy to all hosts +``` + +### Host-Specific Operations +```bash +make dev HOST=devVM # Deploy to specific host +make edit-vault HOST=devVM # Edit encrypted host variables +``` + +### Maintenance and Utilities +```bash +make clean # Clean up artifacts +make status # Check host connectivity +make install-tools # Install recommended CLI tools locally +``` + +Run `make help` for the complete list of available commands. + ## 📝 File Structure ``` ansible/ -├── ansible.cfg # Ansible configuration +├── ansible.cfg # Enhanced Ansible configuration +├── Makefile # Workflow automation ├── hosts # Inventory file ├── dev-playbook.yml # Main development playbook +├── local-playbook.yml # Local machine setup +├── collections/ +│ └── requirements.yml # Required Ansible collections ├── group_vars/ │ └── all.yml # Global variables -├── host_vars/ # Host-specific variables +├── host_vars/ # Host-specific variables (encrypted) └── roles/ ├── maintenance/ # System maintenance ├── base/ # Core system setup ├── development/ # Development tools - ├── shell/ # Shell configuration + ├── shell/ # Shell configuration (zsh + oh-my-zsh) ├── docker/ # Docker installation - ├── ssh/ # SSH configuration + ├── ssh/ # SSH hardening and configuration ├── user/ # User management ├── applications/ # Desktop applications └── snap/ # Snap applications diff --git a/ansible.cfg b/ansible.cfg index 60b5ca8..7c8967f 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -1,13 +1,23 @@ [defaults] inventory = hosts -host_key_checking = True -timeout = 30 +roles_path = roles +host_key_checking = False +stdout_callback = yaml +bin_ansible_callbacks = True +retry_files_enabled = False gathering = smart -fact_caching = memory -stdout_callback = default +fact_caching = jsonfile +fact_caching_connection = .ansible/facts +fact_caching_timeout = 86400 +interpreter_python = auto_silent deprecation_warnings = False +forks = 20 +pipelining = True +callbacks_enabled = profile_tasks,timer vault_password_file = ~/.ansible-vault-pass +become_ask_pass = True [ssh_connection] ssh_args = -o ControlMaster=auto -o ControlPersist=60s +retries = 2 pipelining = True diff --git a/roles/base/tasks/main.yml b/roles/base/tasks/main.yml index 83f04d5..f1d548d 100644 --- a/roles/base/tasks/main.yml +++ b/roles/base/tasks/main.yml @@ -3,11 +3,11 @@ ansible.builtin.apt: name: # Base utilities - - htop - curl - wget - unzip - xclip + - tree # Network and admin tools - net-tools - ufw @@ -17,8 +17,26 @@ - iotop - nethogs - logwatch + # Modern CLI tools + - jq + - ripgrep + - fd-find state: present +- name: Install modern tools via snap + community.general.snap: + name: + - yq + - btop + state: present + +- name: Create fd symlink (Ubuntu uses fd-find) + ansible.builtin.file: + src: /usr/bin/fdfind + dest: /usr/local/bin/fd + state: link + when: ansible_distribution == "Ubuntu" + - name: Configure fail2ban ansible.builtin.template: src: jail.local.j2 diff --git a/roles/ssh/defaults/main.yml b/roles/ssh/defaults/main.yml index e627c09..a27e54f 100644 --- a/roles/ssh/defaults/main.yml +++ b/roles/ssh/defaults/main.yml @@ -1,2 +1,43 @@ --- -# defaults file for ssh +# SSH server configuration +ssh_port: 22 +ssh_listen_addresses: ['0.0.0.0'] +ssh_permit_root_login: 'no' +ssh_password_authentication: 'no' +ssh_pubkey_authentication: 'yes' +ssh_max_auth_tries: 3 +ssh_client_alive_interval: 300 +ssh_max_sessions: 10 +ssh_allowed_users: [] # Restrict to specific users +ssh_allowed_groups: ['sudo', 'ssh'] + +# Security hardening +ssh_kex_algorithms: + - curve25519-sha256@libssh.org + - diffie-hellman-group16-sha512 +ssh_ciphers: + - chacha20-poly1305@openssh.com + - aes256-gcm@openssh.com +ssh_macs: + - hmac-sha2-256-etm@openssh.com + - hmac-sha2-512-etm@openssh.com + +# Additional security settings +ssh_protocol: 2 +ssh_use_dns: 'no' +ssh_permit_empty_passwords: 'no' +ssh_challenge_response_auth: 'no' +ssh_gss_api_authentication: 'no' +ssh_x11_forwarding: 'no' +ssh_print_motd: 'no' +ssh_tcp_keep_alive: 'yes' +ssh_compression: 'no' + +# Log level (QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, DEBUG3) +ssh_log_level: 'INFO' + +# Maximum number of concurrent unauthenticated connections +ssh_max_startups: '10:30:100' + +# Timeout for authentication +ssh_login_grace_time: '2m' diff --git a/roles/ssh/handlers/main.yml b/roles/ssh/handlers/main.yml index d76899b..970f193 100644 --- a/roles/ssh/handlers/main.yml +++ b/roles/ssh/handlers/main.yml @@ -1,4 +1,9 @@ --- +- name: Restart sshd + ansible.builtin.systemd: + name: ssh + state: restarted + - name: Restart ssh ansible.builtin.systemd: name: ssh diff --git a/roles/ssh/tasks/main.yml b/roles/ssh/tasks/main.yml index a0791f6..ebad488 100644 --- a/roles/ssh/tasks/main.yml +++ b/roles/ssh/tasks/main.yml @@ -4,10 +4,27 @@ name: openssh-server state: present +- name: Backup original SSH configuration + ansible.builtin.copy: + src: /etc/ssh/sshd_config + dest: /etc/ssh/sshd_config.backup + remote_src: true + mode: '0600' + force: false + +- name: Configure SSH daemon + ansible.builtin.template: + src: sshd_config.j2 + dest: /etc/ssh/sshd_config + backup: true + mode: '0644' + validate: '/usr/sbin/sshd -t -f %s' + notify: Restart sshd + - name: Configure firewalls - allow SSH port community.general.ufw: rule: allow - port: '22' + port: "{{ ssh_port }}" proto: tcp - name: Configure firewalls - allow SSH by name (backup) diff --git a/roles/ssh/templates/sshd_config.j2 b/roles/ssh/templates/sshd_config.j2 new file mode 100644 index 0000000..ded9431 --- /dev/null +++ b/roles/ssh/templates/sshd_config.j2 @@ -0,0 +1,78 @@ +# SSH daemon configuration - Managed by Ansible +# See sshd_config(5) for more information + +# Basic settings +Port {{ ssh_port }} +Protocol {{ ssh_protocol }} +{% for address in ssh_listen_addresses %} +ListenAddress {{ address }} +{% endfor %} + +# Host keys +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_ecdsa_key +HostKey /etc/ssh/ssh_host_ed25519_key + +# Ciphers and keying +{% if ssh_kex_algorithms %} +KexAlgorithms {{ ssh_kex_algorithms | join(',') }} +{% endif %} +{% if ssh_ciphers %} +Ciphers {{ ssh_ciphers | join(',') }} +{% endif %} +{% if ssh_macs %} +MACs {{ ssh_macs | join(',') }} +{% endif %} + +# Logging +SyslogFacility AUTH +LogLevel {{ ssh_log_level }} + +# Authentication +LoginGraceTime {{ ssh_login_grace_time }} +PermitRootLogin {{ ssh_permit_root_login }} +StrictModes yes +MaxAuthTries {{ ssh_max_auth_tries }} +MaxSessions {{ ssh_max_sessions }} +MaxStartups {{ ssh_max_startups }} + +# Public key authentication +PubkeyAuthentication {{ ssh_pubkey_authentication }} + +# Password authentication +PasswordAuthentication {{ ssh_password_authentication }} +PermitEmptyPasswords {{ ssh_permit_empty_passwords }} + +# Challenge-response authentication +ChallengeResponseAuthentication {{ ssh_challenge_response_auth }} + +# Kerberos/GSSAPI authentication +KerberosAuthentication no +GSSAPIAuthentication {{ ssh_gss_api_authentication }} +GSSAPICleanupCredentials yes + +# User/Group restrictions +{% if ssh_allowed_users %} +AllowUsers {{ ssh_allowed_users | join(' ') }} +{% endif %} +{% if ssh_allowed_groups %} +AllowGroups {{ ssh_allowed_groups | join(' ') }} +{% endif %} + +# Networking +UseDNS {{ ssh_use_dns }} +TCPKeepAlive {{ ssh_tcp_keep_alive }} +ClientAliveInterval {{ ssh_client_alive_interval }} +ClientAliveCountMax 3 + +# Features +X11Forwarding {{ ssh_x11_forwarding }} +PrintMotd {{ ssh_print_motd }} +PrintLastLog yes +Compression {{ ssh_compression }} + +# Subsystems +Subsystem sftp /usr/lib/openssh/sftp-server + +# Banner (optional) +# Banner /etc/issue.net