Features: - Multi-distribution support (Debian, Ubuntu, RHEL, AlmaLinux, Rocky, SUSE) - LVM configuration with meaningful volume groups and logical volumes - 8 LVs: lv_opt, lv_tmp, lv_home, lv_var, lv_var_log, lv_var_tmp, lv_var_audit, lv_swap - Security mount options on sensitive directories SSH Hardening: - GSSAPI authentication disabled - GSSAPI cleanup credentials disabled - Root login disabled via SSH - Password authentication disabled - Key-based authentication only - MaxAuthTries: 3, ClientAliveInterval: 300s Security Features: - SELinux enforcing (RHEL family) - AppArmor enabled (Debian family) - Firewall configuration (UFW/firewalld) - Automatic security updates - Audit daemon (auditd) enabled - Time synchronization (chrony) - Essential security packages (aide, auditd) Role Structure: - Modular task organization (validate, install, download, storage, deploy, lvm) - Tag-based execution for selective deployment - OS-family specific cloud-init templates - Comprehensive variable defaults (100+ configurable options) - Post-deployment validation tasks
11 KiB
11 KiB
Ansible Role: deploy_linux_vm
Deploy Linux virtual machines on KVM hypervisors with LVM storage configuration, security hardening, and cloud-init provisioning. This role supports multiple Linux distributions and implements CLAUDE.md security requirements including LVM partitioning and SSH hardening.
Features
- Multi-Distribution Support: Debian, Ubuntu, RHEL, CentOS Stream, Rocky Linux, AlmaLinux, SLES, openSUSE
- LVM Configuration: Automatic LVM setup with meaningful volume groups and logical volumes per CLAUDE.md
- Security Hardening:
- SSH hardening with GSSAPI disabled
- SELinux enforcing (RHEL family)
- AppArmor enabled (Debian family)
- Firewall configuration (UFW/firewalld)
- Automatic security updates
- Audit daemon enabled
- Cloud-Init: Automated provisioning with distribution-specific configurations
- Modular Design: Tag-based execution for selective deployment stages
- Production Ready: Idempotent, well-tested, and CLAUDE.md compliant
Requirements
Hypervisor Requirements
- Ansible 2.12 or higher
- KVM/libvirt virtualization enabled
- Sufficient disk space in
/var/lib/libvirt/images - Network connectivity for cloud image downloads
Supported Distributions (Guest VMs)
| Distribution | Versions | OS Family |
|---|---|---|
| Debian | 11, 12 | debian |
| Ubuntu | 20.04 LTS, 22.04 LTS, 24.04 LTS | debian |
| RHEL | 8, 9 | rhel |
| CentOS Stream | 8, 9 | rhel |
| Rocky Linux | 8, 9 | rhel |
| AlmaLinux | 8, 9 | rhel |
| SLES | 15 | suse |
| openSUSE Leap | 15.5, 15.6 | suse |
Role Variables
Required Variables
| Variable | Required | Default | Description |
|---|---|---|---|
deploy_linux_vm_os_distribution |
Yes | debian-12 | Distribution identifier (e.g., ubuntu-22.04, almalinux-9) |
VM Configuration
| Variable | Default | Description |
|---|---|---|
deploy_linux_vm_name |
linux-guest | VM name in libvirt |
deploy_linux_vm_hostname |
linux-vm | VM hostname |
deploy_linux_vm_domain |
localdomain | Domain name |
deploy_linux_vm_vcpus |
2 | Number of vCPUs |
deploy_linux_vm_memory_mb |
2048 | RAM in MB |
deploy_linux_vm_disk_size_gb |
30 | Primary disk size in GB |
LVM Configuration
| Variable | Default | Description |
|---|---|---|
deploy_linux_vm_use_lvm |
true | Enable LVM configuration |
deploy_linux_vm_lvm_vg_name |
vg_system | Volume group name |
deploy_linux_vm_lvm_pv_device |
/dev/vdb | Physical volume device |
deploy_linux_vm_lvm_volumes |
(see defaults) | List of logical volumes per CLAUDE.md |
LVM Volumes (CLAUDE.md Compliance)
Default logical volumes created:
deploy_linux_vm_lvm_volumes:
- { name: lv_opt, size: 3G, mount: /opt, fstype: ext4 }
- { name: lv_tmp, size: 1G, mount: /tmp, fstype: ext4, mount_options: noexec,nosuid,nodev }
- { name: lv_home, size: 2G, mount: /home, fstype: ext4 }
- { name: lv_var, size: 5G, mount: /var, fstype: ext4 }
- { name: lv_var_log, size: 2G, mount: /var/log, fstype: ext4 }
- { name: lv_var_tmp, size: 5G, mount: /var/tmp, fstype: ext4, mount_options: noexec,nosuid,nodev }
- { name: lv_var_audit, size: 1G, mount: /var/log/audit, fstype: ext4 }
- { name: lv_swap, size: 2G, mount: none, fstype: swap }
SSH Configuration
| Variable | Default | Description |
|---|---|---|
deploy_linux_vm_ssh_permit_root_login |
no | Allow root SSH login |
deploy_linux_vm_ssh_password_authentication |
no | Allow password authentication |
deploy_linux_vm_ssh_gssapi_authentication |
no | GSSAPI disabled per requirements |
deploy_linux_vm_ssh_gssapi_cleanup_credentials |
no | GSSAPI cleanup |
deploy_linux_vm_ssh_max_auth_tries |
3 | Maximum authentication attempts |
deploy_linux_vm_ssh_client_alive_interval |
300 | SSH keepalive interval |
Security Configuration
| Variable | Default | Description |
|---|---|---|
deploy_linux_vm_enable_firewall |
true | Enable firewall (UFW/firewalld) |
deploy_linux_vm_enable_selinux |
true | Enable SELinux (RHEL family) |
deploy_linux_vm_enable_apparmor |
true | Enable AppArmor (Debian family) |
deploy_linux_vm_enable_auditd |
true | Enable audit daemon |
deploy_linux_vm_enable_automatic_updates |
true | Enable automatic security updates |
deploy_linux_vm_automatic_reboot |
false | Auto-reboot after updates |
User Configuration
| Variable | Default | Description |
|---|---|---|
deploy_linux_vm_ansible_user |
ansible | Service account username |
deploy_linux_vm_ansible_user_ssh_key |
(default key) | SSH public key for ansible user |
deploy_linux_vm_root_password |
ChangeMe123! | Root password (console access) |
Dependencies
None. This role is self-contained.
Example Playbook
Basic Deployment
---
- name: Deploy Linux VM
hosts: grokbox
become: yes
roles:
- role: deploy_linux_vm
vars:
deploy_linux_vm_name: "web-server"
deploy_linux_vm_os_distribution: "ubuntu-22.04"
Advanced Deployment with Custom LVM
---
- name: Deploy Database Server with Custom Resources
hosts: grokbox
become: yes
roles:
- role: deploy_linux_vm
vars:
deploy_linux_vm_name: "db-server"
deploy_linux_vm_hostname: "postgres01"
deploy_linux_vm_domain: "production.local"
deploy_linux_vm_os_distribution: "almalinux-9"
deploy_linux_vm_vcpus: 8
deploy_linux_vm_memory_mb: 16384
deploy_linux_vm_disk_size_gb: 100
deploy_linux_vm_use_lvm: true
deploy_linux_vm_lvm_vg_name: "vg_database"
Multi-VM Deployment
---
- name: Deploy Multiple VMs
hosts: grokbox
become: yes
tasks:
- name: Deploy web servers
include_role:
name: deploy_linux_vm
vars:
deploy_linux_vm_name: "{{ item.name }}"
deploy_linux_vm_hostname: "{{ item.hostname }}"
deploy_linux_vm_os_distribution: "{{ item.distro }}"
loop:
- { name: "web01", hostname: "web01", distro: "ubuntu-22.04" }
- { name: "web02", hostname: "web02", distro: "ubuntu-22.04" }
- { name: "db01", hostname: "db01", distro: "almalinux-9" }
Tag-Based Execution
Execute specific deployment stages:
# Pre-flight validation only
ansible-playbook site.yml --tags validate,preflight
# Download cloud images only
ansible-playbook site.yml --tags download,verify
# Deploy VM without LVM configuration
ansible-playbook site.yml --skip-tags lvm
# Configure LVM only (post-deployment)
ansible-playbook site.yml --tags lvm,post-deploy
# Full deployment with all stages
ansible-playbook site.yml
Available Tags
| Tag | Description |
|---|---|
validate, preflight |
Pre-flight validation checks |
install |
Install required packages on hypervisor |
download, verify |
Download and verify cloud images |
storage |
Create VM disk storage |
cloud-init |
Generate cloud-init configuration |
deploy |
Deploy and start VM |
lvm, post-deploy |
Configure LVM on deployed VM |
cleanup |
Remove temporary files |
LVM Configuration Process
The role implements a comprehensive LVM setup:
- Physical Volume Creation: Creates PV on
/dev/vdb(30GB secondary disk) - Volume Group Setup: Creates
vg_systemvolume group - Logical Volume Creation: Creates LVs per CLAUDE.md specifications
- Filesystem Creation: Formats LVs with ext4/swap
- Data Migration: Copies existing data from primary disk to LVM volumes
- Fstab Update: Configures automatic mounting at boot
- Reboot Required: VM must be rebooted to activate new mounts
LVM Post-Deployment
After role execution with LVM enabled:
# SSH to the VM
ssh ansible@<VM_IP>
# Verify LVM configuration
sudo vgs
sudo lvs
sudo pvs
# Check fstab entries
cat /etc/fstab
# Reboot to activate LVM mounts
sudo reboot
# After reboot, verify mounts
df -h
lsblk
SSH Hardening
The role implements comprehensive SSH hardening per requirements:
- GSSAPI Authentication: Disabled (
GSSAPIAuthentication no) - GSSAPI Cleanup: Disabled (
GSSAPICleanupCredentials no) - Root Login: Disabled via SSH (console access available)
- Password Authentication: Disabled (key-based only)
- Connection Limits: Max 3 auth tries, 10 sessions
- Keepalive: 300s interval with 2 max count
- Additional Hardening: Empty passwords rejected, X11 forwarding disabled
Configuration file: /etc/ssh/sshd_config.d/99-security.conf
Security Features
Debian/Ubuntu Systems
- Firewall: UFW enabled with SSH allowed
- AppArmor: Enabled and enforcing
- Automatic Updates:
unattended-upgradesconfigured for security updates only - Audit:
auditdenabled - Time Sync:
chronyconfigured
RHEL/AlmaLinux/Rocky Systems
- Firewall:
firewalldenabled with SSH allowed - SELinux: Enforcing mode enabled
- Automatic Updates:
dnf-automaticconfigured for security updates - Audit:
auditdenabled - Time Sync:
chronydconfigured
Essential Packages (CLAUDE.md)
All VMs include:
- System tools:
vim,htop,tmux,jq,bc - Network tools:
curl,wget,rsync - Development:
git,python3,python3-pip - Security:
aide,auditd,chrony - Storage:
lvm2,parted
Validation
Post-deployment validation includes:
- VM running status check
- IP address assignment verification
- SSH connectivity test
- System information gathering
- LVM configuration verification (if enabled)
Troubleshooting
Cloud-Init Issues
# Check cloud-init status
ssh ansible@<VM_IP> "cloud-init status --wait"
# View cloud-init logs
ssh ansible@<VM_IP> "tail -f /var/log/cloud-init-output.log"
LVM Issues
# Check LVM status on VM
ssh ansible@<VM_IP> "sudo vgs && sudo lvs && sudo pvs"
# Verify fstab
ssh ansible@<VM_IP> "cat /etc/fstab"
# Check disk layout
ssh ansible@<VM_IP> "lsblk"
SSH Connection Issues
# Test SSH with ProxyJump
ssh -J grokbox ansible@<VM_IP>
# Verify SSH configuration
ssh ansible@<VM_IP> "sudo sshd -T | grep -i gssapi"
Firewall Issues
# Debian/Ubuntu
ssh ansible@<VM_IP> "sudo ufw status verbose"
# RHEL/AlmaLinux
ssh ansible@<VM_IP> "sudo firewall-cmd --list-all"
File Locations
On deployed VMs:
- SSH Security Config:
/etc/ssh/sshd_config.d/99-security.conf - Sudoers Config:
/etc/sudoers.d/ansible - Cloud-Init Log:
/var/log/cloud-init-output.log - Fstab:
/etc/fstab(updated with LVM mounts)
On hypervisor:
- Cloud Images:
/var/lib/libvirt/images/*.qcow2 - VM Disks:
/var/lib/libvirt/images/<vm_name>.qcow2 - LVM Disk:
/var/lib/libvirt/images/<vm_name>-lvm.qcow2 - Cloud-Init ISO:
/var/lib/libvirt/images/<vm_name>-cloud-init.iso
License
MIT
Author
Infrastructure Team
Support
- Documentation:
docs/linux-vm-deployment.md - Cheatsheet:
cheatsheets/deploy-linux-vm.md - Guidelines:
CLAUDE.md