Skip to content

Commit 672b44f

Browse files
committed
Close remaining coverage gaps for lab/prod readiness.
Add lab/prod profiles, Fail2Ban ignoreip guards, exclusive SSH keys, AppArmor/FIDO2 docs, Molecule smoke skeleton, and pre-commit hooks.
1 parent 84b8c59 commit 672b44f

26 files changed

Lines changed: 382 additions & 39 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ jobs:
4444
ansible-playbook --syntax-check playbooks/03-audit.yml
4545
4646
- name: Ansible-lint
47-
run: ansible-lint --nocolor playbooks
47+
run: ansible-lint -c .ansible-lint --nocolor playbooks roles

.pre-commit-config.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
repos:
2+
- repo: https://github.com/pre-commit/pre-commit-hooks
3+
rev: v5.0.0
4+
hooks:
5+
- id: end-of-file-fixer
6+
- id: trailing-whitespace
7+
- id: check-yaml
8+
args: [--allow-multiple-documents]
9+
- id: check-added-large-files
10+
- id: detect-private-key
11+
12+
- repo: https://github.com/adrienverge/yamllint
13+
rev: v1.35.1
14+
hooks:
15+
- id: yamllint
16+
args: [-d, "{extends: relaxed, rules: {line-length: {max: 160}}}"]
17+
files: ^(ansible/.*\.ya?ml|\.github/.*\.ya?ml)$
18+
19+
- repo: https://github.com/ansible/ansible-lint
20+
rev: v25.1.3
21+
hooks:
22+
- id: ansible-lint
23+
files: ^ansible/(playbooks|roles)/
24+
args: [-c, ansible/.ansible-lint]

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ cp inventories/lab/hosts.yml.example inventories/lab/hosts.yml
8484
cp group_vars/all/vars.yml.example group_vars/all/vars.yml
8585
cp group_vars/all/vault.yml.example group_vars/all/vault.yml
8686
# Prefer passworded sudo in production (default). For disposable labs only:
87-
# harden_passwordless_sudo: true
87+
# -e @profiles/lab.yml
88+
# For production overlays (set ignoreip inside the file first):
89+
# -e @profiles/prod.yml
8890
ansible-vault encrypt group_vars/all/vault.yml
8991
# Before harden: set harden_fail2ban_ignoreip to your admin/VPN CIDRs
9092

ansible/.ansible-lint

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
# Soften ansible-lint for a didactic kit; tighten over time.
3+
skip_list:
4+
- yaml[line-length]
5+
- name[casing]
6+
- risky-shell-pipe
7+
- command-instead-of-shell
8+
- no-changed-when
9+
- ignore-errors
10+
- experimental
11+
exclude_paths:
12+
- collections/
13+
- profiles/

ansible/README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,36 @@ ansible-vault encrypt group_vars/all/vault.yml
3333
| `02-harden.yml` | `hosts.yml` (admin + port) | Baseline hardening |
3434
| `03-audit.yml` | `hosts.yml` | Lynis report (forces audit run) |
3535

36-
## Safer defaults
36+
## Profiles (lab vs production)
3737

38-
Passwordless sudo, automatic reboot, PSAD auto-block, ClamAV, AIDE, and chkrootkit are **off** unless you opt in. See `group_vars/all/vars.yml.example`.
38+
```bash
39+
# Disposable lab convenience knobs
40+
ansible-playbook -i inventories/lab/hosts.yml playbooks/02-harden.yml \
41+
--ask-vault-pass --ask-become-pass -e @profiles/lab.yml
42+
43+
# Explicit production overlay (set ignoreip inside profiles/prod.yml first)
44+
ansible-playbook -i inventories/lab/hosts.yml playbooks/02-harden.yml \
45+
--ask-vault-pass --ask-become-pass -e @profiles/prod.yml
46+
```
47+
48+
See `profiles/lab.yml` and `profiles/prod.yml`.
3949

4050
## Tags
4151

4252
`packages`, `ntp`, `sysctl`, `ssh`, `mfa`, `passwords`, `updates`, `firewall`, `ids`, `mail`, `malware`, `integrity`, `chkrootkit`, `aide`, `auditd`, `logwatch`, `lynis`.
4353

44-
## Check mode
54+
## Check mode / Molecule
4555

4656
`--check` is best-effort. Tasks that shell out (moduli trim, aideinit, lynis, psad signature update, test mail) are not fully check-safe.
4757

58+
Optional smoke test (Docker required):
59+
60+
```bash
61+
cd ansible
62+
pip install 'molecule' 'molecule-plugins[docker]'
63+
molecule test
64+
```
65+
4866
## Directory map
4967

5068
```text

ansible/group_vars/all/vars.yml.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ harden_fail2ban_bantime: 1h
4646
harden_fail2ban_maxretry: 5
4747
# Put your static admin/VPN CIDRs here to reduce Fail2Ban lockout risk
4848
harden_fail2ban_ignoreip: ""
49+
# Set true in production overlays to refuse empty ignoreip
50+
harden_fail2ban_require_ignoreip: false
51+
52+
# When true, authorized_keys is replaced by the single configured key (rotation)
53+
harden_ssh_keys_exclusive: false
4954

5055
harden_enable_psad: true
5156
# Auto-block can lock out admins/CI — opt in deliberately
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
- name: Converge — bootstrap identity only (safe smoke path)
3+
hosts: all
4+
become: true
5+
gather_facts: true
6+
vars:
7+
harden_admin_user: molecule
8+
harden_groups:
9+
ssh: sshaccess
10+
sudo: elevated
11+
su: switchroot
12+
harden_passwordless_sudo: true
13+
harden_ssh_public_key_path: "{{ playbook_dir }}/files/molecule.pub"
14+
vault_admin_password: "MoleculeTestPassw0rd!"
15+
pre_tasks:
16+
- name: Install openssh-server for identity tasks
17+
ansible.builtin.apt:
18+
name: openssh-server
19+
state: present
20+
update_cache: true
21+
roles:
22+
- role: admin_user
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOg4nybRVtPvtYid3NPHkvIW1DwiqbMUe9c8w2cgyocf molecule@linux-server-hardening
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
# Minimal Molecule skeleton for Debian converge smoke tests.
3+
# Requires: pip install molecule molecule-plugins[docker] ansible-core
4+
# Run from ansible/: molecule test
5+
dependency:
6+
name: galaxy
7+
options:
8+
requirements-file: requirements.yml
9+
driver:
10+
name: docker
11+
platforms:
12+
- name: debian13-harden
13+
image: geerlingguy/docker-debian13-ansible:latest
14+
pre_build_image: true
15+
command: ""
16+
volumes:
17+
- /sys/fs/cgroup:/sys/fs/cgroup:rw
18+
cgroupns_mode: host
19+
privileged: true
20+
provisioner:
21+
name: ansible
22+
inventory:
23+
hosts:
24+
all:
25+
hosts:
26+
debian13-harden:
27+
ansible_user: root
28+
playbooks:
29+
converge: converge.yml
30+
env:
31+
ANSIBLE_ROLES_PATH: ../roles
32+
verifier:
33+
name: ansible
34+
scenario:
35+
name: default
36+
test_sequence:
37+
- dependency
38+
- syntax
39+
- create
40+
- prepare
41+
- converge
42+
- verify
43+
- destroy
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
# No-op prepare; image already has Python.
3+
- name: Prepare
4+
hosts: all
5+
gather_facts: false
6+
tasks:
7+
- name: Placeholder
8+
ansible.builtin.debug:
9+
msg: prepare ok

0 commit comments

Comments
 (0)