Security Audit: Scan and Harden Your Linux Servers
Run 25 predefined security audits on a connected Linux server over SSH. Get a hardening score, a PDF report, and fix commands that wait for your approval.
What you can do in the Audit tab
- Run any of 25 predefined audits, grouped into Server, Database, Web Servers, and Docker. Filter the catalog by category or search it by name.
- Check what an audit looks at, how long it takes, and whether it needs sudo, before you run it.
- Pick exactly which checks run. Every check is a checkbox and all of them start ticked.
- Watch the run live, with the log streaming and real-time progress.
- Read a hardening score out of 100, a findings-by-severity breakdown, and a pass, warn, fail, and skip tally.
- Sort and filter the findings table, and see the reason a check could not answer instead of having it silently dropped.
- Download any run as a PDF, and reopen or download past runs from the Reports tab.
- Hand selected findings to the AI Terminal as a ready-made prompt, then approve each fix command yourself.
- Run the same audit across many servers at once from the workspace-level Audit Reports screen.
Watch it run
The whole flow in under three minutes - pick an audit, scope the checks, read the report, and fix a finding. Every step is written out below too, so you can skip straight to running your first audit.
The Audit tab checks how a server is configured, from the inside, over an authenticated SSH session. If you want a quick look at a server from the outside with nothing to install and no account, the free VPS Security Scanner probes open ports, TLS, and HTTP headers from the public internet. They answer different questions and are worth running together.
Why an audit tab instead of a manual checklist
| Doing it by hand | Doing it in CtrlOps |
|---|---|
ssh user@host, then remember which questions to ask | Click the server, click Audit, pick from the catalog |
grep -E 'PermitRootLogin|PasswordAuthentication' /etc/ssh/sshd_config | Part of the SSH & Access audit |
ufw status verbose, then check iptables because the box uses that instead | Firewall & Network detects which one is in use |
ss -tulpn | grep LISTEN and read the output yourself | Open Ports and External Listeners, each with a verdict |
find / -perm -4000 -type f 2>/dev/null | SUID/SGID Files |
openssl x509 -enddate -noout -in /etc/letsencrypt/live/*/cert.pem | SSL Certificate Expiry, plus whether renewal is automated |
curl -sI https://example.com | grep -i strict-transport, six times for six headers | The Security Headers audit, all six at once |
| Decide for yourself whether each answer is bad | Every check returns a status and a severity |
| Write the findings up by hand afterwards | A report with a score, a severity split, and a PDF |
| Repeat all of it on the next server | Run on N servers from Audit Reports |
Open the Audit tab
Connect to your server
Open CtrlOps and click the server you want to audit. Wait for the header to show Connected.
Click Audit in the sidebar
Audit sits at the bottom of the left sidebar, under Logs. It only appears once a connection is established, because the audits run over that session.
Read the catalog
The catalog opens on all 25 audits. The pills across the top filter it to Server, Database, Web Servers, or Docker, and the search box filters by name. The toggle at the right switches between grid and list.
Each card tells you what you need before you commit to a run: the audit name, a one-line description of what it looks at, how many checks it contains, roughly how long it takes, and a sudo required badge when full coverage needs elevated access. The i button opens the detail for that audit.
Every audit and the checks inside it are listed in the audit catalog further down, if you would rather read the whole set before you run anything.
Run your first audit
Filter to the category you care about
If you are auditing a plain application server, Server is the place to start, and SSH & Access is the audit most likely to find something. A box running Nginx and MySQL is worth auditing in all three relevant categories rather than one.
Click Generate
The green Generate button on the card starts the flow. Nothing has run on the server yet.
Choose which checks to include
A picker opens listing every check in that audit, all of them ticked. Untick anything that does not apply to this host, and use the search box if the list is long. The footer counts what is selected, and Clear all empties the list if you would rather tick a handful.
This step is the answer to "I do not want it poking at my database". Nothing outside your selection runs.
Click Continue
CtrlOps runs the selected checks over your SSH session. The log streams live and progress updates as each check resolves, so a slow check is visible rather than a frozen screen. When the run finishes, the report opens.
Audits are read-only. Every check inspects configuration, file permissions, and process state. None of them change anything on the server, so running one on production is safe.
Read the report
The report header carries the audit name, a status pill, and a line identifying the run: server name, address, date, time, and audit version. That line is what makes a downloaded PDF meaningful three months later.
Findings by severity
A donut and a count for Critical, High, Medium, and Low. This is the triage view: it answers "how bad is it" before you read a single finding.
Hardening score
A score out of 100 with a plain-language label, plus a note about the run. On a first scan it says a baseline was saved for trend, so the next run on the same server can be compared against it.
Under the score sits the coverage note. If checks were skipped because they needed elevated access, the card says so in amber with the percentage that actually ran. See Coverage and sudo below.
Checks run
The tally: how many checks passed, how many raised warnings, how many failed, and how many were skipped. Skipped is not a failure and it is not a pass, which is why it gets its own number rather than being folded into either.
Findings and recommendations
A table with four columns:
| Column | What it holds |
|---|---|
| Type | The check that produced the finding |
| Finding | The check number, its name, and the recommendation in a sentence |
| Status | PASSED, WARNING, FAILED, or SKIPPED |
| Severity | CRITICAL, HIGH, MEDIUM, or LOW |
Status and Severity are both sortable and filterable, so you can pull every failed high-severity finding to the top and work down from there.
The recommendation is written to be actionable rather than diagnostic. A real one reads:
SSL Certificate Expiry - SKIPPED - HIGH No
/etc/letsencrypt/livedirectory - if TLS terminates elsewhere, check that platform
That is the shape of every finding: what was looked at, what was found, and what to do about it.
Statuses and severities
Every check returns exactly one status and one severity.
| Status | Meaning |
|---|---|
| PASSED | The check ran and the configuration is what it should be. |
| WARNING | The check ran and found something worth changing, but it is not immediately dangerous on its own. |
| FAILED | The check ran and found a real problem. |
| SKIPPED | The check could not answer, and the message says why. Usually the software is not installed, or reading the file needs root. |
| Severity | Meaning |
|---|---|
| CRITICAL | Exploitable now, or exposes the whole host. |
| HIGH | A serious weakness that materially widens your attack surface. |
| MEDIUM | Worth fixing, and usually cheap to fix. |
| LOW | Hygiene and defence in depth. |
Severity describes how much the check matters, not how bad this particular server is. A SKIPPED finding can still carry HIGH severity: the check is important and it could not be answered, which is exactly the pair you want to know about.
Coverage and sudo
Audits run as the SSH user you connected with, and a large share of the checks read files that only root can open, such as mongod.conf, redis.conf, database sockets, and certificate directories. Rather than guessing or silently dropping those checks, CtrlOps reports them as skipped with the real reason:
MySQL not accessible - server down, or root/sudo needed for socket authRoot or sudo required to read mongod.confRoot or sudo required to scan backup pathsNeed root to read certificates
The score card then shows the coverage percentage for the run, so a report built from half the checks is never presented as a clean bill of health.
Connect as a user with sudo, or as root, before you run an audit you intend to act on or send to someone. A run at low coverage is still useful for a first look, but it is not the report you want in an email.
The sudo required badge on a card tells you in advance which audits are affected. Database and Docker audits are the ones that lean on it hardest.
Fix a finding with AI
This is the part that separates the Audit tab from a scanner. A scanner ends at a list of problems; here the list is the input to the fix.
Select the findings you want to fix
Tick the checkbox at the start of each row in the findings table. Start with a single finding so you can see the whole loop before you batch them.
Click Fix selected findings
The bar above the summary cards becomes the action. Clicking it builds a prompt from the findings you selected and copies it to your clipboard. Nothing has been sent anywhere and nothing has run on the server.
Paste it into the AI Terminal
Open the AI Terminal on the same server, paste the prompt into the assistant, and press Enter. It reads the findings and works out what needs to change.
Read the command, then run it
Each command arrives in an approval card marked Awaiting approval, with the command itself and a Why this command explanation of what it does and why the assistant chose it. Read it. Then click Run to execute it, or Cancel to skip it and move on.
Nothing is remediated automatically, ever. The assistant proposes and explains; you approve. A command you cancel leaves the server exactly as it was. This is the same approval boundary that governs every command in the AI Terminal.
When you have worked through the findings, run the same audit again. The new run is scored against the same checks, so the score moving is a real measurement rather than a claim, and the first run is kept as the baseline to compare against.
Download or revisit a report
Open the report
Either use the report that opened when the audit finished, or click Reports at the top of the Audit tab. The badge on that button counts how many saved runs that server has.
Click the download icon
The download button sits at the top right of the report. It writes the whole report to your machine as a PDF: the score, the severity split, and every finding with its status and message.
Check the header before you send it
The header line carries the server name, address, date, time, and audit version, so whoever opens the PDF knows exactly which host was scanned, when, and with which version of the checks.
Run an audit on many servers
The per-server Audit tab audits the server you are connected to. To audit several at once, use the workspace-level version instead.
Open Audit Reports from the Home screen
Go to the Home tab and click Audit Reports under Tools in the left sidebar. The catalog is identical.
Click Generate and pick your checks
Same flow as a single server: choose the audit, tick the checks to include, and continue.
Select the servers
The picker lists every saved server with a checkbox and a search box for name or address. Tick the hosts you want and click Run on N servers. Each server is audited separately and gets its own report under Reports.
Running one audit across a fleet is what makes the results comparable. The same checks with the same thresholds on every host means a difference in the score is a difference in the servers, not a difference in how carefully somebody looked.
The audit catalog
The full reference: all 25 audits and every named check inside them, so you can work out which ones apply to a given server without opening the app. Approximate times are for a single server on a normal connection.
Checks for software that is not installed report as skipped, so an audit aimed at the wrong server is harmless, just not useful. Pick by what the box actually runs.
Server
| Audit | Approx | Checks it runs |
|---|---|---|
| SSH & Access | ~14s | SSH Root Login, SSH Password Auth, SSH Port, SSH Idle Timeout, Authorized Keys, UID 0 Accounts, Sudo Group Members, Sudo NOPASSWD |
| Firewall & Network | ~14s | Firewall State, Firewall Rules Audit, Docker Port Exposure, Open Ports, Exposed Database Ports, IPv6 Firewall Coverage, Outbound Policy |
| Services & Processes | ~11s | Unneeded Services, Running Services, External Listeners, Cron Jobs, Systemd Timers |
| File System | ~10s | World-Writable Files, SUID/SGID Files, /tmp Permissions, Log File Permissions |
| Application Security | ~10s | SSL Certificate Expiry, Certificate Auto-Renewal, Application Versions, Database Access, Resource Isolation |
| Logging & Monitoring | ~9s | Fail2ban, Log Rotation, Failed SSH Logins |
| System Updates | ~8s | Pending Updates, Unattended Upgrades, Kernel Version, Reboot Required |
Database
Covers MySQL and MariaDB, PostgreSQL, MongoDB, and Redis.
| Audit | Approx | Checks it runs |
|---|---|---|
| Authentication | ~14s | MySQL Root Password, MySQL Anonymous Users, MySQL Passwordless Accounts, MySQL Remote Root, PostgreSQL Auth Methods, PostgreSQL Password Hashing, MongoDB Authorization, Redis Authentication |
| Network Isolation | ~12s | Database Engines, MySQL Network Binding, PostgreSQL Network Binding, MongoDB Network Binding, Redis Network Binding, Exposed Database Ports, Web DB Admin Tools |
| Least-Privilege Permissions | ~12s | MySQL Wildcard Hosts, MySQL FILE Privilege, MySQL File Access, MySQL Admin Accounts, PostgreSQL Superusers, PostgreSQL Allowed Hosts |
| Configuration & Hardening | ~19s | Service User, Default Databases, Database Patches, Transaction Log Placement, Database Backups, Backup File Permissions |
| Transport Encryption | ~11s | MySQL TLS Enforcement, MySQL Certificate, PostgreSQL TLS, PostgreSQL Certificate, MongoDB TLS |
| Credential Storage | ~10s | Config Files In Web Root, Config File Permissions, Git In Web Root, Client Credential Files |
Web Servers
Covers Nginx and Apache.
| Audit | Approx | Checks it runs |
|---|---|---|
| Content Exposure | ~14s | Directory Listing, Hidden File Exposure, Backup Files, HTTP Methods, Status Endpoints, Default Pages |
| TLS Configuration | ~12s | HTTPS Enabled, TLS Protocols, TLS Ciphers, Certificate Expiry, HTTPS Redirect, OCSP Stapling |
| Logging & Monitoring | ~12s | Access Logging, Error Logging, Log File Permissions, Log Rotation, Web Application Firewall |
| Configuration Hardening | ~11s | Config File Permissions, Web Root Ownership, Request Size Limits, Connection Timeouts, Rate Limiting, Unnecessary Modules |
| Security Headers | ~10s | HSTS, Content Type Options, Frame Protection, Content Security Policy, Referrer Policy, Permissions Policy |
| Identification & Patching | ~9s | Web Server Inventory, Version Disclosure, Worker User, Web Server Updates, Config Syntax |
Docker
| Audit | Approx | Checks it runs |
|---|---|---|
| Image Vulnerabilities | ~16s | Image Vulnerability Scan, Base Image Security, Image Secrets Scan, Image Configuration Review, Image Signature Verification, End-of-Life Base Image, Image Best Practices, Software Bill of Materials (SBOM) |
| Volume Permissions | ~13s | Sensitive Host Path Mounts, Volume Read/Write Access, World-Writable Volumes, Volume Ownership, Volume Permissions, Anonymous/Dangling Volumes, Read-Only Volume Enforcement |
| Daemon & Socket | ~12s | Docker Inventory, Docker TCP Socket, Socket In Containers, Docker Group Members, Daemon Log Level, Rootless Mode |
| Network & Supply Chain | ~12s | Port Publishing, Inter-Container Connectivity, Secrets In Env, Engine Updates, Image Hygiene |
| Container Hardening | ~10s | Privileged Containers, No New Privileges, Container User, Capabilities, Read-Only Filesystem, Sensitive Bind Mounts |
| Runtime & Resources | ~10s | Seccomp Profile, MAC Confinement, Memory Limits, CPU Limits, PID Limits, Restart Policy |
The scripts behind these audits are plain POSIX sh and detect the distribution and package manager themselves, so the same audit runs on Ubuntu, Debian, RHEL, Rocky, AlmaLinux, Amazon Linux, Fedora, Oracle Linux, SUSE, and Alpine without you picking a variant.
What the audit does not do
Owning the boundary is more useful than implying a wider one. Four things this is not:
It is not a vulnerability scanner. There is no CVE feed, no exploit testing, and it never probes hosts you do not already have SSH access to. It reads how a machine is configured. The Docker audit named Image Vulnerabilities checks image hygiene, such as base image age, mutable tags, embedded secrets, signature verification, and whether an SBOM exists, rather than querying a vulnerability database. For CVE scanning, use Trivy, Grype, or a commercial scanner.
It is not continuous monitoring. There is no schedule, no daemon, and no agent installed on the server. Every audit is a run you start, and nothing watches the box between runs. If you need continuous detection and alerting, that is a host intrusion detection system such as Wazuh or Falco.
It is not a compliance certification. The checks are not mapped to CIS Benchmark or STIG control identifiers, and the reports are not attested evidence for SOC 2, ISO 27001, PCI, or HIPAA. They are a hardening record: useful internally, useful to hand a client, not something an auditor will accept as a control matrix. For that path, use a compliance platform such as Vanta or Drata, or OpenSCAP with a certified SCAP profile.
It does not fix anything by itself. Findings become a prompt, the AI writes a command, and you approve it. There is no background remediation and no auto-apply.
Everything runs inside the SSH session you already opened. Nothing is installed on the server, no credential leaves your machine, and the report is written to your own disk. See SSH and Security for how the connection is handled, Access Management for auditing who can log in, which is a separate question from how the box is configured, or the Security Audit feature page for a shorter overview of what the module is for.
Tips
Run SSH & Access first on any server you have just inherited. It is fast, it needs the least privilege of the server audits, and root login plus password authentication left on is the single most common real finding.
Use the check picker rather than skipping an audit entirely. If a Database audit is mostly irrelevant because the box only runs Postgres, untick the MySQL, MongoDB, and Redis checks and you get a short, fully relevant run instead of a wall of skips.
Fix one finding at a time on your first pass through the AI loop. Reading a single approval card teaches you what the assistant does with an audit prompt, and batching is much less nerve-racking once you have seen it work.
Troubleshooting
Frequently Asked Questions
How do I run a security audit on a Linux server?
Connect the server in CtrlOps and click Audit in the left sidebar. Pick an audit from the catalog, click Generate, tick the checks you want to include, and click Continue. CtrlOps runs the checks over the SSH session you already have open, streams the log live, and hands you a report with a hardening score and a downloadable PDF. Nothing is installed on the server.
What does a CtrlOps security audit check?
There are 25 audits across four categories. Server audits cover SSH and access, firewall and network, system updates, file system permissions, services and processes, application security, and logging. Database audits cover authentication, network isolation, least-privilege permissions, transport encryption, credential storage, and configuration hardening for MySQL, PostgreSQL, MongoDB, and Redis. Docker audits cover container hardening, the daemon and socket, image hygiene, volume permissions, runtime limits, and network exposure. Web server audits cover security headers, TLS configuration, content exposure, configuration hardening, logging, and version disclosure.
Do I need root or sudo to run a security audit?
No. Audits run as the SSH user you connected with. Many checks read files that only root can open, and those checks report SKIP with the reason rather than guessing. The score card shows a coverage percentage so a partial run is never mistaken for a clean one. Connect as a user with sudo, or as root, to get full coverage.
Can I get a PDF security report to send to a client?
Yes. Every audit run produces a report you can download as a PDF from the download button at the top right of the result panel. It contains the hardening score, the severity breakdown, and every finding with its status and message. Past runs are kept under the Reports tab, so you can download an older one at any time.
Is CtrlOps Audit a vulnerability scanner?
No. It is a configuration audit. There is no CVE feed, no exploit testing, and it never probes hosts you do not have SSH access to. The Docker audit named Image Vulnerabilities checks image hygiene such as base image age, mutable tags, embedded secrets, and signature verification, rather than querying a vulnerability database. For CVE scanning use a dedicated scanner such as Trivy, Grype, or Nessus.
Does CtrlOps run security audits on a schedule?
No. There is no schedule, no daemon, and no agent on the server. Every audit is a run you start yourself, and nothing watches the server between runs. If you need continuous detection, that is a host intrusion detection system such as Wazuh or Falco.
Can I use CtrlOps audit reports for SOC 2, ISO 27001, or CIS compliance?
Not as attested evidence. The checks are not mapped to CIS Benchmark or STIG control identifiers and the reports are not audit evidence for a certification. They are a hardening record you can act on and hand to a client. For a certification path use a compliance platform such as Vanta or Drata, or OpenSCAP with a certified SCAP profile.
Does the AI fix security audit findings automatically?
No. Selecting findings and clicking Fix selected findings copies a prompt to your clipboard. You paste it into the AI Terminal, the assistant writes the command and explains why, and then it waits. Nothing runs on the server until you read the command and click Run. Findings are fixed one at a time with you approving each step.
Can I run the same audit on more than one server at once?
Yes. Open Audit Reports from the Home screen instead of the per-server Audit tab, click Generate on the audit you want, and the server picker lists every saved server with a checkbox. Select the servers and click Run on N servers. Each host is audited separately and gets its own report.
Access Management
See who can log in to every Linux server from one place. Offboard users across all servers at once and onboard with per-server roles.
Monitor Linux Server
The Infrastructure Details module lets you monitor your Linux server in real-time - live CPU usage, RAM status, disk storage, and the top 10 running processes - all from a single visual dashboard inside CtrlOps. No terminal commands like top, htop, or free -h required.