Files
infra-automation/roles/deploy_linux_vm/README.md
Infrastructure Team eec15a1cc2 Add deploy_linux_vm role with LVM and SSH hardening
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
2025-11-10 22:51:51 +01:00

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:

  1. Physical Volume Creation: Creates PV on /dev/vdb (30GB secondary disk)
  2. Volume Group Setup: Creates vg_system volume group
  3. Logical Volume Creation: Creates LVs per CLAUDE.md specifications
  4. Filesystem Creation: Formats LVs with ext4/swap
  5. Data Migration: Copies existing data from primary disk to LVM volumes
  6. Fstab Update: Configures automatic mounting at boot
  7. 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-upgrades configured for security updates only
  • Audit: auditd enabled
  • Time Sync: chrony configured

RHEL/AlmaLinux/Rocky Systems

  • Firewall: firewalld enabled with SSH allowed
  • SELinux: Enforcing mode enabled
  • Automatic Updates: dnf-automatic configured for security updates
  • Audit: auditd enabled
  • Time Sync: chronyd configured

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