From 3075996ad52f896f521244ce91857a38e59d63d2 Mon Sep 17 00:00:00 2001 From: boxswapper Date: Sun, 13 Sep 2026 13:12:07 -0600 Subject: [PATCH] initial commit --- .codegraph/.gitignore | 5 + .../ses_f63e23ebcffe7Q6sJKCl9W8sFt.json | 10 + LICENSE | 18 ++ README.md | 2 + harden_fedora44.yml | 186 ++++++++++++++++++ inventory.ini | 15 ++ jail.local.j2 | 44 +++++ 7 files changed, 280 insertions(+) create mode 100644 .codegraph/.gitignore create mode 100644 .omo/run-continuation/ses_f63e23ebcffe7Q6sJKCl9W8sFt.json create mode 100644 LICENSE create mode 100644 README.md create mode 100644 harden_fedora44.yml create mode 100644 inventory.ini create mode 100644 jail.local.j2 diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore new file mode 100644 index 0000000..d20c0fe --- /dev/null +++ b/.codegraph/.gitignore @@ -0,0 +1,5 @@ +# CodeGraph data files — local to each machine, not for committing. +# Ignore everything in .codegraph/ except this file itself, so transient +# files (the database, daemon.pid, sockets, logs) never show up in git. +* +!.gitignore diff --git a/.omo/run-continuation/ses_f63e23ebcffe7Q6sJKCl9W8sFt.json b/.omo/run-continuation/ses_f63e23ebcffe7Q6sJKCl9W8sFt.json new file mode 100644 index 0000000..cf3e6d3 --- /dev/null +++ b/.omo/run-continuation/ses_f63e23ebcffe7Q6sJKCl9W8sFt.json @@ -0,0 +1,10 @@ +{ + "sessionID": "ses_f63e23ebcffe7Q6sJKCl9W8sFt", + "updatedAt": "2026-09-13T19:05:07.347Z", + "sources": { + "background-task": { + "state": "idle", + "updatedAt": "2026-09-13T19:05:07.347Z" + } + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..09492f5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) 2026 boxswapper + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8760ef4 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# stoat + diff --git a/harden_fedora44.yml b/harden_fedora44.yml new file mode 100644 index 0000000..60b133c --- /dev/null +++ b/harden_fedora44.yml @@ -0,0 +1,186 @@ +--- +# -------------------------------------------------------------- +# Ansible playbook: Harden a Fedora 44 server & install Fail2Ban +# -------------------------------------------------------------- +# Requirements: +# - Ansible >= 2.14 +# - A reachable Fedora 44 host with SSH access (password or key) +# - `sudo` rights for the control user (via become: true) +# -------------------------------------------------------------- + +- name: Harden Fedora 44 & install Fail2Ban + hosts: all + become: true # elevates to root for package/service ops + vars: + # ---- Tunables ------------------------------------------------- + # Change these to match your environment + fail2ban_port: "22" # default SSH jail; can be "http","https",etc. + fail2ban_maxretry: 3 # how many attempts before ban + fail2ban_bantime: 3600 # seconds (1 h) + # ----------------------------------------------------------------- + # Ensure we are on Fedora 44 (you can relax this if you wish) + fedora_release: "44" + tasks: + # -------------------------------------------------------------- + # Verify OS version (informational, can be removed) + # -------------------------------------------------------------- + - name: Gather os fact + ansible.builtin.debug: + var: ansible_distribution_version + + - name: Ensure we are running Fedora {{ fedora_release }} + ansible.builtin.assert: + that: + - ansible_distribution == "Fedora" + - ansible_distribution_version == fedora_release + fail_msg: "This playbook targets Fedora {{ fedora_release }} only." + success_msg: "Running on the correct Fedora version." + + # -------------------------------------------------------------- + # Update / upgrade all packages + # -------------------------------------------------------------- + - name: Update all packages to latest versions + ansible.builtin.dnf: + name: "*" + state: latest + update_cache: true + + # -------------------------------------------------------------- + # Install essential hardening packages + # -------------------------------------------------------------- + - name: Install fail2ban, firewalld, and sudo + ansible.builtin.dnf: + name: + - fail2ban + - firewalld + - sudo + state: present + + # -------------------------------------------------------------- + # Start & enable firewalld (default-deny all, allow SSH) + # -------------------------------------------------------------- + - name: Start and enable firewalld + ansible.builtin.systemd: + name: firewalld + state: started + enabled: true + + - name: Allow SSH service through firewalld + ansible.builtin.command: + cmd: firewall-cmd --permanent --add-service=ssh + changed_when: false # idempotent check handled by firewall_cmd + + - name: Reload firewalld to apply rules + ansible.builtin.command: + cmd: firewall-cmd --reload + changed_when: false + + # -------------------------------------------------------------- + # Harden SSH (disable root login, enforce key‑only auth) + # -------------------------------------------------------------- + - name: Ensure SSH config directory exists + ansible.builtin.file: + path: /etc/ssh/sshd_config.d + state: directory + mode: "0755" + + - name: Disable root login via SSH + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config.d/00-disable-root.conf + create: true + mode: "0644" + line: "PermitRootLogin no" + state: present + + - name: Enforce password-auth disabled (key-only) + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config.d/00-key-only.conf + create: true + mode: "0644" + line: "PasswordAuthentication no" + state: present + + - name: Require MFA (optional) — add if you use Duo, etc. + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config.d/00-mfa.conf + create: true + mode: "0644" + line: "AuthenticationMethods publickey,keyboard-interactive:pam" + state: present + + - name: Restart sshd to apply changes + ansible.builtin.service: + name: sshd + state: restarted + + # -------------------------------------------------------------- + # Configure Fail2Ban + # -------------------------------------------------------------- + - name: template fail2ban jail.local + ansible.builtin.template: + src: jail.local.j2 + dest: /etc/fail2ban/jail.local + mode: "0644" + notify: Restart fail2ban + + - name: Ensure fail2ban is started & enabled + ansible.builtin.service: + name: fail2ban + state: started + enabled: true + + # -------------------------------------------------------------- + # (Optional) Install & configure unattended‑security updates + # -------------------------------------------------------------- + - name: Install dnf-automatic (security-only) + ansible.builtin.dnf: + name: dnf-automatic + state: present + + - name: Enable security-only automatic updates + ansible.builtin.lineinfile: + path: /etc/dnf/automatic.conf + regexp: "^update_type" + line: "update_type = security" + state: present + + - name: Enable and start dnf-automatic.timer + ansible.builtin.systemd: + name: "@dnf-automatic.timer" + state: started + enabled: true + + # -------------------------------------------------------------- + # Log rotation for security‑relevant logs + # -------------------------------------------------------------- + - name: Ensure rsyslog is running + ansible.builtin.service: + name: rsyslog + state: started + enabled: true + + - name: Add logrotate rule for fail2ban logs + ansible.builtin.lineinfile: + path: /etc/logrotate.d/fail2ban + create: true + mode: "0644" + content: | + /var/log/fail2ban/*.log { + daily + rotate 7 + compress + missingok + notifempty + postrotate + /usr/bin/fail2ban-client reload >/dev/null 2>&1 || true + endscript + } + + # -------------------------------------------------------------- + # 9 Handlers + # -------------------------------------------------------------- + handlers: + - name: Restart fail2ban + ansible.builtin.service: + name: fail2ban + state: restarted \ No newline at end of file diff --git a/inventory.ini b/inventory.ini new file mode 100644 index 0000000..043f84d --- /dev/null +++ b/inventory.ini @@ -0,0 +1,15 @@ +# -------------------------------------------------------------- +# Ansible inventory - servers to harden +# Usage: ansible-playbook -i inventory.ini harden_fedora44.yml +# -------------------------------------------------------------- + +[servers] +stoat-demo ansible_host=stoat-demo ansible_user=YOUR_SSH_USER + +[servers:vars] +# Elevate privileges (the playbook already sets become: true, +# but this allows per-group override if needed) +ansible_become=yes +ansible_become_method=sudo +# Optional: path to your SSH private key +# ansible_ssh_private_key_file=/home/you/.ssh/id_ed25519 \ No newline at end of file diff --git a/jail.local.j2 b/jail.local.j2 new file mode 100644 index 0000000..0259af8 --- /dev/null +++ b/jail.local.j2 @@ -0,0 +1,44 @@ +# -------------------------------------------------------------- +# Fail2Ban jail.local - default settings for Fedora 44 +# Values like {{ fail2ban_port }} come from the playbook vars +# -------------------------------------------------------------- + +[DEFAULT] +# Ban IPs after maxretry failures for bantime seconds +bantime = {{ fail2ban_bantime }} +maxretry = {{ fail2ban_maxretry }} +# Raise ban time incrementally for repeat offenders +bantime.increment = true +bantime.factor = 2 +bantime.maxtime = 24h + +# -------------------------------------------------------------- +# SSH jail - protects against brute-force login attempts +# -------------------------------------------------------------- +[sshd] +enabled = true +port = {{ fail2ban_port }} +filter = sshd +logpath = /var/log/secure +backend = systemd +maxretry = {{ fail2ban_maxretry }} +bantime = {{ fail2ban_bantime }} + +# -------------------------------------------------------------- +# HTTP auth jail - uncomment if you run a web server +# -------------------------------------------------------------- +#[http-auth] +#enabled = true +#port = http,https +#filter = http-auth +#logpath = /var/log/nginx/access.log +#maxretry = {{ fail2ban_maxretry }} +#bantime = {{ fail2ban_bantime }} + +# -------------------------------------------------------------- +# Add more jails below as needed, e.g.: +# [vsftpd] - FTP brute force +# [postfix] - mail smtp auth failures +# [dovecot] - IMAP/POP3 brute force +# Each jail needs: enabled = true, filter, logpath +# -------------------------------------------------------------- \ No newline at end of file