Hoverfly 1.12.17 on AWS User Guide
Overview
This guide covers launching and using the Hoverfly 1.12.17 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg developer and API tools listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
Hoverfly, from SpectoLabs, simulates the HTTP and HTTPS APIs your code depends on. Your application or test suite sends its requests through Hoverfly's forward proxy, and Hoverfly answers them from a simulation: request/response pairs you record from the real API (capture mode) or write by hand. Tests then run against a fast, stable fake of a third-party API that is slow, rate limited, costly or not built yet, and you can reproduce exact responses, delays and failures on demand.
The cloudimg image runs Hoverfly as a private service:
- HTTPS from the first boot. nginx serves Hoverfly's admin API at
https://<public-ip>/api/with a certificate generated on your instance's first boot. Port 80 only redirects to HTTPS. Hoverfly's web dashboard is not served (its Angular 6 front end is end of life). - Two locks on the admin API. Every request needs this instance's API key in an
X-Cloudimg-Keyheader, and then Hoverfly's own admin login (a token from/api/token-auth). Without the key nginx answers401and Hoverfly never sees the request, so nobody else can use Hoverfly's login lockout to lock you out. - A private proxy. The proxy listens on
127.0.0.1:8500only and needs the admin credential. You reach it through an SSH tunnel or from tests that run on the instance; it is never an open relay. - A MITM CA that is unique to your instance. To simulate HTTPS, Hoverfly signs certificates with a CA. Hoverfly's built-in CA has its private key published in the Hoverfly source code, so the image does not use it: first boot generates a new CA on your instance (Step 7).
- No default login. The image contains no key, password, certificate or host name. First boot writes them to
/var/lib/cloudimg/hoverfly-credentials.txt(root only). - Hoverfly cannot reach inside your AWS account. Capture and spy mode, post-serve actions and body files make Hoverfly open connections for you. It is blocked at the kernel level from the EC2 instance metadata service (IPv4 and IPv6), from other link-local addresses and from this instance's own loopback services. Public addresses keep working.
- Hoverfly runs locked in. It runs as its own unprivileged user with no capabilities, a read-only file system apart from its state directory, a 1 GB memory cap, and no way to run any program but itself. The admin API refuses requests that would run commands (post-serve action and middleware writes) or stop the service.
- Pinned. The official Hoverfly 1.12.17 release, checked against its SHA-256 checksum. The binaries are the unmodified upstream files.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: Hoverfly 1.12.17 and hoverctl (/opt/hoverfly/bin/), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. Settings and simulations are in /etc/hoverfly/; the build record is in /usr/share/doc/cloudimg-hoverfly/. cloudimg also masks the fwupd-refresh timer (EC2 has no firmware it can update).
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 and the proxy tunnel), TCP 443 (the admin API) and TCP 80 (redirects to HTTPS). Open them to your own networks only. Do not open port 8500: the proxy is reached through SSH.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Hoverfly 1.12.17 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair, a security group as above and a subnet.
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 <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=hoverfly}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| Hoverfly 1.12.17 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot takes under a minute: it generates the certificate, the API key, the admin password, the token signing secret and the MITM CA, then starts Hoverfly and nginx. Check that everything is running:
systemctl is-active hoverfly nginx hoverfly-firstboot hoverfly-egress-guard
sudo test -f /var/lib/cloudimg/hoverfly-firstboot.done && echo FIRSTBOOT_DONE
/opt/hoverfly/bin/hoverfly -version
active
active
active
active
FIRSTBOOT_DONE
v1.12.17
Only SSH and nginx listen on the network; Hoverfly's admin API (8888) and proxy (8500) listen on loopback:
sudo ss -lntp | awk 'NR>1 && $4 !~ /^127\.0\.0\.5[34]/ {print $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort -u
0.0.0.0:22 users:(("sshd"),("systemd"))
0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
127.0.0.1:8500 users:(("hoverfly"))
127.0.0.1:8888 users:(("hoverfly"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))
Step 4: Get your URL and credentials
Run sudo cat /var/lib/cloudimg/hoverfly-credentials.txt to see your settings. The file is readable by root only and holds these values, all generated for this instance:
sudo sed -n 's/^\(HOVERFLY_[A-Z0-9_]*\)=.*/\1/p' /var/lib/cloudimg/hoverfly-credentials.txt
HOVERFLY_URL
HOVERFLY_HOST
HOVERFLY_GATE_HEADER
HOVERFLY_API_KEY
HOVERFLY_ADMIN_USER
HOVERFLY_ADMIN_PASSWORD
HOVERFLY_PROXY_ADDRESS
HOVERFLY_MITM_CA_CERT
HOVERFLY_MITM_CA_SHA256_FINGERPRINT
HOVERFLY_TLS_CERT
HOVERFLY_TLS_SHA256_FINGERPRINT
HOVERFLY_URLishttps://<public-ip>/api/, the admin API. SendHOVERFLY_API_KEYin theX-Cloudimg-Keyheader with every request.HOVERFLY_GATE_HEADERnames that header,X-Cloudimg-Key.HOVERFLY_ADMIN_USER(admin) andHOVERFLY_ADMIN_PASSWORDare Hoverfly's own login, for the admin API's tokens and for the proxy.HOVERFLY_PROXY_ADDRESSis the proxy,127.0.0.1:8500on the instance.HOVERFLY_MITM_CA_CERTandHOVERFLY_MITM_CA_SHA256_FINGERPRINTare this instance's MITM CA (Step 7).HOVERFLY_TLS_CERTandHOVERFLY_TLS_SHA256_FINGERPRINTare the admin API's own certificate. It is self-signed: when a client reports it, compare its SHA-256 fingerprint withHOVERFLY_TLS_SHA256_FINGERPRINTbefore you trust the connection.
The instance's private IP address and its EC2 DNS names are served too, so clients inside your VPC can use them.
Step 5: Use the admin API
The commands in this guide run on the instance. Save these helper functions once; they read the root-only credentials file with sudo each time and hand the secrets to curl through file descriptors, so no key or password lands in your shell history or the process list:
cat > ~/hoverfly-env.sh <<'EOF'
CF=/var/lib/cloudimg/hoverfly-credentials.txt
H=$(sudo sed -n 's/^HOVERFLY_HOST=//p' $CF)
# api: the admin API through nginx, with this instance's API key and certificate
api() { curl -s --cacert /etc/ssl/hoverfly/hoverfly.crt --resolve "$H:443:127.0.0.1" \
-K <(sudo sed -n 's/^HOVERFLY_API_KEY=\(.*\)/header = "X-Cloudimg-Key: \1"/p' $CF) "$@"; }
# token: log in to Hoverfly and print a token
token() { sudo sed -n 's/^HOVERFLY_ADMIN_PASSWORD=\(.*\)/{"username":"admin","password":"\1"}/p' $CF |
api -H 'Content-Type: application/json' --data-binary @- "https://$H/api/token-auth" | jq -r .token; }
# admin: an admin API call with the key and the token in $TOKEN
admin() { api -K <(printf 'header = "Authorization: Bearer %s"\n' "$TOKEN") "$@"; }
# pxy: a request through Hoverfly's proxy with the admin credential
pxy() { curl -s -x http://127.0.0.1:8500 \
-K <(sudo sed -n 's/^HOVERFLY_ADMIN_PASSWORD=\(.*\)/proxy-user = "admin:\1"/p' $CF) "$@"; }
EOF
Now log in and ask Hoverfly for its mode and version. jq -e prints the answer and fails if it is not what you expect:
. ~/hoverfly-env.sh
api "https://$H/api/health" | jq -e 'select(.message == "Hoverfly is healthy")'
TOKEN=$(token)
admin "https://$H/api/v2/hoverfly/mode" | jq -e 'select(.mode == "simulate")'
admin "https://$H/api/v2/hoverfly/version" | jq -e 'select(.version == "v1.12.17")'
{
"message": "Hoverfly is healthy"
}
{
"mode": "simulate",
"arguments": {
"matchingStrategy": "strongest"
}
}
{
"version": "v1.12.17"
}
Without the key, nginx answers 401 with its own WWW-Authenticate: CloudimgKey challenge. With the key but no token, the 401 comes from Hoverfly:
. ~/hoverfly-env.sh
curl -s --cacert /etc/ssl/hoverfly/hoverfly.crt --resolve "$H:443:127.0.0.1" -o /dev/null \
-w 'no key: %{http_code} %header{www-authenticate}\n' "https://$H/api/v2/hoverfly/mode"
api -o /dev/null -w 'key, no token: %{http_code}\n' "https://$H/api/v2/hoverfly/mode"
no key: 401 CloudimgKey realm="Hoverfly", header="X-Cloudimg-Key"
key, no token: 401
Step 6: Send requests through the proxy
The image starts in simulate mode with a small sample simulation, /etc/hoverfly/simulations/startup.json, for a made-up API at api.cloudimg-sample.test. That host does not exist: every answer below comes from Hoverfly. Without the proxy credential the proxy answers 407:
. ~/hoverfly-env.sh
curl -s -o /dev/null -w 'no proxy credential: %{http_code}\n' -x http://127.0.0.1:8500 http://api.cloudimg-sample.test/widgets
pxy http://api.cloudimg-sample.test/widgets | jq -e 'select(.[0].name == "Acme Sprocket")'
pxy -H 'Content-Type: application/json' -d '{"name":"Gear"}' http://api.cloudimg-sample.test/widgets | jq -e 'select(.name == "Gear")'
pxy http://api.cloudimg-sample.test/status | jq -e 'select(.servedFrom == "bodyFile")'
no proxy credential: 407
[
{
"id": 1,
"name": "Acme Sprocket",
"color": "blue"
},
{
"id": 2,
"name": "Acme Cog",
"color": "green"
}
]
{
"id": 3,
"name": "Gear",
"created": true
}
{
"service": "cloudimg-sample",
"status": "ok",
"servedFrom": "bodyFile"
}
The POST answer is a template: Hoverfly copies name from your request body into the response. The /status answer is read from a body file in /srv/hoverfly/body-files/.
Step 7: HTTPS and your instance's MITM CA
To answer an HTTPS request, Hoverfly acts as a man in the middle: it makes a certificate for the host your client asked for and signs it with a certificate authority (CA). Your client accepts that certificate only if it trusts the CA.
The MITM CA is unique to your instance. Hoverfly's built-in CA is the same for every Hoverfly user and its private key is published in the Hoverfly source code, so a client that trusts it would accept a certificate for any web site from anyone. This image never uses it. First boot generates a new CA on your instance (RSA 3072, valid for 10 years, allowed to sign server certificates only) and Hoverfly is started with it:
- The certificate (public, safe to copy) is
/etc/hoverfly/hoverfly-ca.crt. Its SHA-256 fingerprint isHOVERFLY_MITM_CA_SHA256_FINGERPRINTin the credentials file. - The private key never leaves the instance. It is
/etc/hoverfly/ca/ca.key, mode 0600, owned by thehoverflyservice user, inside a directory only that user and root can open. It is not in the credentials file, nothing prints it, and you never need to copy it.
openssl x509 -in /etc/hoverfly/hoverfly-ca.crt -noout -subject -ext basicConstraints
sudo stat -c '%n %a %U:%G' /etc/hoverfly/ca/ca.key
sudo grep -c 'PRIVATE KEY' /var/lib/cloudimg/hoverfly-credentials.txt || true
subject=O = cloudimg Hoverfly, CN = Hoverfly per-instance MITM CA 0aca677d
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:0
/etc/hoverfly/ca/ca.key 600 hoverfly:hoverfly
0
Send an HTTPS request through the proxy and verify Hoverfly's certificate against this CA only (--cacert):
. ~/hoverfly-env.sh
pxy --cacert /etc/hoverfly/hoverfly-ca.crt https://api.cloudimg-sample.test/widgets | jq -e 'select(.[1].name == "Acme Cog")'
[
{
"id": 1,
"name": "Acme Sprocket",
"color": "blue"
},
{
"id": 2,
"name": "Acme Cog",
"color": "green"
}
]
hoverfly-ca-presented shows which CA Hoverfly actually presents. It opens a TLS connection through the proxy, reads the certificate chain Hoverfly sends, and checks that the issuing CA is this instance's CA and not one of Hoverfly's public default CAs:
sudo hoverfly-ca-presented
HOVERFLY_CA_PRESENTED_SHA256=90:45:FF:7E:9D:29:28:A2:1E:CE:0A:26:1D:90:0C:84:30:6F:F9:58:16:45:59:81:C4:5F:F7:42:16:19:74:34
HOVERFLY_CA_INSTANCE_SHA256=90:45:FF:7E:9D:29:28:A2:1E:CE:0A:26:1D:90:0C:84:30:6F:F9:58:16:45:59:81:C4:5F:F7:42:16:19:74:34
HOVERFLY_CA_PROOF_OK
The fingerprint differs on every instance.
Trust the CA in your test clients
Trust the CA only on machines and tools you use for testing, and remove it when you finish. First copy the certificate to your machine and check its fingerprint against HOVERFLY_MITM_CA_SHA256_FINGERPRINT:
scp -i <your-key.pem> ubuntu@<public-ip>:/etc/hoverfly/hoverfly-ca.crt ./hoverfly-ca.crt
openssl x509 -in hoverfly-ca.crt -noout -fingerprint -sha256
- curl and most command-line tools: pass the file, as above:
curl --cacert hoverfly-ca.crt .... Many tools also read an environment variable:REQUESTS_CA_BUNDLE=hoverfly-ca.crtfor Pythonrequests,NODE_EXTRA_CA_CERTS=hoverfly-ca.crtfor Node.js,SSL_CERT_FILEfor many others. For Java, import it into a test trust store (keytool -importcert -alias hoverfly -file hoverfly-ca.crt -keystore hoverfly-test.jks) and start the JVM with-Djavax.net.ssl.trustStore=hoverfly-test.jks. - The system trust store (a Linux test machine or CI runner; on Ubuntu and Debian), named after the instance so you can remove it later:
sudo cp hoverfly-ca.crt /usr/local/share/ca-certificates/hoverfly-<public-ip>.crt
sudo update-ca-certificates
# when you are done:
sudo rm /usr/local/share/ca-certificates/hoverfly-<public-ip>.crt && sudo update-ca-certificates --fresh
On macOS, add it to the System keychain with Keychain Access (or sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain hoverfly-ca.crt); on Windows, import it into Trusted Root Certification Authorities with certutil -addstore -f Root hoverfly-ca.crt from an administrator prompt. Chrome and Edge use the system store.
* A browser: in Firefox open Settings > Privacy & Security > Certificates > View Certificates > Authorities > Import, choose hoverfly-ca.crt and tick Trust this CA to identify websites. Then set the browser's HTTP and HTTPS proxy to 127.0.0.1 port 8500 through the SSH tunnel in Step 10, and sign in as admin with HOVERFLY_ADMIN_PASSWORD when it asks. Use a separate browser profile for testing.
If you ever think the CA has been exposed, or when you hand the instance to someone else, replace it with sudo hoverfly-mint-ca (Step 13) and remove the old certificate from your trust stores.
Step 8: Load your own simulation
A simulation is a JSON document of request/response pairs. Write one, load it with PUT /api/v2/simulation (this replaces the current simulation), and call it through the proxy. This example simulates a small Todo API, then puts the sample back:
. ~/hoverfly-env.sh
TOKEN=$(token)
cd "$(mktemp -d)"
cat > todo-simulation.json <<'JSON'
{
"data": {
"pairs": [
{
"request": {
"method": [{"matcher": "exact", "value": "GET"}],
"destination": [{"matcher": "exact", "value": "todo.example.test"}],
"path": [{"matcher": "exact", "value": "/todos"}]
},
"response": {
"status": 200,
"body": "[{\"id\":1,\"title\":\"Write the simulation\",\"done\":true}]",
"headers": {"Content-Type": ["application/json"]}
}
}
]
},
"meta": {"schemaVersion": "v5.3"}
}
JSON
admin -X PUT -H 'Content-Type: application/json' --data-binary @todo-simulation.json "https://$H/api/v2/simulation" | jq -e '.data.pairs | length'
pxy http://todo.example.test/todos | jq -e 'select(.[0].title == "Write the simulation")'
admin -X PUT -H 'Content-Type: application/json' --data-binary @/etc/hoverfly/simulations/startup.json "https://$H/api/v2/simulation" | jq -e '.data.pairs | length'
1
[
{
"id": 1,
"title": "Write the simulation",
"done": true
}
]
3
Hoverfly keeps simulations in memory. At every start it loads /etc/hoverfly/simulations/startup.json; to keep your own simulation across restarts, copy it there (sudo install -m 0644 my-simulation.json /etc/hoverfly/simulations/startup.json) and restart Hoverfly. The original sample stays in /etc/hoverfly/simulations/cloudimg-sample.json. Request bodies are limited to 32 MB, and a simulation's bodyFile paths are read from /srv/hoverfly/body-files/.
Step 9: Capture a real API
In capture mode Hoverfly forwards each request to the real API and records the request and response as a new pair. Switch to capture, send traffic, switch back to simulate and the recorded API answers without the real one. This example records https://example.com/, exports the recording, then restores the sample:
. ~/hoverfly-env.sh
TOKEN=$(token)
admin -X PUT -H 'Content-Type: application/json' -d '{"mode":"capture"}' "https://$H/api/v2/hoverfly/mode" | jq -e 'select(.mode == "capture")'
pxy --cacert /etc/hoverfly/hoverfly-ca.crt -o /dev/null -w 'captured: %{http_code}\n' https://example.com/
admin -X PUT -H 'Content-Type: application/json' -d '{"mode":"simulate"}' "https://$H/api/v2/hoverfly/mode" | jq -e 'select(.mode == "simulate")'
admin "https://$H/api/v2/simulation" > ~/my-recording.json
jq -e '.data.pairs[].request.destination[0].value | select(. == "example.com")' ~/my-recording.json
pxy --cacert /etc/hoverfly/hoverfly-ca.crt -o /dev/null -w 'replayed: %{http_code}\n' https://example.com/
admin -X PUT -H 'Content-Type: application/json' --data-binary @/etc/hoverfly/simulations/startup.json "https://$H/api/v2/simulation" | jq -e '.data.pairs | length'
rm ~/my-recording.json
{
"mode": "capture",
"arguments": {}
}
captured: 200
{
"mode": "simulate",
"arguments": {
"matchingStrategy": "strongest"
}
}
"example.com"
replayed: 200
3
Hoverfly's other modes work the same way: spy answers from the simulation and forwards anything it has no pair for, modify and synthesize change or generate responses with middleware (middleware is off in this image), and diff compares the real API with the simulation. See the Hoverfly documentation for matchers, templating, delays and state.
hoverctl, Hoverfly's command-line client, is installed too. It talks to the admin API on 127.0.0.1:8888 and logs in with hoverctl login (user admin and HOVERFLY_ADMIN_PASSWORD). Enter the password carefully: three wrong logins within ten minutes lock every user out of Hoverfly until it restarts.
Step 10: Use Hoverfly from your own machine
The proxy is not open to the network. Forward it over SSH, then point your client or test suite at 127.0.0.1:8500 with the admin credential. Keep the password out of your shell history by putting it in a curl config file:
ssh -i <your-key.pem> -N -L 8500:127.0.0.1:8500 ubuntu@<public-ip> &
printf 'proxy-user = "admin:%s"\n' '<HOVERFLY_ADMIN_PASSWORD>' > hoverfly-proxy.cfg && chmod 600 hoverfly-proxy.cfg
curl -x http://127.0.0.1:8500 -K hoverfly-proxy.cfg --cacert hoverfly-ca.crt https://api.cloudimg-sample.test/widgets
The admin API is open on port 443. Copy its certificate, check the fingerprint against HOVERFLY_TLS_SHA256_FINGERPRINT, and send the key with every call:
scp -i <your-key.pem> ubuntu@<public-ip>:/etc/ssl/hoverfly/hoverfly.crt ./hoverfly-admin.crt
openssl x509 -in hoverfly-admin.crt -noout -fingerprint -sha256
printf 'header = "X-Cloudimg-Key: %s"\n' '<HOVERFLY_API_KEY>' > hoverfly-key.cfg && chmod 600 hoverfly-key.cfg
curl --cacert hoverfly-admin.crt -K hoverfly-key.cfg https://<public-ip>/api/health
Most HTTP clients take a proxy setting (HTTP_PROXY and HTTPS_PROXY, for example http://admin:<password>@127.0.0.1:8500) and a CA file (Step 7). Hoverfly does not send CORS headers and the admin API refuses state-changing requests from other web sites, so keep browser code away from the admin API; drive it from your test harness.
Step 11: What the front door refuses
Without the key every path answers 401, and a wrong key gets the same 401 (there is no lockout to trip, and the key is 192 random bits). With the key, the dashboard is not served, the endpoints that would run commands or stop Hoverfly are refused, another web site cannot change the mode, plain HTTP only redirects and unknown host names get no answer:
. ~/hoverfly-env.sh
TOKEN=$(token)
C=(curl -s --cacert /etc/ssl/hoverfly/hoverfly.crt --resolve "$H:443:127.0.0.1")
"${C[@]}" -o /dev/null -w 'no key: %{http_code}\n' "https://$H/api/v2/simulation"
"${C[@]}" -o /dev/null -w 'wrong key: %{http_code}\n' -K <(printf 'header = "X-Cloudimg-Key: %s"\n' "$(openssl rand -hex 24)") "https://$H/api/v2/simulation"
api -o /dev/null -w 'dashboard: %{http_code}\n' "https://$H/dashboard"
admin -o /dev/null -w 'post-serve action: %{http_code}\n' -X PUT -H 'Content-Type: application/json' -d '{"actionName":"x","binary":"sh","script":"id"}' "https://$H/api/v2/hoverfly/post-serve-action"
admin -o /dev/null -w 'middleware: %{http_code}\n' -X PUT -H 'Content-Type: application/json' -d '{"binary":"sh","script":"id"}' "https://$H/api/v2/hoverfly/middleware"
admin -o /dev/null -w 'shutdown: %{http_code}\n' -X DELETE "https://$H/api/v2/shutdown"
admin -o /dev/null -w 'cross-site mode change: %{http_code}\n' -X PUT -H 'Origin: https://evil.example' -H 'Content-Type: application/json' -d '{"mode":"capture"}' "https://$H/api/v2/hoverfly/mode"
curl -s -o /dev/null -w 'plain http: %{http_code} %header{location}\n' --resolve "$H:80:127.0.0.1" "http://$H/api/health"
curl -sk -o /dev/null -w 'unknown host name: %{http_code}\n' --resolve "other.example:443:127.0.0.1" https://other.example/api/health || true
no key: 401
wrong key: 401
dashboard: 404
post-serve action: 403
middleware: 403
shutdown: 403
cross-site mode change: 403
plain http: 301 https://<public-ip>/api/health
unknown host name: 000
Hoverfly's outbound fence is visible in its unit and in the nftables rules for its service account (the metadata addresses 169.254.169.254 and fd00:ec2::254 fall inside the listed ranges):
systemctl show -p IPAddressDeny --value hoverfly | tr ' ' '\n' | sort
sudo nft list table inet cloudimg_hoverfly_egress | grep -oE '(oifname "lo"|ip6? daddr \{).*(accept|reject.*)' | sed -E 's/counter packets [0-9]+ bytes [0-9]+ //'
168.63.129.16/32
169.254.0.0/16
fd00:ec2::254/128
fe80::/10
oifname "lo" ip daddr 127.0.0.53 udp dport 53 accept
oifname "lo" ip daddr 127.0.0.53 tcp dport 53 accept
oifname "lo" ct state new reject
ip daddr { 168.63.129.16, 169.254.0.0/16 } reject with icmp port-unreachable
ip6 daddr { fd00:ec2::254, fe80::/10 } reject with icmpv6 port-unreachable
So a capture, a spy request, a remote post-serve action or a body file URL that points at the metadata service or at this instance's own ports never connects, while public addresses work.
Step 12: Run the self-test
The image ships a self-test that checks every refusal of the gate (including 25 wrong keys followed by the right one), Hoverfly's own login, the refused endpoints, the proxy over HTTP and HTTPS, the CA Hoverfly presents, the request policy, the listening ports and the certificate:
sudo /usr/local/sbin/hoverfly-selftest
HOVERFLY_SELFTEST_OK
Step 13: Rotate the key, the password or the CA
Each helper generates a new secret, puts it in force and writes it to the credentials file; none of them prints a secret. A new API key works at once and the old one stops:
sudo hoverfly-rotate-key
hoverfly-rotate-key: a new API key is in force; read it with: sudo grep HOVERFLY_API_KEY= /var/lib/cloudimg/hoverfly-credentials.txt
A new admin password and token signing secret restart Hoverfly, so every issued token stops working and the simulation is reloaded from startup.json (save your own first, Step 8):
sudo hoverfly-rotate-password
hoverfly-rotate-password: a new admin password is in force; read it with: sudo grep HOVERFLY_ADMIN_PASSWORD= /var/lib/cloudimg/hoverfly-credentials.txt
A new MITM CA also restarts Hoverfly. Replace the old certificate in your trust stores with the new /etc/hoverfly/hoverfly-ca.crt:
sudo hoverfly-mint-ca
sudo hoverfly-ca-presented | tail -n 1
hoverfly-mint-ca: MITM CA SHA-256 fingerprint 18:D5:1A:47:2A:DE:FE:7D:D6:95:00:9A:3D:4D:7F:CC:89:79:65:97:1B:81:A6:C1:D9:E9:CF:D5:B4:A1:26:19
HOVERFLY_CA_PROOF_OK
Step 14: Use your own domain name
Point a DNS record at the instance, then set the name. The helper generates a new certificate for it (and the instance's addresses), pins nginx to those names (other host names get no answer) and prints the new fingerprint:
sudo hoverfly-set-hostname <your-domain>
To use a CA-issued certificate instead, replace /etc/ssl/hoverfly/hoverfly.crt and /etc/ssl/hoverfly/hoverfly.key with your own (keep the key root only, mode 0600) and run sudo systemctl reload nginx. If the instance's public IP changes after a stop and start, run sudo hoverfly-set-hostname with the new address, or use an Elastic IP.
Step 15: Security notes
The admin API certificate generated at first boot is a server certificate only: it cannot sign other certificates. Only the MITM CA can, and it can sign server certificates only (pathlen:0).
openssl x509 -in /etc/ssl/hoverfly/hoverfly.crt -noout -ext basicConstraints,extendedKeyUsage
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Extended Key Usage:
TLS Web Server Authentication
Hoverfly's public default CA keys. The official Hoverfly binaries contain the private keys of Hoverfly's two public default CAs, as every copy of Hoverfly does. They are public, the image never uses them (Hoverfly always starts with this instance's CA, and refuses to start with a default one), and the binaries are byte-identical to the files in the official release zip (hoverfly_bundle_linux_amd64.zip, whose SHA-256 the build record /usr/share/doc/cloudimg-hoverfly/BUILD-INFO.txt lists with each file's). Check them at any time:
cd / && sha256sum -c /usr/share/doc/cloudimg-hoverfly/SHA256SUMS
opt/hoverfly/bin/hoverfly: OK
opt/hoverfly/bin/hoverctl: OK
opt/hoverfly/LICENSE: OK
Known advisories with no reachable path. The build record lists the advisories that remain in the shipped binaries and why none is reachable (for example, the end-of-life dashboard is not served and the middleware API is off):
grep -oE '^(GO-[0-9-]+( / GO-[0-9-]+)?|Embedded admin dashboard)' /usr/share/doc/cloudimg-hoverfly/RESIDUALS
GO-2026-6866 / GO-2026-6860
GO-2026-5932
GO-2025-3944
Embedded admin dashboard
Step 16: Keep it up to date
Ubuntu security updates install automatically. To apply everything now:
sudo apt-get update && sudo apt-get upgrade -y
New Hoverfly releases arrive as new versions of the AWS Marketplace listing: launch the new version and copy your simulations from /etc/hoverfly/simulations/ across.
Support
- Email: support@cloudimg.co.uk
- Website: https://www.cloudimg.co.uk
Hoverfly is developed by SpectoLabs and released under the Apache License 2.0. cloudimg is not affiliated with SpectoLabs. The other versions on this listing are on the developer and API tools guide.