Prowler 5.48.0 on AWS User Guide
Overview
This guide covers launching and using the Prowler 5.48.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg OWASP ZAP and Vulnerability Scanners listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
Prowler is the open source cloud security assessment tool. It runs hundreds of read-only checks against an AWS account (672 in this release), and also Azure, Google Cloud, Kubernetes, Microsoft 365 and GitHub. It reports exactly which controls pass and which fail, with a severity, the affected resource and a remediation, and maps every finding onto published compliance frameworks such as CIS, AWS Foundational Security Best Practices, PCI DSS, HIPAA, SOC 2, ISO 27001, NIST and FedRAMP (50 frameworks for AWS). Its iac provider also scans Terraform, CloudFormation, Kubernetes manifests and Dockerfiles for misconfigurations and leaked secrets before they are deployed. Results are written as CSV, JSON in the OCSF schema and a self-contained HTML report.
Upstream Prowler installs from PyPI with pip. The cloudimg image adds:
- A fully hash-locked install. Prowler 5.48.0 and every one of its 243 dependencies are installed from a lock file that pins the sha256 of each file, the Prowler wheel included: nothing is resolved at build time and a byte that differs from PyPI aborts the build. The 11 pure-Python dependencies that publish no wheel were built from hash-checked source in a sandbox with no network, and each resulting wheel is pinned too. An integrity check (
sudo prowler-verify) re-hashes all of the roughly 76,000 installed Python files against the build manifest. - Read-only access to your AWS account through the instance role. No access key is ever stored on the instance: you attach an IAM role with the AWS managed
SecurityAuditandViewOnlyAccesspolicies, andprowler awspicks it up through IMDSv2. - A scheduled scan that cannot phone home. A daily timer scans infrastructure-as-code in a directory you choose. The scan runs as its own unprivileged
prowleruser inside a systemd sandbox with no network access at all: every address is denied, including the EC2 instance metadata service, so code in a scanned repository cannot reach your instance role or anything else. The operating system is read-only to it and it holds no capabilities. - Reports only root can read. The scheduled scan writes its reports with mode
0600into dated0700directories under/var/lib/prowler/reports, and the system journal gets only a one line count of findings. - A self-test on every new instance. On first boot the instance checks the installed scanner against its build manifest and scans two bundled fixtures: a deliberately insecure Terraform file that must be flagged and a clean target that must not.
- Key-only SSH (no password or keyboard-interactive login, no root login), IMDSv2 only, and a fully patched Ubuntu 24.04 LTS with unattended security upgrades switched on, including for the bundled
trivyengine that theiacprovider uses.
This is a command-line product: there is no web UI, no listening service, no account and no password. SSH on port 22 is the only open port.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet.
- A security group that allows TCP 22 (SSH) from your own networks only. No other inbound port is needed.
- To assess an AWS account: outbound HTTPS to the AWS APIs (through an internet gateway, a NAT gateway or VPC endpoints) and permission to create an IAM role. Scanning infrastructure-as-code needs neither.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Prowler 5.48.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (a full AWS assessment runs many checks in parallel and uses about 2 GB of memory), your key pair and a security group as above. The root volume defaults to 12 GiB; add space if you will keep many reports.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <sg-allowing-22-from-your-network> --subnet-id <subnet-id> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=prowler}]'
Step 2: Connect to your instance
| Operating system | SSH user |
|---|---|
| Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<instance-public-ip>
There is no password to look up: /etc/prowler/credentials on the instance is an informational note that says so and lists the paths used below.
Step 3: Check the installation
prowler --version
systemctl is-enabled prowler-scan.timer prowler-firstboot.service
systemctl is-active prowler-scan.timer
Prowler 5.48.0 (You are running the latest version, yay!)
enabled
enabled
active
prowler-firstboot.service ran once on the first boot of your instance and started the self-test. prowler-scan.timer runs the scheduled scan once a day. Confirm the self-test passed:
sudo journalctl -u prowler-selftest.service -o cat --no-pager | grep SELFTEST
prowler-scan: SELFTEST insecure rc=3 total=81 fail=13 critical=0 high=9 medium=1 low=3; clean rc=0 total=0 fail=0 critical=0 high=0 medium=0 low=0
prowler-scan: SELFTEST OK
Network use. When you run prowler yourself it makes one HTTPS request to see whether a newer Prowler release exists (the "latest version" note above); it sends nothing about your accounts or findings. The scheduled scan cannot make it at all (Step 9).
Step 4: Scan the insecure fixture
/usr/share/prowler/fixtures/insecure/main.tf is a small Terraform file with an SSH security group open to 0.0.0.0/0, a public-read S3 bucket and an EC2 instance with an unencrypted volume that allows IMDSv1. Never apply it. Scan it with the iac provider, which needs no credentials, and write all three report formats to ~/prowler-out. Prowler adds to an existing report of the same name instead of replacing it, so each command below first removes the previous run's files (or give every run its own -F name):
rm -f ~/prowler-out/insecure.*
prowler iac --scan-path /usr/share/prowler/fixtures/insecure --no-banner --no-color \
-M csv json-ocsf html -F insecure -o ~/prowler-out > ~/prowler-insecure.log 2>&1; echo "exit code: $?"
grep -A 12 'Overview Results' ~/prowler-insecure.log
exit code: 3
Overview Results:
╭────────────────────┬────────────────────┬────────────────╮
│ 16.05% (13) Failed │ 83.95% (68) Passed │ 0.0% (0) Muted │
╰────────────────────┴────────────────────┴────────────────╯
Directory /usr/share/prowler/fixtures/insecure Scan Results (severity columns are for fails only):
╭────────────┬───────────┬───────────┬────────────┬────────┬──────────┬───────┬─────────╮
│ Provider │ Service │ Status │ Critical │ High │ Medium │ Low │ Muted │
├────────────┼───────────┼───────────┼────────────┼────────┼──────────┼───────┼─────────┤
│ iac │ terraform │ FAIL (13) │ 0 │ 9 │ 1 │ 3 │ 0 │
╰────────────┴───────────┴───────────┴────────────┴────────┴──────────┴───────┴─────────╯
The OCSF JSON report holds every check with its status, severity, resource and remediation. List the failed ones:
jq -r '.[] | select(.status_code=="FAIL") | "\(.severity) \(.metadata.event_code) \(.finding_info.title)"' \
~/prowler-out/insecure.ocsf.json | sort -u
High AWS-0028 aws_instance should activate session tokens for Instance Metadata Service.
High AWS-0086 S3 Access block should block public ACL
High AWS-0087 S3 Access block should block public policy
High AWS-0091 S3 Access Block should Ignore Public ACL
High AWS-0092 S3 Buckets not publicly accessible through ACL.
High AWS-0093 S3 Access block should restrict public bucket to limit access
High AWS-0107 Security groups should not allow unrestricted ingress to SSH or RDP from any IP address.
High AWS-0131 Instance with unencrypted block device.
High AWS-0132 S3 encryption should use Customer Managed Keys
Low AWS-0089 S3 Bucket Logging
Low AWS-0094 S3 buckets should each define an aws_s3_bucket_public_access_block
Low AWS-0124 Missing description for security group rule.
Medium AWS-0090 S3 Data should be versioned

insecure.html is a self-contained report you can open in a browser; copy it to your workstation with scp ubuntu@<instance-public-ip>:prowler-out/insecure.html ..
Step 5: Confirm a clean scan reports nothing
rm -f ~/prowler-out/clean.*
prowler iac --scan-path /usr/share/prowler/fixtures/clean --no-banner --no-color \
-M json-ocsf -F clean -o ~/prowler-out > ~/prowler-clean.log 2>&1; echo "clean exit code: $?"
grep 'no findings' ~/prowler-clean.log
clean exit code: 0
There are no findings in Directory /usr/share/prowler/fixtures/clean
Step 6: Scan your own infrastructure code
Point --scan-path at a directory. As an example, a Terraform RDS instance that is public and unencrypted:
mkdir -p ~/iac-demo
cat > ~/iac-demo/main.tf <<'EOF'
resource "aws_db_instance" "orders" {
identifier = "orders"
engine = "postgres"
instance_class = "db.t3.micro"
allocated_storage = 20
publicly_accessible = true
storage_encrypted = false
skip_final_snapshot = true
}
EOF
rm -f ~/prowler-out/demo.*
prowler iac --scan-path ~/iac-demo --no-banner --no-color -M json-ocsf -F demo -o ~/prowler-out > ~/prowler-demo.log 2>&1; echo "exit code: $?"
jq -r '.[] | select(.status_code=="FAIL") | "\(.severity) \(.metadata.event_code) \(.finding_info.title)"' ~/prowler-out/demo.ocsf.json | sort -u
exit code: 3
High AWS-0080 RDS encryption has not been enabled at a DB Instance level.
High AWS-0180 RDS Publicly Accessible
Low AWS-0133 Enable Performance Insights to detect potential problems
Medium AWS-0077 RDS Cluster and RDS instance should have backup retention longer than default 1 day
Medium AWS-0176 RDS IAM Database Authentication Disabled
Medium AWS-0177 RDS Deletion Protection Disabled
To scan one of your repositories, clone it and point --scan-path at it, or let Prowler clone it with --scan-repository-url https://github.com/<your-org>/<your-infra-repo>. --exclude-path skips vendored directories, and --scanners misconfig turns off the secret scanner if you only want misconfigurations.
Step 7: Assess your AWS account with a read-only instance role
prowler aws needs AWS credentials. On this image you give it a read-only IAM role attached to the instance, so no access key is ever written to disk. Interactive users on the instance (ubuntu, and root through sudo) can reach the instance metadata service over IMDSv2 and receive the role's temporary credentials; the scheduled scan and the self-test cannot (Step 9).
1. Create the role (from your workstation or CloudShell, as an administrator of the account to assess):
cat > ec2-trust.json <<'EOF'
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"ec2.amazonaws.com"},"Action":"sts:AssumeRole"}]}
EOF
aws iam create-role --role-name prowler-readonly --assume-role-policy-document file://ec2-trust.json
aws iam attach-role-policy --role-name prowler-readonly --policy-arn arn:aws:iam::aws:policy/SecurityAudit
aws iam attach-role-policy --role-name prowler-readonly --policy-arn arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
aws iam create-instance-profile --instance-profile-name prowler-readonly
aws iam add-role-to-instance-profile --instance-profile-name prowler-readonly --role-name prowler-readonly
SecurityAudit and ViewOnlyAccess are the AWS managed read-only policies Prowler documents. Neither allows any change to your account. Grant nothing broader.
2. Attach it to the instance:
aws ec2 associate-iam-instance-profile --region <region> --instance-id <instance-id> \
--iam-instance-profile Name=prowler-readonly
3. Run an assessment on the instance. Start narrow, then widen:
prowler aws --service s3 iam # two services
prowler aws --severity critical high # only critical and high findings
prowler aws --compliance cis_4.0_aws # one compliance framework
prowler aws -o ~/prowler-out/aws-full # everything (allow 15 to 60 minutes)
Each run writes CSV, OCSF JSON and HTML reports (named after the account ID and the time) plus one CSV per compliance framework under compliance/. To assess other accounts of an AWS Organization, add a role in each that trusts this role and use prowler aws --role arn:aws:iam::<account-id>:role/<role-name>.
You can see what Prowler will check without any credentials:
for p in aws azure gcp kubernetes; do printf "%s: " $p; prowler $p --list-checks --no-banner --no-color 2>/dev/null | grep -E "There (are|is)" | tail -1; done
prowler aws --list-compliance --no-banner --no-color 2>/dev/null | grep -cE '^ ?- '
aws: There are 672 available checks.
azure: There are 203 available checks.
gcp: There are 116 available checks.
kubernetes: There are 92 available checks.
50
Exit codes. Prowler exits 3 when at least one check failed, 0 when none did and 1 when the run itself failed, which makes prowler ... ; [ $? -eq 3 ] a usable pipeline gate. --ignore-exit-code-3 makes a run with findings exit 0.
Step 8: The scheduled scan and its reports
The scheduled scan reads /etc/prowler/scan.conf, scans SCAN_TARGET (default /srv/iac) with the iac provider and writes a dated report directory plus a latest link. Copy the example code into it, run one now and read its log:
sudo cp -r ~/iac-demo /srv/iac/ && sudo chmod -R a+rX /srv/iac
sudo systemctl start prowler-scan.service
sudo journalctl -u prowler-scan.service -n 4 --no-pager -o cat
prowler-scan: FINDINGS (iac, rc=3) total=60 fail=6 critical=0 high=2 medium=3 low=1; report /var/lib/prowler/reports/20261108T164003Z
prowler-scan.service: Deactivated successfully.
Finished prowler-scan.service - Scheduled Prowler scan (cloudimg; config /etc/prowler/scan.conf).
prowler-scan.service: Consumed 19.497s CPU time.
systemctl start waits for the scan to finish. The reports belong to the prowler user and only root can read them:
sudo ls -l /var/lib/prowler/reports/latest/
ls /var/lib/prowler/reports 2>&1 || true
total 400
drwx------ 2 prowler prowler 4096 Nov 8 16:40 compliance
-rw------- 1 prowler prowler 1924 Nov 8 16:40 prowler.log
-rw------- 1 prowler prowler 39712 Nov 8 16:40 results.csv
-rw------- 1 prowler prowler 74157 Nov 8 16:40 results.html
-rw------- 1 prowler prowler 282429 Nov 8 16:40 results.ocsf.json
ls: cannot access '/var/lib/prowler/reports': Permission denied

A scan with findings exits 3, which systemd treats as a normal result; the journal line tells you which report to open with sudo. Everything under SCAN_TARGET must be readable by the prowler user (files 0644, directories 0755). Read the HTML report on your workstation by copying it off as root first, for example sudo install -m 0644 /var/lib/prowler/reports/latest/results.html ~/ and then scp.
Step 9: What the scheduled scan can reach
systemctl show -p User -p IPAddressDeny -p ProtectSystem -p ProtectHome -p ReadWritePaths -p NoNewPrivileges -p CapabilityBoundingSet -p PrivateTmp prowler-scan.service
IPAddressDeny=::/0 169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 0.0.0.0/0
CapabilityBoundingSet=
User=prowler
ReadWritePaths=/var/lib/prowler
PrivateTmp=yes
ProtectHome=read-only
ProtectSystem=strict
NoNewPrivileges=yes
0.0.0.0/0 and ::/0 mean every address. The unit also names the instance metadata service (169.254.169.254, fd00:ec2::254) explicitly; once systemd has reloaded its configuration it may show only ::/0 0.0.0.0/0, because those two already include every other entry: the scheduled scan, the self-test and the first boot unit cannot open any network connection, so they can never receive the instance role's credentials and a malicious file in a scanned repository cannot use them to reach anything. The iac provider needs no network; its rules ship with the bundled engine. The only path the scan may write to is /var/lib/prowler.
Scheduled AWS assessments (opt in). The scheduled scan defaults to iac for exactly this reason. If you want a daily prowler aws run under the instance role instead, you must deliberately give the unit network access. Set PROWLER_PROVIDER="aws" in /etc/prowler/scan.conf, then add a drop-in that clears the deny list:
sudo systemctl edit prowler-scan.service
# add these two lines in the editor, then save:
[Service]
IPAddressDeny=
Only do this with a read-only role as in Step 7, and remember that the prowler user then can reach the metadata service like any interactive user. sudo systemctl revert prowler-scan.service restores the sandbox.
Step 10: Configuration
grep -v '^#' /etc/prowler/scan.conf | grep .
SCAN_ENABLED="yes"
PROWLER_PROVIDER="iac"
SCAN_TARGET="/srv/iac"
PROWLER_ARGS=""
KEEP_REPORTS="14"
SCAN_ENABLED:nomakes the daily scan exit without scanning.PROWLER_PROVIDERandSCAN_TARGET: what the daily scan assesses (iacscansSCAN_TARGET; see Step 9 before choosingaws).PROWLER_ARGS: extra Prowler flags, for example--exclude-path /srv/iac/vendoror--severity critical high.KEEP_REPORTS: how many dated report directories are kept.
The file is read at the start of every scan. Edit it with sudo nano /etc/prowler/scan.conf. To change the schedule, add a drop-in with sudo systemctl edit prowler-scan.timer (for example OnCalendar=hourly). The default is OnCalendar=daily with up to 30 minutes of random delay.
Step 11: How the scanner on this image was built
sudo prowler-verify
prowler-verify: OK prowler 5.48.0, tree 76275 c6dbcc8b84b9fac41eff4ad7ec16b598737476a84f8413cc48a06e41446d07de
prowler-verify re-hashes every file of the virtual environment's site-packages (Prowler and all its dependencies) and compares the result with the manifest recorded at build time; first boot and every scheduled scan run it first and refuse to scan if anything changed. The full record:
cat /usr/share/prowler/build-provenance/BUILD-PROVENANCE.txt
Prowler 5.48.0 on Ubuntu 24.04 (AWS), built by cloudimg
release tag 5.48.0 = commit 8a5aebb8a8c34dcecd17eba79466b94226440804 (github.com/prowler-cloud/prowler, Apache-2.0)
wheel prowler-5.48.0-py3-none-any.whl sha256 63ded14566f2314150db354282821530965d0c2f31e5b431c8452fd1d46ad2e8 (PyPI)
dependencies: hash lock for python 3.12 x86_64 linux (requirements.lock sha256 6d0ff5765f08f4a00fd1b6936d200561028b6b52e434c761776c3f389ad071f8): 233 wheels installed
--require-hashes --only-binary=:all: --no-deps. 11 pure-Python packages with no wheel for the needed version
(sdists.lock sha256 c9f528dfe2aaa37daa9709ecf3f9f5e198c3104d627f520fc802fe3579c75178): each sdist fetched by fixed URL (sdist-urls.txt) and sha256-checked before
use, built by a throwaway unprivileged user in a network-less, read-only systemd sandbox with the hash-locked
setuptools (--no-index --no-build-isolation, SOURCE_DATE_EPOCH=315532800); every built wheel matched its pinned
sha256 (sdist-built-wheels.sha256); build user and work directory removed. pymongo 4.18.3 per upstream's own
[tool.uv] override (the in-range 4.15.1 carries GHSA-vp6j-j7w5-5xjj and four more); pip-audit clean
pip 26.3 (sha256 f3b60b20ffaadd4415ed7957212a38ac63fa4c5fa375bd0f4a797777940fad13) used to build, then removed from the venv; trivy 0.76.2 from aquasecurity.github.io/trivy-repo (key 825AD9036F7C850E6A6FED4935B8ACA44FD9CA9F)
tree manifest: 76275 c6dbcc8b84b9fac41eff4ad7ec16b598737476a84f8413cc48a06e41446d07de
The same directory holds the lock files (requirements.lock with the sha256 of all 233 wheels, sdists.lock with the sha256 of the 11 source packages), the source URLs, the pinned sha256 of every wheel built from source, and the installed package list. The 11 source-only packages are ten small alibabacloud-* helpers used by Prowler's Alibaba Cloud provider and shodan, used by the optional Shodan check; none of them publishes a wheel for the version used here (for alibabacloud-tea, the last wheel is an older release that lacks an interface Prowler uses). pip itself was only a build tool and is not on the image.
Step 12: Access to the instance
sudo sshd -T | grep -E '^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin|pubkeyauthentication) '
permitrootlogin no
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
SSH accepts only the key pair you chose at launch (/etc/ssh/sshd_config.d/10-cloudimg-key-only.conf), and no account on the image has a password. Nothing else listens on a public address.

Step 13: Components
| Component | Path |
|---|---|
| Prowler | /opt/prowler/venv (command linked from /usr/local/bin/prowler) |
| IaC engine | /usr/bin/trivy (Aqua Security apt repository, signing key pinned) |
| Scheduled scan runner | /usr/local/bin/prowler-scan-run |
| Scan, self-test and first boot units | /etc/systemd/system/prowler-scan.service, prowler-scan.timer, prowler-selftest.service, prowler-firstboot.service |
| Configuration | /etc/prowler/scan.conf |
| Reports | /var/lib/prowler/reports (prowler, mode 0700) |
| Default scan target | /srv/iac |
| Self-test fixtures | /usr/share/prowler/fixtures |
| Integrity check | /usr/local/sbin/prowler-verify |
| Build provenance and licence | /usr/share/prowler/build-provenance/, /usr/share/prowler/LICENSE.upstream |
| Key-only SSH drop-in | /etc/ssh/sshd_config.d/10-cloudimg-key-only.conf |
Step 14: Security recommendations
- Allow port 22 in the security group from your own networks only.
- Give the instance role
SecurityAuditandViewOnlyAccessand nothing more. Prowler never needs write access. - Treat every report as sensitive: it is a map of where your account is weak. Keep reports under
/var/lib/prowleror in your home directory, and copy them only to systems you trust. - Keep the operating system patched; unattended security upgrades are on (including
trivyfrom its vendor repository), andsudo apt-get update && sudo apt-get upgradeapplies everything else. New Prowler releases, with their new checks, arrive as new versions of this listing.
Support and licensing
Prowler is open source under the Apache License 2.0 (a copy is at /usr/share/prowler/LICENSE.upstream). This image is not affiliated with or endorsed by the Prowler project. There is no per user or per account licence fee. The AMI price covers cloudimg's build, hardening, first boot setup and support.
- Email: support@cloudimg.co.uk
- Website: https://www.cloudimg.co.uk
- Support: 24/7, with a 24 hour response SLA