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