{"id":48294192,"url":"https://github.com/davidwhittington/linux-security","last_synced_at":"2026-04-04T23:28:48.696Z","repository":{"id":343106807,"uuid":"1172947527","full_name":"davidwhittington/linux-security","owner":"davidwhittington","description":"Production-ready hardening toolkit for Ubuntu/Debian VPS servers — firewall, SSH, fail2ban, Apache security headers, automated updates, and log monitoring.","archived":false,"fork":false,"pushed_at":"2026-03-19T03:10:37.000Z","size":309,"stargazers_count":1,"open_issues_count":36,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-19T16:54:03.857Z","etag":null,"topics":["bash","debian","fail2ban","hardening","linux","security","ubuntu","ufw","vps"],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidwhittington.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/security/README.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-04T21:22:47.000Z","updated_at":"2026-03-19T03:10:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/davidwhittington/linux-security","commit_stats":null,"previous_names":["davidwhittington/vps-security","davidwhittington/linux-security"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/davidwhittington/linux-security","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwhittington%2Flinux-security","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwhittington%2Flinux-security/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwhittington%2Flinux-security/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwhittington%2Flinux-security/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidwhittington","download_url":"https://codeload.github.com/davidwhittington/linux-security/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwhittington%2Flinux-security/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31418982,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","debian","fail2ban","hardening","linux","security","ubuntu","ufw","vps"],"created_at":"2026-04-04T23:28:48.061Z","updated_at":"2026-04-04T23:28:48.680Z","avatar_url":"https://github.com/davidwhittington.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# linux-security\n\nA layered hardening toolkit for Ubuntu/Debian servers. Two profiles: `baseline` (any server) and `web-server` (Apache/PHP/MySQL addendum). One config file, one command.\n\nEach script is idempotent, validates preconditions before making changes, supports `--dry-run`, and backs up any files it modifies.\n\n**Tested on:** Ubuntu 24.04 LTS (Noble Numbat) · Apache 2.4\n\n---\n\n## Background\n\nThis toolkit came out of necessity. Managing a growing number of projects across multiple VPS providers made it clear that ad-hoc server setup wasn't sustainable. Every new deployment meant repeating the same hardening steps from memory, with inconsistent results and no audit trail.\n\nThe goal was a standardized, repeatable baseline: cookie-cutter deployments that stand up a secure server quickly, while staying flexible enough to handle the custom, one-off requirements that come with running a variety of distinct projects. Each server has its own quirks. Different domain configurations, monitoring needs, access patterns. The toolkit is structured to handle both the common baseline and those edge cases without diverging into a tangle of server-specific scripts.\n\n---\n\n## Status\n\n| Component | Status |\n|---|---|\n| Core hardening scripts (01–08) | Complete |\n| Web hardening scripts (01–10) | Complete |\n| `config.env` / `config.web.env` configuration | Complete |\n| `bootstrap.sh` with `--profile` support | Complete |\n| `scripts/audit/audit.sh` baseline checker | Complete |\n| `scripts/core/audit/` extended audit tools | Complete |\n| `scripts/web/audit/` web audit tools | Complete |\n| Nginx support | Planned (Phase 2) |\n| Multi-server fleet tooling | Planned (Phase 2) |\n\nSee the [open issues](https://github.com/davidwhittington/linux-security/issues) for the full phased roadmap.\n\n---\n\n## What It Hardens\n\n| Area | Coverage |\n|---|---|\n| **Firewall** | UFW — deny all inbound, allow SSH / 80 / 443 |\n| **SSH** | Key-only auth, no root password login, no X11 forwarding |\n| **Intrusion Prevention** | fail2ban with SSH + Apache jails, 3 strikes / 1 hour ban; recidive jail for repeat offenders |\n| **Kernel Network** | ICMP redirect blocking, martian packet logging |\n| **Rootkit Detection** | rkhunter with scheduled scans |\n| **Syscall Auditing** | auditd with baseline ruleset |\n| **Filesystem Integrity** | AIDE — baseline snapshot + scheduled diff |\n| **Apache** | `ServerTokens Prod`, `ServerSignature Off`, security headers, HSTS, CSP, block `.git`/`.svn`, disable `mod_status` (web-server profile) |\n| **TLS** | Modern cipher suite, HSTS preload, cert expiry monitoring (web-server profile) |\n| **Admin User** | Non-root sudo user with SSH key access |\n| **Automatic Updates** | `unattended-upgrades` + monthly full upgrade with email report |\n| **Log Monitoring** | Logwatch daily digest + GoAccess traffic reports, password-protected (web-server profile) |\n| **Malware Scanning** | ClamAV with scheduled scans (web-server profile) |\n| **WAF** | ModSecurity with OWASP Core Rule Set (web-server profile) |\n\n---\n\n## Prerequisites\n\n- Ubuntu 22.04+ or Debian 12+\n- Root access\n- **SSH public key already in `/root/.ssh/authorized_keys`** before running — script `core/01` disables password authentication and aborts if no key is present\n\nFor the **web-server profile**, Apache 2.4 must be installed and running. PHP and MySQL/MariaDB hardening scripts are optional and will skip cleanly if those services are not present.\n\n---\n\n## Quick Start\n\n**1. Clone and configure**\n\n```bash\ngit clone https://github.com/davidwhittington/linux-security.git\ncd linux-security\ncp config.env config.env.local   # or edit config.env directly\n```\n\nEdit `config.env` and set your values: admin username, email address, SMTP relay, and SSH port.\n\nFor the web-server profile, also fill in `config.web.env` (CSP domains, cert warn threshold, web roots path).\n\nSee [docs/customization.md](docs/customization.md) for details on every variable.\n\n**2. Run**\n\nOption A — single command (recommended):\n\n```bash\n# As root on the target server\nbash bootstrap.sh --profile baseline     # core controls only (any server)\nbash bootstrap.sh --profile web-server   # core + Apache/PHP/MySQL hardening\nbash bootstrap.sh --dry-run              # preview all changes first\nbash bootstrap.sh --profile baseline --dry-run\nbash bootstrap.sh --confirm              # skip the AGREE prompt (automation/CI)\n```\n\nOption B — run scripts individually in order:\n\n```bash\n# Core layer\nbash scripts/core/hardening/01-immediate-hardening.sh   # Firewall · SSH · fail2ban · sysctl\nbash scripts/core/hardening/02-setup-admin-user.sh      # Non-root admin with sudo + SSH keys\nbash scripts/core/hardening/03-monthly-updates-setup.sh # Scheduled apt upgrades + email report\n\n# Web layer (web-server profile only)\nbash scripts/web/hardening/01-apache-hardening.sh       # Apache headers · TLS · mod_status\nbash scripts/web/hardening/02-log-monitoring-setup.sh   # Logwatch + GoAccess traffic reports\nbash scripts/web/hardening/03-cert-monitor-setup.sh     # Cert expiry monitoring\n```\n\n**3. Verify**\n\n```bash\nbash scripts/audit/audit.sh                      # web-server checks (default)\nbash scripts/audit/audit.sh --profile baseline   # core checks only\n```\n\n\u003e **After running `core/01`:** Open a second terminal and verify SSH access before closing your current session. Password authentication will be disabled.\n\n---\n\n## Scripts\n\n### Core Layer\n\nAvailable in both profiles. No Apache or web-server dependency.\n\n---\n\n#### `core/01-immediate-hardening.sh` — Critical Fixes\n\nAddresses the highest-risk issues found on most freshly provisioned servers.\n\n- Installs and configures **fail2ban** with SSH jail (3 strikes, 1h ban) and Apache jails\n- Enables **UFW** with deny-all inbound policy; opens SSH port, 80, 443\n- Disables **SSH password authentication** and root password login\n- Disables **X11 forwarding**\n- Hardens **kernel sysctl**: disables ICMP redirects, enables martian logging\n\nSafe to re-run. Aborts if no SSH authorized key is found.\n\n---\n\n#### `core/02-setup-admin-user.sh` — Admin User\n\nPromotes an existing user to sudo admin and removes the cloud-init NOPASSWD sudoers rule.\n\n- Sets login shell to `/bin/bash`\n- Adds user to `sudo` group\n- Copies root's `authorized_keys` so SSH access works immediately\n- Removes `/etc/sudoers.d/90-cloud-init-users`\n\nAdmin username is set from `$ADMIN_USER` in `config.env`. The user must exist before running.\n\nAfter verifying SSH access and `sudo -v` work, set `PermitRootLogin no` in `/etc/ssh/sshd_config`.\n\n---\n\n#### `core/03-monthly-updates-setup.sh` — Scheduled Updates\n\nSets up a monthly full system update with an emailed report.\n\n- Installs and configures `msmtp` with your SMTP relay settings\n- Creates `/usr/local/sbin/monthly-apt-report.sh` — runs `apt upgrade`, checks kernel version, disk usage, uptime, fail2ban status, and cert expiry\n- Schedules a cron job at 3:00 AM on the 1st of each month\n\nEmail address and SMTP settings read from `config.env`.\n\n---\n\n#### `core/04–08` — Defense in Depth\n\nAdditional hardening applied after the baseline is established:\n\n| Script | Installs / Configures |\n|---|---|\n| `04-rkhunter-setup.sh` | rkhunter rootkit scanner with scheduled scans and email alerts |\n| `05-auditd-setup.sh` | auditd syscall auditing with a baseline ruleset |\n| `06-fail2ban-recidive.sh` | fail2ban recidive jail — escalating bans for repeat offenders |\n| `07-aide-setup.sh` | AIDE filesystem integrity baseline and nightly diff |\n| `08-disk-alert-setup.sh` | Disk usage cron — emails when any partition crosses threshold |\n\n---\n\n### Web Layer\n\nApplied only with `--profile web-server`. Requires Apache 2.4 running.\n\n---\n\n#### `web/01-apache-hardening.sh` — Apache Security\n\nReduces information disclosure and adds browser security headers.\n\n- Enables `mod_headers`\n- Sets `ServerTokens Prod` and `ServerSignature Off`\n- Disables TRACE method\n- Blocks access to `.git` and `.svn` directories\n- Adds security headers: `X-Content-Type-Options`, `Referrer-Policy`, `Permissions-Policy`, HSTS, CSP\n- Disables `mod_status`\n- Backs up existing `security.conf` before overwriting; restores on failure\n\nCSP `frame-ancestors` is set from `$CSP_FRAME_ANCESTORS` in `config.web.env`.\n\n---\n\n#### `web/02-log-monitoring-setup.sh` — Log Monitoring\n\nInstalls daily log digest and traffic reporting.\n\n- Installs **Logwatch** — configures a daily HTML email digest of all services\n- Installs **GoAccess** — generates a daily HTML traffic report from Apache access logs\n- Password-protects the reports directory with HTTP Basic Auth\n- Schedules GoAccess at 4:00 AM daily\n\nReports are served from `/var/www/html/reports/`.\n\n---\n\n#### `web/03-cert-monitor-setup.sh` — Certificate Monitoring\n\nConfigures automated TLS cert expiry alerts.\n\n- Installs a daily cron that checks cert expiry via `certbot certificates`\n- Emails a warning when any cert is within `$CERT_WARN_DAYS` days of expiry (default: 30)\n\n---\n\n#### `web/04–10` — Extended Web Hardening\n\n| Script | Installs / Configures |\n|---|---|\n| `04-clamav-setup.sh` | ClamAV with scheduled scans of web roots |\n| `05-modsecurity-setup.sh` | ModSecurity WAF with OWASP Core Rule Set |\n| `06-vhost-hardener.sh` | Per-vhost security headers and directory restrictions |\n| `07-apache-tls-hardening.sh` | Modern cipher suite, HSTS preload, OCSP stapling |\n| `08-apache-dos-mitigation.sh` | mod_evasive and mod_reqtimeout tuning |\n| `09-php-hardening.sh` | PHP ini hardening (skips cleanly if PHP not installed) |\n| `10-mysql-hardening.sh` | MySQL/MariaDB secure defaults (skips cleanly if not installed) |\n\n---\n\n## Repository Structure\n\n```\nlinux-security/\n├── config.env                   # Core configuration — fill in before running\n├── config.web.env               # Web-layer configuration — needed for web-server profile\n├── bootstrap.sh                 # Single-command provisioner\n├── profiles/\n│   ├── baseline.conf            # Core-only script list\n│   └── web-server.conf          # Core + web script list\n├── docs/\n│   ├── security/\n│   │   └── README.md            # Security baseline, requirements, audit cadence\n│   ├── architecture.md          # How the toolkit fits together\n│   ├── customization.md         # config.env and config.web.env variables explained\n│   ├── TEMPLATE.md              # Blank audit report template\n│   └── VPS_HARDENING_GUIDE.html # Standalone HTML knowledge base (offline reference)\n├── scripts/\n│   ├── core/\n│   │   ├── hardening/           # Core hardening scripts (01–08)\n│   │   └── audit/               # Core read-only checkers\n│   ├── web/\n│   │   ├── hardening/           # Web hardening scripts (01–10)\n│   │   └── audit/               # Web read-only checkers\n│   └── audit/\n│       └── audit.sh             # Profile-aware baseline checker\n├── lib/                         # Shared shell libraries\n├── logs/                        # Per-run bootstrap logs (gitignored)\n├── config/                      # Config snippets and templates (planned)\n└── private/                     # Git submodule — server-specific data (not public)\n```\n\n---\n\n## Auditing a Server\n\nRun the built-in checker after hardening to verify every control is active:\n\n```bash\nbash scripts/audit/audit.sh                      # web-server checks (default)\nbash scripts/audit/audit.sh --profile baseline   # core checks only\nbash scripts/audit/audit.sh --json               # machine-readable output\nbash scripts/audit/audit.sh --report html        # full HTML report\n```\n\nFor a full manual audit, use the template:\n\n1. Copy `docs/TEMPLATE.md` to your private repo as `private/servers/\u003chostname\u003e/AUDIT_REPORT.md`\n2. Work through each finding category against your server\n3. Use the checklist at the bottom to track remediation progress\n\nSee [docs/security/README.md](docs/security/README.md) for the full security baseline.\n\n---\n\n## Using This as a Template\n\nThis repo is structured to keep generic, reusable scripts public and server-specific data private. The `private/` directory is a separate private git submodule holding actual audit reports, inventory, and network data.\n\nTo adopt this pattern for your own infrastructure:\n\n```bash\n# Fork or clone this repo\ngh repo fork davidwhittington/linux-security\n\n# Create your own private companion repo\ngh repo create my-linux-private --private\n\n# Add it as a submodule\ngit submodule add https://github.com/\u003cyou\u003e/my-linux-private private/\ngit commit -m \"Add private submodule\"\n```\n\n---\n\n## Docs\n\n- [Customization Guide](docs/customization.md) — config.env and config.web.env variables explained\n- [Architecture](docs/architecture.md) — how the toolkit fits together\n- [Security Baseline](docs/security/README.md) — requirements, headers, audit cadence\n- [SSH Two-Factor Authentication](docs/ssh-2fa.md) — optional TOTP setup guide\n- [Glossary](docs/GLOSSARY.md) — terminology reference for security terms used throughout\n- [Audit Report Template](docs/TEMPLATE.md) — blank template for documenting findings\n- [VPS Hardening Guide](docs/VPS_HARDENING_GUIDE.html) — standalone offline reference\n- [Changelog](CHANGELOG.md) — version history\n\n---\n\n## License\n\nMIT — use freely, adapt for your own infrastructure.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidwhittington%2Flinux-security","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidwhittington%2Flinux-security","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidwhittington%2Flinux-security/lists"}