MockServer 9.2.0
Overview
MockServer mocks the HTTP and HTTPS APIs your code depends on. You define expectations (a request matcher and the response to send), point your application or test suite at MockServer, and then verify which requests your code actually sent. It can also act as a proxy that records, forwards and inspects real traffic, and it has a live web dashboard.
This SecureAnyCloud image, MockServer 9.2.0 on Ubuntu 24.04 for Amazon EC2, runs MockServer as a private, hardened service. It is one version of the SecureAnyCloud developer and API tools listing on AWS Marketplace, and you pick it as the software version when you subscribe and launch. Your tests then run against a fast, predictable fake of a third-party API that is slow, rate-limited, costly or not built yet.
What you get:
- HTTPS from the first boot. nginx serves MockServer at
https://<public-ip>/with a certificate generated on your instance's first boot. Port 80 only redirects to HTTPS. - Two separate locks. Your mocked API (every path outside
/mockserver/) needs this instance's API key in anX-Sac-Keyheader. MockServer's control plane (/mockserver/...: creating expectations, verifying, retrieving) and the dashboard need the admin login, which MockServer checks too. Without them, nginx answers401and MockServer never sees the request. - A private proxy. MockServer's own port,
1080(control plane, mocked API and proxy in one), listens on127.0.0.1only. You reach it through an SSH tunnel or from tests that run on the instance. It is never an open relay. - A TLS CA that is unique to your instance. To answer HTTPS through its proxy, MockServer signs certificates with a CA. MockServer's built-in CA has its private key published inside the MockServer jar, so the image does not use it. First boot generates a new CA on your instance (Step 9). Trust only this per-instance CA in your test clients. Its certificate (PEM) is
/etc/mockserver/mockserver-ca.crt. Never trust MockServer's default CA (CN=www.mockserver.com). - No default login. The image contains no key, password, certificate or host name. First boot writes them to
/var/lib/sac/data/mockserver-credentials.txt(root only). - No reach into your AWS account. Forwarded requests, the proxy and remote OpenAPI or JSON schema references make MockServer open connections on your behalf. The kernel blocks MockServer from the EC2 instance metadata service (IPv4 and IPv6), from every private and link-local range and from this instance's other loopback services. MockServer's own private-network block is on as well. Public addresses keep working.
- A locked-down runtime. MockServer runs as its own unprivileged Java service user with no capabilities, a read-only file system apart from its state directory, a memory cap, and no way to run any program but Java. Templates cannot load Java classes, JavaScript templates do not run, and the endpoints that would stop the server, open new ports, write files or load extensions are refused.
- Pinned versions. The official MockServer 9.2.0 jar from Maven Central, checked against its SHA-256 checksum, runs on Ubuntu's own OpenJDK 21 runtime.
- A fully patched base. Ubuntu 24.04 LTS is patched at build time, with unattended security upgrades switched on.
The image contains MockServer 9.2.0 (/opt/mockserver/), OpenJDK 21 (headless runtime), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. Settings and the startup expectations are in /etc/mockserver/, and the build record is in /usr/share/doc/sac-mockserver/.
- 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 (MockServer over HTTPS) and TCP 80 (redirects to HTTPS). Open them to your own networks only. Do not open port 1080. It does not listen on the network, and you reach the proxy through SSH.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick MockServer 9.2.0 on Ubuntu 24.04 as the software version, select your Region, and choose Launch through EC2.
Choose m5.large, your key pair, a security group as described above, and a subnet.
To launch with the AWS CLI instead, run the following. Replace the values in angle brackets with your own. <your-mgmt-cidr> is the network you connect from, for example 203.0.113.0/24:
aws ec2 create-security-group --region <region> --vpc-id <your-vpc> \
--group-name mockserver --description "MockServer: SSH and HTTPS from my network" for p in 22 80 443; do
aws ec2 authorize-security-group-ingress --region <region> --group-id <your-security-group> \
--protocol tcp --port $p --cidr <your-mgmt-cidr> done
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=mockserver}]'Connect to your instance
Version SSH user
MockServer 9.2.0 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot takes about a minute. It generates the certificate, the API key, the admin password and the TLS CA, then starts MockServer and nginx. Check that everything is running:
systemctl is-active mockserver nginx mockserver-firstboot mockserver-egress-guard
sudo test -f /var/lib/sac/data/mockserver-firstboot.done && echo FIRSTBOOT_DONEactive active active active FIRSTBOOT_DONEOnly SSH and nginx listen on the network. MockServer (port 1080) listens 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:1080 users:(("java")) [::]: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/mockserver-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/^\(MOCKSERVER_[A-Z0-9_]*\)=.*/\1/p' /var/lib/sac/data/mockserver-credentials.txtMOCKSERVER_URL MOCKSERVER_HOST MOCKSERVER_DASHBOARD_URL MOCKSERVER_ADMIN_USER MOCKSERVER_ADMIN_PASSWORD MOCKSERVER_GATE_HEADER MOCKSERVER_API_KEY MOCKSERVER_LOCAL_ADDRESS MOCKSERVER_CA_CERT MOCKSERVER_CA_SHA256_FINGERPRINT MOCKSERVER_TLS_CERT MOCKSERVER_TLS_SHA256_FINGERPRINTMOCKSERVER_URLishttps://<public-ip>/. Every path outside/mockserver/is your mocked API. SendMOCKSERVER_API_KEYin theX-Sac-Keyheader (named byMOCKSERVER_GATE_HEADER).MOCKSERVER_ADMIN_USER(admin) andMOCKSERVER_ADMIN_PASSWORDare the login for the control plane (https://<public-ip>/mockserver/...) and the dashboard (MOCKSERVER_DASHBOARD_URL).MOCKSERVER_LOCAL_ADDRESSis MockServer itself,127.0.0.1:1080on the instance, which is also its proxy.MOCKSERVER_CA_CERTandMOCKSERVER_CA_SHA256_FINGERPRINTare this instance's TLS CA (Step 9).MOCKSERVER_TLS_CERTandMOCKSERVER_TLS_SHA256_FINGERPRINTare the certificate nginx serves on port 443. It is self-signed. When a client or browser reports it, compare its SHA-256 fingerprint withMOCKSERVER_TLS_SHA256_FINGERPRINTbefore you trust the connection. Both fingerprints are the SHA-256 of the certificate in DER form, as lowercase hex.
The instance's private IP address and its EC2 DNS names are served too, so clients inside your VPC can use them.
Talk to MockServer
The commands in this guide run on the instance. Save the following 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 > ~/mockserver-env.sh <<'EOF' CF=/var/lib/sac/data/mockserver-credentials.txt
H=$(sudo sed -n 's/^MOCKSERVER_HOST=//p' $CF)
# ctl: MockServer's control plane (/mockserver/...) through nginx, with the admin login ctl() { curl -s --cacert /etc/ssl/mockserver/mockserver.crt --resolve "$H:443:127.0.0.1" \
-K <(sudo sed -n 's/^MOCKSERVER_ADMIN_PASSWORD=\(.*\)/user = "admin:\1"/p' $CF) "$@"; } # mock: your mocked API (every other path) through nginx, with this instance's API key mock() { curl -s --cacert /etc/ssl/mockserver/mockserver.crt --resolve "$H:443:127.0.0.1" \
-K <(sudo sed -n 's/^MOCKSERVER_API_KEY=\(.*\)/header = "X-Sac-Key: \1"/p' $CF) "$@"; }
# pxy: a request through MockServer's proxy on the instance, trusting only this instance's CA pxy() { curl -s -x http://127.0.0.1:1080 --cacert /etc/mockserver/mockserver-ca.crt "$@"; } EOFAsk MockServer for its status. jq -e prints the answer and fails if it is not what you expect:
. ~/mockserver-env.sh ctl -X PUT "https://$H/mockserver/status" | jq -e 'select(.version == "9.2.0" and .ports == [1080])'{ "artifactId": "mockserver-core", "gitHash": "789142c1", "groupId": "org.mock-server", "ports": [ 1080 ], "version": "9.2.0" }Without the login, nginx answers 401 with a Basic challenge. Without the key, the mocked API answers 401 with its own SacKey challenge:
. ~/mockserver-env.sh C=(curl -s --cacert /etc/ssl/mockserver/mockserver.crt --resolve "$H:443:127.0.0.1") "${C[@]}" -o /dev/null -w 'no login: %{http_code} %header{www-authenticate}\n' -X PUT "https://$H/mockserver/status" "${C[@]}" -o /dev/null -w 'no key: %{http_code} %header{www-authenticate}\n' "https://$H/sac-sample/widgets"no login: 401 Basic realm="MockServer (sac)", charset="UTF-8" no key: 401 SacKey realm="MockServer", header="X-Sac-Key"Call the sample API
The image starts with three sample expectations for a made-up widgets API under /sac-sample/. They load at every start from /etc/mockserver/initialization.json, and a copy is in /usr/share/doc/sac-mockserver/sac-sample-expectations.json. Run the following. Every answer comes from MockServer:
. ~/mockserver-env.sh mock "https://$H/sac-sample/widgets" | jq -e 'select(.[0].name == "Acme Sprocket")' mock "https://$H/sac-sample/widgets/7" | jq -e 'select(.id == 7)' mock -H 'Content-Type: application/json' -d '{"name":"Gear"}' "https://$H/sac-sample/widgets" | jq -e 'select(.created == true)' mock -o /dev/null -w 'no expectation for this path: %{http_code}\n' "https://$H/nothing/here"[ { "id": 1, "name": "Acme Sprocket", "color": "blue" }, { "id": 2, "name": "Acme Cog", "color": "green" } ] { "id": 7, "method": "GET", "servedBy": "velocity template" } { "id": 3, "created": true } no expectation for this path: 404The second answer comes from a Velocity template, in which MockServer copies the {id} path parameter into the response. A request that matches no expectation gets 404.
Create, verify and clear your own expectations
An expectation is a JSON document with an httpRequest matcher and an httpResponse. Create one with PUT /mockserver/expectation and call it. Then check how often your code called it with PUT /mockserver/verify. A 202 response means the verification passed, and 406 means it failed, with the reason. This example mocks a small Task API:
. ~/mockserver-env.sh cd "$(mktemp -d)" cat > task-expectation.json <<'JSON' { "id": "task-list", "httpRequest": {"method": "GET", "path": "/tasks"}, "httpResponse": { "statusCode": 200, "headers": {"Content-Type": ["application/json"]}, "body": {"type": "JSON", "json": [{"id": 1, "title": "Write the expectation", "done": true}]} } } JSON ctl -X PUT -H 'Content-Type: application/json' --data-binary @task-expectation.json "https://$H/mockserver/expectation" | jq -e '.[0].id' mock "https://$H/tasks" | jq -e 'select(.[0].title == "Write the expectation")' mock -o /dev/null "https://$H/tasks" ctl -X PUT -H 'Content-Type: application/json' -d '{"httpRequest":{"path":"/tasks"},"times":{"atLeast":2}}' -o /dev/null -w 'verify at least 2 calls: %{http_code}\n' "https://$H/mockserver/verify" ctl -X PUT -H 'Content-Type: application/json' -d '{"httpRequest":{"path":"/tasks"},"times":{"atLeast":5}}' -o /dev/null -w 'verify at least 5 calls: %{http_code}\n' "https://$H/mockserver/verify""task-list" [ { "id": 1, "title": "Write the expectation", "done": true } ] verify at least 2 calls: 202 verify at least 5 calls: 406Without -o /dev/null, a failed verification explains itself. It shows how many matching requests MockServer recorded and the closest requests that did not match.
MockServer records every request it receives. Retrieve them to see exactly what your code sent. nginx removes the X-Sac-Key header before the request reaches MockServer, so the key is never recorded:
. ~/mockserver-env.sh ctl -X PUT -H 'Content-Type: application/json' -d '{"path":"/tasks"}' "https://$H/mockserver/retrieve?type=REQUESTS&format=JSON" | jq -e 'length >= 2' ctl -X PUT -H 'Content-Type: application/json' -d '{"path":"/tasks"}' "https://$H/mockserver/retrieve?type=REQUESTS&format=JSON" | jq -e '[.[].headers | keys[] | ascii_downcase] | any(. == "x-sac-key") | not'true trueRemove one expectation by its id with PUT /mockserver/clear?type=EXPECTATIONS. The sample expectations stay:
. ~/mockserver-env.sh ctl -X PUT -H 'Content-Type: application/json' -d '{"id":"task-list"}' -o /dev/null -w 'clear: %{http_code}\n' "https://$H/mockserver/clear?type=EXPECTATIONS" mock -o /dev/null -w 'after clear: %{http_code}\n' "https://$H/tasks" ctl -X PUT "https://$H/mockserver/retrieve?type=ACTIVE_EXPECTATIONS" | jq -r '.[].id'clear: 200 after clear: 404 sac-sample-list-widgets sac-sample-get-widget sac-sample-create-widgetMockServer keeps expectations and recorded requests in memory. PUT /mockserver/reset removes everything, the samples included, and a restart reloads /etc/mockserver/initialization.json.
To keep your own expectations across restarts, add them to that file as a JSON array of expectations (sudo install -m 0644 my-expectations.json /etc/mockserver/initialization.json), then run sudo systemctl restart mockserver. Request bodies are limited to 10 MB. See the MockServer documentation for request matchers, response templates, delays, callbacks and OpenAPI import.
Open the dashboard
MockServer's dashboard shows the log, the active expectations, the requests it received and the requests it proxied, live. Open MOCKSERVER_DASHBOARD_URL (https://<public-ip>/mockserver/dashboard) in your browser and sign in as admin with MOCKSERVER_ADMIN_PASSWORD.
The browser warns about the self-signed certificate. Check that its SHA-256 fingerprint matches MOCKSERVER_TLS_SHA256_FINGERPRINT before you continue, or use your own domain and certificate (Step 15).
Open Dashboard shows the live view. The log on the left includes a forwarded request that MockServer's private-network block refused (Step 10).
Click an expectation to see its JSON.
The dashboard works only on this instance's own address, so other web sites cannot drive it. Its "Proxied Requests" hint about sending proxy traffic to port 443 does not apply here. The proxy is 127.0.0.1:1080 through SSH (Step 11).
HTTPS through the proxy and your instance's CA
Point an application at MockServer as its HTTP proxy, and MockServer answers with your expectations, even for host names that do not exist. For HTTPS, MockServer 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 CA is unique to your instance. MockServer's built-in CA is the same for every MockServer user, and its private key is published inside the MockServer jar. A client that trusts it would accept a certificate for any web site from anyone. This image never uses it, and MockServer refuses to start with it. First boot generates a new CA on your instance (RSA 3072, valid for 10 years, allowed to sign server certificates only):
- The certificate (public, safe to copy) is
/etc/mockserver/mockserver-ca.crt. Its SHA-256 fingerprint isMOCKSERVER_CA_SHA256_FINGERPRINTin the credentials file. - The private key never leaves the instance. It is
/etc/mockserver/ca/ca.key, mode 0600, owned by themockserverservice 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. The per-host certificates MockServer signs with it are kept in memory only.
openssl x509 -in /etc/mockserver/mockserver-ca.crt -noout -subject -ext basicConstraints | sed -E 's/CA [0-9a-f]{8}$/CA <id>/'
sudo stat -c '%n %a %U:%G' /etc/mockserver/ca/ca.key /etc/mockserver/ca
sudo grep -c 'PRIVATE KEY' /var/lib/sac/data/mockserver-credentials.txt || truesubject=O = sac MockServer, CN = MockServer per-instance CA <id> X509v3 Basic Constraints: critical CA:TRUE, pathlen:0 /etc/mockserver/ca/ca.key 600 mockserver:mockserver /etc/mockserver/ca 750 root:mockserver 0Send requests for the sample host api.sac-sample.test (which does not exist) through the proxy. Use plain HTTP, HTTPS verified against this CA only (--cacert, with the host name checked), and HTTP/2:
. ~/mockserver-env.sh pxy http://api.sac-sample.test/sac-sample/widgets | jq -e 'select(.[0].name == "Acme Sprocket")' >/dev/null && echo "http: sample" pxy https://api.sac-sample.test/sac-sample/widgets | jq -e 'select(.[1].name == "Acme Cog")' >/dev/null && echo "https: sample" pxy --http2 -o /dev/null -w 'https over HTTP/%{http_version}: %{http_code}\n' https://api.sac-sample.test/sac-sample/widgetshttp: sample https: sample https over HTTP/2: 200Run mockserver-ca-presented to see which CA MockServer actually presents. It opens TLS connections through the proxy and directly, reads the certificate chain MockServer sends, and checks that the issuing CA is this instance's CA and not MockServer's public default CA:
sudo mockserver-ca-presentedMOCKSERVER_CA_INSTANCE_SHA256=bd3e725e24e1cb1784796058a93ad79e90de439f3dd58989e8a63e6d7b348698 MOCKSERVER_CA_PRESENTED_PROXY_SHA256=bd3e725e24e1cb1784796058a93ad79e90de439f3dd58989e8a63e6d7b348698 MOCKSERVER_CA_PRESENTED_DIRECT_SHA256=bd3e725e24e1cb1784796058a93ad79e90de439f3dd58989e8a63e6d7b348698 MOCKSERVER_CA_PROOF_OKThe fingerprint differs on every instance.
What the proxy does with requests it has no expectation for. A plain HTTP request gets 404. An HTTPS request through the proxy (an HTTP CONNECT tunnel) is forwarded to the real host, so MockServer works as a recording proxy for HTTPS. You see the traffic in the dashboard and in retrieve. The outbound fence (Step 10) still applies, so only public addresses are reachable:
. ~/mockserver-env.sh pxy -o /dev/null -w 'unmatched plain http: %{http_code}\n' http://example.com/ pxy -o /dev/null -w 'unmatched https, forwarded: %{http_code}\n' https://example.com/unmatched plain http: 404 unmatched https, forwarded: 200Trust 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 MOCKSERVER_CA_SHA256_FINGERPRINT:
scp -i <your-key.pem> ubuntu@<public-ip>:/etc/mockserver/mockserver-ca.crt ./mockserver-ca.crt openssl x509 -in mockserver-ca.crt -outform der | sha256sum- curl and most command-line tools: pass the file, as above:
curl --cacert mockserver-ca.crt .... Many tools also read an environment variable:REQUESTS_CA_BUNDLE=mockserver-ca.crtfor Pythonrequests,NODE_EXTRA_CA_CERTS=mockserver-ca.crtfor Node.js, andSSL_CERT_FILEfor many others. For Java, import the certificate into a test trust store (keytool -importcert -alias mockserver -file mockserver-ca.crt -keystore mockserver-test.jks) and start the JVM with-Djavax.net.ssl.trustStore=mockserver-test.jks. - The system trust store (a Linux test machine or CI runner, on Ubuntu and Debian): name the file after the instance so you can remove it later:
sudo cp mockserver-ca.crt /usr/local/share/ca-certificates/mockserver-<public-ip>.crt
sudo update-ca-certificates
# when you are done:
sudo rm /usr/local/share/ca-certificates/mockserver-<public-ip>.crt && sudo update-ca-certificates --fresh- 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 mockserver-ca.crt. - Windows: import the certificate into Trusted Root Certification Authorities with
certutil -addstore -f Root mockserver-ca.crtfrom an administrator prompt. - A browser: in Firefox, open Settings > Privacy & Security > Certificates > View Certificates > Authorities > Import, choose
mockserver-ca.crtand tick Trust this CA to identify websites. Then set the browser's HTTP and HTTPS proxy to127.0.0.1port1080through the SSH tunnel in Step 11. 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 mockserver-mint-ca (Step 14) and remove the old certificate from your trust stores.
Forward to a real API, and the outbound fence
An expectation can forward a request to a real server instead of answering it. Use this to put MockServer in front of a real API so that you can record and inspect the traffic. The following expectation forwards /example to https://example.com/:
. ~/mockserver-env.sh ctl -X PUT -H 'Content-Type: application/json' -o /dev/null -w 'forward expectation: %{http_code}\n' \
-d '{"id":"example-forward","httpRequest":{"path":"/example"},"httpOverrideForwardedRequest":{"httpRequest":{"path":"/","headers":{"Host":["example.com"]},"secure":true,"socketAddress":{"host":"example.com","port":443,"scheme":"HTTPS"}}}}' \
"https://$H/mockserver/expectation" mock "https://$H/example" | grep -o '<title>[^<]*</title>' ctl -X PUT -H 'Content-Type: application/json' -o /dev/null -w 'clear: %{http_code}\n' -d '{"id":"example-forward"}' "https://$H/mockserver/clear?type=EXPECTATIONS"forward expectation: 201 <title>Example Domain</title> clear: 200A forward to the EC2 instance metadata service, to a private address in your VPC or to another service on this instance never connects. MockServer's own private-network block refuses it (the WARN ... blocked by SSRF policy line in the dashboard log above). Underneath that block, the kernel refuses every connection from MockServer's service account to those ranges:
. ~/mockserver-env.sh ctl -X PUT -H 'Content-Type: application/json' -o /dev/null -w 'metadata forward expectation: %{http_code}\n' \
-d '{"id":"imds-forward","httpRequest":{"path":"/imds"},"httpOverrideForwardedRequest":{"httpRequest":{"path":"/latest/meta-data/","headers":{"Host":["169.254.169.254"]},"socketAddress":{"host":"169.254.169.254","port":80,"scheme":"HTTP"}}}}' \
"https://$H/mockserver/expectation" mock -o /dev/null -m 20 -w 'forwarded to the metadata service: %{http_code}\n' "https://$H/imds" ctl -X PUT -H 'Content-Type: application/json' -o /dev/null -w 'clear: %{http_code}\n' -d '{"id":"imds-forward"}' "https://$H/mockserver/clear?type=EXPECTATIONS"metadata forward expectation: 201 forwarded to the metadata service: 502 clear: 200You can see the fence in MockServer's 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, and MockServer may reach only the local DNS resolver and its own port on loopback:
systemctl show -p IPAddressDeny --value mockserver | tr ' ' '\n' | sort
sudo nft list table inet sac_mockserver_egress | grep -oE '(oifname "lo"|ip6? daddr \{).*(accept|reject.*)' | sed -E 's/counter packets [0-9]+ bytes [0-9]+ //'0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 168.63.129.16/32 169.254.0.0/16 172.16.0.0/12 192.0.0.0/24 192.168.0.0/16 198.18.0.0/15 224.0.0.0/4 240.0.0.0/4 64:ff9b::/96 ::/127 fc00::/7 fe80::/10 ff00::/8 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" ip daddr 127.0.0.1 tcp dport 1080 accept oifname "lo" ct state new reject ip daddr { 0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.168.0.0/16, 198.18.0.0/15, 224.0.0.0/3 } reject with icmp port-unreachable ip6 daddr { ::/127, 64:ff9b::/96, fc00::/7, fe80::/10, ff00::/8 } reject with icmpv6 port-unreachableTo mock or record an API that lives on a private address inside your VPC, run that test from a MockServer whose network policy you control. This image is built never to reach into the network it runs in.
Use MockServer from your own machine
Your mocked API and the control plane are served on port 443. Copy the server certificate and check its fingerprint against MOCKSERVER_TLS_SHA256_FINGERPRINT. Then put the key and the password in curl config files, which keeps them out of your shell history:
scp -i <your-key.pem> ubuntu@<public-ip>:/etc/ssl/mockserver/mockserver.crt ./mockserver-server.crt openssl x509 -in mockserver-server.crt -outform der | sha256sum printf 'header = "X-Sac-Key: %s"\n' '<MOCKSERVER_API_KEY>' > mockserver-key.cfg && chmod 600 mockserver-key.cfg printf 'user = "admin:%s"\n' '<MOCKSERVER_ADMIN_PASSWORD>' > mockserver-admin.cfg && chmod 600 mockserver-admin.cfg curl --cacert mockserver-server.crt -K mockserver-key.cfg https://<public-ip>/sac-sample/widgets curl --cacert mockserver-server.crt -K mockserver-admin.cfg -X PUT https://<public-ip>/mockserver/statusMockServer's Java, Node.js and other client libraries take a host, a port and these headers. Most test frameworks let you set a default header (the key) and HTTP Basic credentials (the login) on their HTTP client.
The proxy is not open to the network. Forward it over SSH, then point your client or test suite at 127.0.0.1:1080 as its HTTP and HTTPS proxy, and trust this instance's CA (Step 9):
ssh -i <your-key.pem> -N -L 1080:127.0.0.1:1080 ubuntu@<public-ip> & curl -x http://127.0.0.1:1080 --cacert mockserver-ca.crt https://api.sac-sample.test/sac-sample/widgetsThrough the tunnel you reach MockServer directly. Control-plane calls to http://127.0.0.1:1080/mockserver/... need the admin login (MockServer checks it itself), but mocked API calls need no key. Anyone who can SSH to the instance can use the tunnel, so keep SSH restricted to your own networks.
What the front door refuses
Without the key, every mocked API path answers 401, and a wrong key gets the same 401. There is no lockout to trip, and the key is 192 random bits. The control plane needs the login, not the key.
Even with the login, the endpoints that would stop MockServer, open a port or change its settings are refused. Another web site cannot create expectations, TRACE and compressed bodies are refused, bodies are limited to 10 MB, plain HTTP only redirects and unknown host names get no answer:
. ~/mockserver-env.sh C=(curl -s --cacert /etc/ssl/mockserver/mockserver.crt --resolve "$H:443:127.0.0.1") "${C[@]}" -o /dev/null -w 'wrong key: %{http_code}\n' -K <(printf 'header = "X-Sac-Key: %s"\n' "$(openssl rand -hex 24)") "https://$H/sac-sample/widgets" mock -o /dev/null -w 'control plane with the key but no login: %{http_code}\n' -X PUT "https://$H/mockserver/status" ctl -o /dev/null -w 'stop: %{http_code}\n' -X PUT "https://$H/mockserver/stop" ctl -o /dev/null -w 'bind a new port: %{http_code}\n' -X PUT -d '{"ports":[2000]}' "https://$H/mockserver/bind" ctl -o /dev/null -w 'change the configuration: %{http_code}\n' -X PUT -H 'Content-Type: application/json' -d '{}' "https://$H/mockserver/configuration" ctl -o /dev/null -w 'read the configuration: %{http_code}\n' "https://$H/mockserver/configuration" ctl -o /dev/null -w 'cross-site expectation: %{http_code}\n' -X PUT -H 'Sec-Fetch-Site: cross-site' -H 'Content-Type: application/json' -d '{}' "https://$H/mockserver/expectation" ctl -o /dev/null -w 'other origin: %{http_code}\n' -X PUT -H 'Origin: https://evil.example' -H 'Content-Type: application/json' -d '{}' "https://$H/mockserver/expectation" mock -o /dev/null -w 'TRACE: %{http_code}\n' -X TRACE "https://$H/sac-sample/widgets" mock -o /dev/null -w 'compressed body: %{http_code}\n' -H 'Content-Encoding: gzip' -H 'Content-Type: application/json' -d 'x' "https://$H/sac-sample/widgets" head -c 11000000 /dev/zero | mock -o /dev/null -w 'an 11 MB body: %{http_code}\n' -H 'Content-Type: application/octet-stream' --data-binary @- "https://$H/sac-sample/widgets" curl -s -o /dev/null -w 'plain http: %{http_code} %header{location}\n' --resolve "$H:80:127.0.0.1" "http://$H/sac-sample/widgets" curl -sk -o /dev/null -w 'unknown host name: %{http_code}\n' --resolve "other.example:443:127.0.0.1" https://other.example/ || truewrong key: 401 control plane with the key but no login: 401 stop: 403 bind a new port: 403 change the configuration: 403 read the configuration: 200 cross-site expectation: 403 other origin: 403 TRACE: 405 compressed body: 415 an 11 MB body: 413 plain http: 301 https://<public-ip>/sac-sample/widgets unknown host name: 000nginx logs every request with its status and its path only: no query string, no headers and no credentials.
Run the self-test
The image ships a self-test. It checks every refusal of the front door (including 25 wrong keys followed by the right one), the admin login at nginx and at MockServer, the refused endpoints, the sample API, HTTPS through the proxy against this instance's CA, the CA MockServer presents, the dashboard, the listening ports and the certificate:
sudo /usr/local/sbin/mockserver-selftestMOCKSERVER_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 mockserver-rotate-keymockserver-rotate-key: a new API key is in force; read it with:
sudo grep MOCKSERVER_API_KEY= /var/lib/sac/data/mockserver-credentials.txtA new admin password restarts MockServer, so the expectations and recorded requests in memory are replaced by those in initialization.json. Save yours first with PUT /mockserver/retrieve?type=ACTIVE_EXPECTATIONS:
sudo mockserver-rotate-passwordmockserver-rotate-password: the admin password is in force; read it with:
sudo grep MOCKSERVER_ADMIN_PASSWORD= /var/lib/sac/data/mockserver-credentials.txtA new CA also restarts MockServer. Replace the old certificate in your trust stores with the new /etc/mockserver/mockserver-ca.crt. Run:
sudo mockserver-mint-ca | sed -E 's/[0-9a-f]{64}$/<new-fingerprint>/'
sudo mockserver-ca-presented | tail -n 1mockserver-mint-ca: CA SHA-256 fingerprint (DER) <new-fingerprint> MOCKSERVER_CA_PROOF_OKUse your own domain name
Point a DNS record at the instance, then set the name with the helper below. It 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 where MockServer is now served:
sudo mockserver-set-hostname <your-domain>To use a CA-issued certificate instead, replace /etc/ssl/mockserver/mockserver.crt and /etc/ssl/mockserver/mockserver.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 mockserver-set-hostname with the new address, or use an Elastic IP.
Security notes
The server certificate generated at first boot is a server certificate only. It cannot sign other certificates. Only the instance CA can, and it can sign server certificates only (pathlen:0). Check it with:
openssl x509 -in /etc/ssl/mockserver/mockserver.crt -noout -ext basicConstraints,extendedKeyUsageX509v3 Basic Constraints: critical CA:FALSE X509v3 Extended Key Usage: TLS Web Server AuthenticationMockServer's public default CA. The official MockServer jar contains the certificate and private key of MockServer's public default CA (CN=www.mockserver.com), as every copy of MockServer does. They are public, and the image never uses them: MockServer always starts with this instance's CA and refuses to start with the default one. That CA is not trusted anywhere on the instance, neither in the system trust store nor in Java's.
The jar is the unmodified file from Maven Central, and the build record /usr/share/doc/sac-mockserver/BUILD-INFO.txt lists its SHA-256. Verify it at any time:
cd / && sha256sum -c /usr/share/doc/sac-mockserver/SHA256SUMSopt/mockserver/mockserver-netty-9.2.0-jar-with-dependencies.jar: OK opt/mockserver/LICENSE: OKKnown advisories. The build record lists the vulnerability scans of the jar and its bundled libraries at build time, and the MockServer security advisories fixed in 9.2.0. List them with:
grep -oE 'GHSA-[a-z0-9-]+' /usr/share/doc/sac-mockserver/BUILD-INFO.txtGHSA-7pwj-xvc2-hfpc GHSA-xcr9-3j63-6cgq GHSA-52vf-hgvq-8pr3 GHSA-qrq7-fxf6-47p7 GHSA-jg86-hv4g-8h78Keep it up to date
Ubuntu security updates, OpenJDK included, install automatically. To apply all updates now, run:
sudo apt-get update && sudo apt-get upgrade -yNew MockServer releases arrive as new versions of the AWS Marketplace listing. Launch the new version and copy your expectations from /etc/mockserver/initialization.json across.
Support
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
MockServer is developed by James D Bloom and contributors and released under the Apache License 2.0. SecureAnyCloud is not affiliated with the MockServer project. The other versions on this listing are on the developer and API tools guide.
Deploy on AWS
Launch MockServer 9.2.0 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