This project demonstrates the transition from manual system administration to Infrastructure as Code (IaC). While the 42 born2broot subject traditionally requires a manual installation, this repository automates the entire lifecycle—from raw ISO to a hardened, LVM-partitioned, and audited "Golden Image."
| Component | Technology | Purpose |
|---|---|---|
| Orchestrator | Packer | Automates VM creation and OS installation via Preseed. |
| Provisioner | Ansible | Handles security hardening and configuration management. |
| Virtualizer | VirtualBox | The target environment for the exported VM artifact. |
| OS | Debian 13 (Trixie) | The minimal, stable base for the server. |
The build process follows a coordinated "Bake" strategy to ensure a repeatable and immutable environment:
- The Seed (Preseed): Packer boots the Debian ISO and serves a
preseed.cfgvia an HTTP server. This automates the partitioning (LVM), user creation, and base package installation. - The Hardening (Ansible): Once the OS reboots, Packer connects via SSH and hands over control to Ansible. Ansible applies the hardening roles (UFW, Sudoers, Password Policies).
- The Artifact: Packer executes a graceful shutdown and exports the VM as an
.ovfand.vdipackage.
The Ansible hardening role implements the following strict security requirements:
- Default Policy: Deny all incoming traffic.
- Restricted Access: Only Port 4242 is permitted for SSH.
- Build Strategy: Port 22 was temporarily permitted to allow the Ansible handshake, then programmatically closed in the final cleanup to reduce the attack surface.
- TTY Enforcement:
requirettyis enabled to prevent non-interactive shell exploits. - Exemptions: A specific exception was created for the
packeruser (!requiretty) to allow the automation tool to perform a graceful shutdown. - Logging: All administrative actions are logged to
/var/log/sudo/sudo_configfor forensic auditing. - Custom Messaging: Failed password attempts trigger a custom "Wrong password!" message.
- Complexity: Enforces a 10-character minimum, requiring uppercase, lowercase, and digits.
- History: Prevents password reuse (last 7 passwords).
To comply with the project's partitioning requirements, an Expert Recipe was defined in the preseed configuration to create the following Logical Volume structure:
| Partition | Size | Type | Mount Point |
|---|---|---|---|
| sda1 | 512MB | ext4 (Primary) | /boot |
| lv_root | ~35GB | LVM Logical Volume | / (root) |
| lv_swap | 2GB | LVM Logical Volume | swap |
To generate the identical "Golden Image" from source:
- Clone the repository:
git clone [https://github.com/kevshouse/born2broot-devops.git](https://github.com/kevshouse/born2broot-devops.git) cd born2broot-devops - Initialise Plugins:
packer init packer/debian.pkr.hcl
- Build the Image:
ssh packer@localhost -p 4242
A primary challenge was migrating the SSH port from 22 to 4242 mid-build. Changing the port and restarting the service immediately would sever the Packer connection and cause a build failure.
Implemented a "Stealth Migration" where Ansible updated the sshd_config but deferred the service restart. This allowed Packer to finish its tasks on port 22. Upon the first "production" boot, the system automatically initializes SSH on the required port 4242.
Enabling requiretty in the sudoers file for security purposes initially blocked Packer from executing the shutdown_command.
Integrated a specific Ansible task to inject a !requiretty default for the packer service user. This maintains high security for human users while allowing the automation pipeline to terminate gracefully.
After the build completes and the .ovf artifact is imported into VirtualBox, the following manual configurations are required to ensure the VM is accessible and adheres to the final security state.
By default, the VM uses a NAT network. To allow SSH access from the host machine to the guest, a Port Forwarding rule must be manually established:
| Setting | Value |
|---|---|
| Protocol | TCP |
| Host IP | 127.0.0.1 |
| Host Port | 4242 |
| Guest IP | (Leave Blank) |
| Guest Port | 4242 |
Steps in VirtualBox GUI:
- Select the VM > Settings > Network.
- Under Adapter 1, ensure it is attached to NAT.
- Click Advanced > Port Forwarding.
- Add a new rule using the values in the table above.
During the build process, the SSH service was configured to listen on port 4242, but the service was not restarted to maintain the automation tunnel. On the first "live" boot, the configuration must be refreshed.
- Log in to the VM console.
- Restart the SSH service:
sudo systemctl restart ssh
- Verify the service is listening on the correct port:
ss -tuln | grep 4242
To achieve the final hardened state required for the Born2bRoot evaluation, the temporary "automation bridge" on port 22 must be closed. This ensures that port 4242 is the only entry point for remote administration.
Execute the following inside the VM:
# Verify current rules
sudo ufw status numbered
# Delete the temporary rule for port 22
# (Replace '1' with the actual rule number if different)
sudo ufw delete 1
# Confirm final status
sudo ufw status verboseFor optimal performance in a live environment, verify the following hardware settings in the VirtualBox Manager before starting the VM:
| Category | Setting | Recommendation |
|---|---|---|
| System | Processor | Ensure at least 2 CPUs are allocated for smooth background auditing. |
| System | Motherboard | Enable PAE/NX to support modern kernel security features. |
| Display | Screen | Set Video Memory to at least 16MB to prevent console flickering. |
Before considering the deployment complete and ready for evaluation, run through this final audit:
- SSH Access:
ssh packer@localhost -p 4242connects successfully without a timeout. - Firewall Integrity:
sudo ufw statusshows4242/tcp ALLOWand specifically that port 22 has been removed. - Audit Trail:
sudo ls /var/log/sudocontains thesudo_configfile, proving administrative logging is active. - LVM Architecture:
lsblkconfirms the Logical Volume partitions (root and swap) are mounted and sized correctly. - Password Strength: Attempting to change a password to something simple (e.g., "123") is rejected by
libpam-pwquality.
Project status: Hardened & Verified. 🚀