Hoverfly 1.12.17
Overview
This guide covers launching and using the Hoverfly 1.12.17 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud developer and API tools listing on AWS Marketplace. You select 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 from a simulation. A simulation is a set of request/response pairs that you record from the real API (capture mode) or write by hand. Your tests then run against a fast, stable stand-in for a third-party API that is slow, rate limited, costly or not built yet. You can reproduce exact responses, delays and failures on demand.
The SecureAnyCloud image runs Hoverfly as a private service. What you get:
- 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, because 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-Sac-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/sac/data/hoverfly-credentials.txt(root only). - No reach into your AWS account. Capture and spy mode, post-serve actions and body files make Hoverfly open connections on your behalf. Hoverfly 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.
- A locked-down service. Hoverfly 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/, and the build record is in /usr/share/doc/sac-hoverfly/. SecureAnyCloud also masks the fwupd-refresh timer, because EC2 has no firmware to 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, because you reach the proxy through SSH.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Select Hoverfly 1.12.17 on Ubuntu 24.04 as the software version, choose your Region, and choose Launch through EC2. Select m5.large, your key pair, a subnet and a security group as described above.
To launch with the AWS CLI instead, 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}]'Connect to your instance
On Hoverfly 1.12.17 on Ubuntu 24.04, the SSH user is ubuntu. Connect with your key pair:
ssh -i <your-key.pem> ubuntu@<public-ip>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/sac/data/hoverfly-firstboot.done && echo FIRSTBOOT_DONE /opt/hoverfly/bin/hoverfly -versionactive active active active FIRSTBOOT_DONE v1.12.17Only 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 -u0.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"))Get your URL and credentials
Run sudo cat /var/lib/sac/data/hoverfly-credentials.txt to see your settings. Only root can read the file. It holds the following values, all generated for this instance:
sudo sed -n 's/^\(HOVERFLY_[A-Z0-9_]*\)=.*/\1/p' /var/lib/sac/data/hoverfly-credentials.txtHOVERFLY_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_FINGERPRINTHOVERFLY_URLishttps://<public-ip>/api/, the admin API. SendHOVERFLY_API_KEYin theX-Sac-Keyheader with every request.HOVERFLY_GATE_HEADERnames that header,X-Sac-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. The certificate is self-signed, so 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.
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/sac/data/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-Sac-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) "$@"; } EOFNow log in and ask Hoverfly for its mode and version. jq -e prints the answer and fails if the answer 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: SacKey challenge. With the key but no token, Hoverfly itself answers 401:
. ~/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 SacKey realm="Hoverfly", header="X-Sac-Key" key, no token: 401Send 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.sac-sample.test. That host does not exist, so 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.sac-sample.test/widgets pxy http://api.sac-sample.test/widgets | jq -e 'select(.[0].name == "Acme Sprocket")' pxy -H 'Content-Type: application/json' -d '{"name":"Gear"}' http://api.sac-sample.test/widgets | jq -e 'select(.name == "Gear")' pxy http://api.sac-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": "sac-sample", "status": "ok", "servedFrom": "bodyFile" }The POST answer is a template: Hoverfly copies name from your request body into the response. Hoverfly reads the /status answer from a body file in /srv/hoverfly/body-files/.
Use 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. A client that trusts it would accept a certificate for any web site from anyone, so 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 starts 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 that only that user and root can open. It is not in the credentials file, nothing prints it, and you never need to copy it.
Check the certificate, the key permissions and the credentials file:
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/sac/data/hoverfly-credentials.txt || truesubject=O = sac Hoverfly, CN = Hoverfly per-instance MITM CA 0aca677d X509v3 Basic Constraints: critical CA:TRUE, pathlen:0 /etc/hoverfly/ca/ca.key 600 hoverfly:hoverfly 0Send 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.sac-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 and reads the certificate chain that Hoverfly sends. It then checks that the issuing CA is this instance's CA and not one of Hoverfly's public default CAs:
sudo hoverfly-ca-presentedHOVERFLY_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_OKThe fingerprint differs on every instance.
Trust the CA in your test clients
Trust the CA only on machines and tools that 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, andSSL_CERT_FILEfor many others. For Java, import the certificate 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: on a Linux test machine or CI runner (Ubuntu and Debian), name the file after the instance so that 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 --freshOn macOS, add the certificate to the System keychain with Keychain Access, or run 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 from an administrator prompt with certutil -addstore -f Root hoverfly-ca.crt. 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 select 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. When the browser asks, sign in as admin with HOVERFLY_ADMIN_PASSWORD. Use a separate browser profile for testing.
If you 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.
Load your own simulation
A simulation is a JSON document of request/response pairs. Write one, load it with PUT /api/v2/simulation (which replaces the current simulation), and call it through the proxy. This example simulates a small Task API, then restores the sample:
. ~/hoverfly-env.sh
TOKEN=$(token)
cd "$(mktemp -d)" cat > task-simulation.json <<'JSON' { "data": { "pairs": [ { "request": { "method": [{"matcher": "exact", "value": "GET"}], "destination": [{"matcher": "exact", "value": "task.example.test"}], "path": [{"matcher": "exact", "value": "/tasks"}] }, "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 @task-simulation.json "https://$H/api/v2/simulation" | jq -e '.data.pairs | length' pxy http://task.example.test/tasks | 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 } ] 3Hoverfly keeps simulations in memory and loads /etc/hoverfly/simulations/startup.json at every start. To keep your own simulation across restarts, copy it there with sudo install -m 0644 my-simulation.json /etc/hoverfly/simulations/startup.json and restart Hoverfly. The original sample stays in /etc/hoverfly/simulations/sac-sample.json.
Request bodies are limited to 32 MB. Hoverfly reads a simulation's bodyFile paths from /srv/hoverfly/body-files/.
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, then switch back to simulate, and Hoverfly answers from the recording without calling the real API. 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 3Hoverfly'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, which is off in this image. 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 also installed. 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, because three wrong logins within ten minutes lock every user out of Hoverfly until it restarts.
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. To keep the password out of your shell history, put 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.sac-sample.test/widgetsThe admin API is reachable 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-Sac-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/healthMost 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. Keep browser code away from the admin API, and drive it from your test harness instead.
See what the front door refuses
Without the key, every path answers 401. A wrong key gets the same 401, so there is no lockout to trip, and the key is 192 random bits. With the key, the dashboard is not served and 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-Sac-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 || trueno 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: 000You can see Hoverfly's outbound restrictions 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 sac_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-unreachableAs a result, 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. Public addresses work normally.
Run the self-test
The image includes a self-test. It 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-selftestHOVERFLY_SELFTEST_OKRotate 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 working:
sudo hoverfly-rotate-keyhoverfly-rotate-key: a new API key is in force; read it with:
sudo grep HOVERFLY_API_KEY= /var/lib/sac/data/hoverfly-credentials.txtA new admin password and token signing secret restart Hoverfly. Every issued token stops working, and the simulation reloads from startup.json, so save your own simulation first (Step 8):
sudo hoverfly-rotate-passwordhoverfly-rotate-password: a new admin password is in force; read it with:
sudo grep HOVERFLY_ADMIN_PASSWORD= /var/lib/sac/data/hoverfly-credentials.txtA new MITM CA also restarts Hoverfly. Replace the old certificate in your trust stores with the new /etc/hoverfly/hoverfly-ca.crt. To rotate the CA, run:
sudo hoverfly-mint-ca
sudo hoverfly-ca-presented | tail -n 1hoverfly-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_OKUse your own domain name
Point a DNS record at the instance, then set the name. The helper generates a new certificate for the name and the instance's addresses, pins nginx to those names so that 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, keeping the key root-only with mode 0600. Then 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.
Security notes
The admin API certificate generated at first boot is a server certificate only, so it cannot sign other certificates. Only the MITM CA can sign certificates, and it can sign server certificates only (pathlen:0). To confirm, run:
openssl x509 -in /etc/ssl/hoverfly/hoverfly.crt -noout -ext basicConstraints,extendedKeyUsageX509v3 Basic Constraints: critical CA:FALSE X509v3 Extended Key Usage: TLS Web Server AuthenticationHoverfly'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. Those keys are public, and the image never uses them: Hoverfly always starts with this instance's CA and refuses to start with a default one. The binaries are byte-identical to the files in the official release zip (hoverfly_bundle_linux_amd64.zip). The build record /usr/share/doc/sac-hoverfly/BUILD-INFO.txt lists the zip's SHA-256 together with each file's. Check them at any time:
cd / && sha256sum -c /usr/share/doc/sac-hoverfly/SHA256SUMSopt/hoverfly/bin/hoverfly: OK opt/hoverfly/bin/hoverctl: OK opt/hoverfly/LICENSE: OKKnown advisories with no reachable path. The build record lists the advisories that remain in the shipped binaries and explains why none is reachable. For example, the end-of-life dashboard is not served, and the middleware API is off. List them with:
grep -oE '^(GO-[0-9-]+( / GO-[0-9-]+)?|Embedded admin dashboard)' /usr/share/doc/sac-hoverfly/RESIDUALSGO-2026-6866 / GO-2026-6860 GO-2026-5932 GO-2025-3944 Embedded admin dashboardKeep it up to date
Ubuntu security updates install automatically. To apply everything now:
sudo apt-get update && sudo apt-get upgrade -yNew Hoverfly releases arrive as new versions of the AWS Marketplace listing. Launch the new version and copy your simulations across from /etc/hoverfly/simulations/.
Support
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
Hoverfly is developed by SpectoLabs and released under the Apache License 2.0. SecureAnyCloud is not affiliated with SpectoLabs. The developer and API tools guide covers the other versions in this listing.
Deploy on AWS
Launch Hoverfly 1.12.17 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com