courses

Lab — Week 2: OS Hardening and Secrets Management
Due: 17 July 2026
Submission: GitLab repo secdevops-s26-<CECS>

Overview

Reduce the attack surface of the freshly provisioned Ubuntu server — removing what isn’t needed, locking down what remains — then replace plaintext credentials with a proper secrets manager.

Prerequisites


Part 1: OS Hardening

A freshly installed Ubuntu 26.04 server is configured for convenience, not security. This part reduces the attack surface of ubuntu-server by removing unnecessary services, hardening SSH, auditing file permissions, and deploying fail2ban. Every change you make here should be traceable to a threat in your threat model.

SSH Hardening

  1. Edit /etc/ssh/sshd_config on ubuntu-server to implement at minimum:
    • PasswordAuthentication no
    • PermitRootLogin no
    • AllowUsers dmcgrath
    • MaxAuthTries 3
    • LoginGraceTime 30
    • X11Forwarding no
  2. Test the configuration before reloading: sudo sshd -t

  3. Reload sshd: sudo systemctl reload sshd

  4. Verify from Kali that password authentication is rejected:
    ssh -o PreferredAuthentications=password dmcgrath@10.10.10.20
    # Must fail with: Permission denied (publickey)
    

Service Minimization

  1. List all running services: systemctl list-units --type=service --state=running

  2. Identify and disable at least three services that are not needed for the course workload (nginx, MariaDB, Docker are needed — everything else is a candidate). Document each with: service name, why it was disabled, and what attack surface it removes.

  3. Mask any services that should never start:

    sudo systemctl mask <service>
    

Audit Script

  1. Write scripts/harden-audit.sh on ubuntu-server (commit the script to your repo). The script must check all of the following and print PASS or FAIL for each:
    • PermitRootLogin is no
    • PasswordAuthentication is no
    • No world-writable files exist in /etc
    • No unexpected SUID binaries exist in /tmp or /var/tmp
    • The dmcgrath account has a valid SSH key in ~/.ssh/authorized_keys
  2. Run the script and fix all FAIL results. Re-run to confirm all checks pass.

fail2ban

  1. Configure /etc/fail2ban/jail.local to protect SSH with: maxretry = 3, findtime = 600, bantime = 3600. Use the nftables-multiport banaction.

  2. Enable and start fail2ban: sudo systemctl enable --now fail2ban

  3. From Kali, trigger the ban by attempting five failed SSH logins. Confirm the Kali IP appears in fail2ban’s ban list: sudo fail2ban-client status sshd

Threat Model Update

  1. In lab02/threat-model.md, mark any threats that are now mitigated. Update their status from OPEN to MITIGATED and add the control applied.

Deliverables

Create lab03/ in your repo containing:

All findings and changes must be documented in lab03/lab03.md with a brief explanation of why each change was made, referencing specific threat model entries.


Part 2: Secrets Management with OpenBao

Hardcoded credentials are one of the most common and consequential security failures. This part replaces plaintext credentials on ubuntu-server with an OpenBao instance, demonstrates the policy model that enforces least-privilege access to secrets, and verifies that no credentials exist in plaintext anywhere on the system.

OpenBao is the open-source (Linux Foundation) fork of HashiCorp Vault; its CLI is bao and its API is Vault-compatible.

OpenBao Setup

  1. Create /etc/openbao/openbao.hcl with a file storage backend listening on 127.0.0.1:8200 (TLS disabled for lab use):
    ui = true
    storage "file" { path = "/opt/openbao/data" }
    listener "tcp" { address = "127.0.0.1:8200"; tls_disable = true }
    api_addr = "http://127.0.0.1:8200"
    
  2. Create storage directory and set ownership:
    sudo mkdir -p /opt/openbao/data
    sudo chown openbao:openbao /opt/openbao/data
    
  3. Enable and start OpenBao: sudo systemctl enable --now openbao

  4. Initialize OpenBao with 5 key shares and a threshold of 3:
    export BAO_ADDR='http://127.0.0.1:8200'
    bao operator init -key-shares=5 -key-threshold=3
    

    Save the unseal keys and root token somewhere secure (a local file is fine for the lab).

  5. Unseal OpenBao using 3 of the 5 keys: bao operator unseal <key>

  6. Enable the audit log: bao audit enable file file_path=/var/log/openbao/audit.log

Secrets and Policies

  1. Enable the KV-v2 secrets engine: bao secrets enable -path=secret kv-v2

  2. Store the DVWA database credentials:
    bao kv put secret/dvwa/db username=dvwauser password=$(openssl rand -base64 16)
    
  3. Write a read-only policy file policy/dvwa-read.hcl that allows read on secret/data/dvwa/* and list on secret/metadata/dvwa/*.

  4. Load the policy and create a 24-hour service token scoped to it:
    bao policy write dvwa-read policy/dvwa-read.hcl
    bao token create -policy=dvwa-read -ttl=24h -display-name=dvwa-app
    
  5. Verify the token can read but not write:
    BAO_TOKEN=<service-token> bao kv get secret/dvwa/db       # must succeed
    BAO_TOKEN=<service-token> bao kv put secret/dvwa/db x=y   # must fail
    

Python Integration

  1. Write scripts/get-secret.py that reads the password field from secret/dvwa/db using the hvac library and prints it. The script must read BAO_ADDR and BAO_TOKEN from environment variables — no hardcoded values.

  2. Run it: BAO_TOKEN=<service-token> python3 scripts/get-secret.py

Verify No Plaintext Credentials

  1. Search the system for plaintext credentials:
    sudo grep -rn --include="*.conf" --include="*.yml" --include="*.yaml" \
        -E '(password|passwd|secret)\s*[:=]\s*\S+' /etc /opt 2>/dev/null \
        | grep -v '^\s*#'
    

    Any result that contains a real credential (not a template placeholder) must be remediated.

Deliverables

Create lab04/ in your repo containing:


Submission

Commit all files for both parts — lab03/ and lab04/ — and push to your GitLab repo. Do not commit the root OpenBao token, unseal keys, or any actual secret values. The lab is considered submitted when the commit appears in GitLab before the due date.