OpenAPI Generator 7.26.0 on AWS User Guide
Overview
This guide covers launching and using the OpenAPI Generator 7.26.0 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.
OpenAPI Generator turns an OpenAPI (Swagger) specification into working code: client SDKs for 89 languages and frameworks, server stubs for 73, and documentation sets. This image runs OpenAPI Generator Online, the service form of the project: a REST API your build pipelines can call, plus an interactive console in the browser. You send it a specification (in the request body, or as a URL it fetches) and it returns a zip of generated code. Running it yourself keeps internal API descriptions inside your own network and gives every team the same generator version.
The cloudimg image locks the service down:
- HTTPS from the first boot. nginx serves the generator on port 443 with a certificate generated on your instance's first boot. Port 80 only redirects to HTTPS.
- A password gate in front of everything. The upstream service has no authentication at all. Every path (the console, the API, the OpenAPI document and the archive download) sits behind an HTTP Basic Auth prompt whose password is generated on your instance's first boot.
- No default login. The image contains no password, no certificate and no host name. First boot writes the gate's user name and a random 32 character password to
/var/lib/cloudimg/openapi-generator-credentials.txt(root only); nginx stores only a SHA-512 crypt hash, and removes the password from every request before the generator sees it. - Spec fetching cannot reach inside your AWS account. The generator fetches the specification URLs you give it, so its service is blocked at the kernel level from the EC2 instance metadata service (IPv4 and IPv6), from every private (RFC 1918) address in your VPC and from this instance's own loopback services. Public specification URLs keep working.
- Other web sites cannot act with your login. A browser re-sends the gate password to any site that asks, so nginx refuses every cross-site POST and every cross-site request to the download route (which deletes the archive it serves).
- A rebuilt, current web stack. The upstream jar is built on Spring Boot 2.5, which is end of life. The image rebuilds it with current same-line releases of its web libraries (Apache Tomcat 9.0.123, Spring Framework 5.3.39, Jackson 2.21.8, Logback 1.2.13, SnakeYAML 2.4 and Commons Lang 3.18.0), each pinned by checksum. The rebuilt jar also fixes the upstream jar's broken Python client generation.
- The generator runs locked in. It listens on
127.0.0.1:8080only, as its own unprivileged user, with a read only file system apart from its output directory, and it cannot read the gate password, the TLS key or the credentials file. Generated archives are cleaned up hourly once their download link has expired, with a 2 GB cap. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: OpenAPI Generator Online 7.26.0 (/opt/openapi-generator/), OpenJDK 21, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. Settings are in /etc/openapi-generator/; the build record is in /usr/share/doc/cloudimg-openapi-generator/.
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), TCP 443 (OpenAPI Generator) and TCP 80 (redirects to HTTPS). Open them to your own networks only.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick OpenAPI Generator 7.26.0 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=openapi-generator}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| OpenAPI Generator 7.26.0 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot takes about a minute: it generates the certificate and the password, then starts the generator and nginx. Check that everything is running:
systemctl is-active openapi-generator nginx openapi-generator-firstboot openapi-generator-egress-guard
sudo test -f /var/lib/cloudimg/openapi-generator-firstboot.done && echo FIRSTBOOT_DONE
active
active
active
active
FIRSTBOOT_DONE
Only SSH and nginx listen on the network; the generator itself listens on loopback:
sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | 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"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))
[::ffff:127.0.0.1]:8080 users:(("java"))
Step 4: Get your URL and password
Run sudo cat /var/lib/cloudimg/openapi-generator-credentials.txt to see your console URL, user name and password. The file is readable by root only and holds these settings, all generated for this instance, plus the SHA-256 fingerprint of its certificate:
sudo sed -n 's/^\(OAG_[A-Z0-9_]*\)=.*/\1/p' /var/lib/cloudimg/openapi-generator-credentials.txt
OAG_URL
OAG_HOST
OAG_AUTH_USER
OAG_AUTH_PASSWORD
OAG_TLS_CERT
OAG_TLS_SHA256_FINGERPRINT
The certificate is self-signed and generated on your instance. When your browser warns about it, compare the fingerprint it shows with OAG_TLS_SHA256_FINGERPRINT before you continue. Do not add the certificate to a trust store; to remove the warning, give the instance a domain name and a CA-issued certificate (Step 9).
Step 5: Use the console
Open OAG_URL (https://<public-ip>/) in a browser and sign in with OAG_AUTH_USER and OAG_AUTH_PASSWORD. The console lists every operation of the generation API:

Open POST /api/gen/clients/{language} to see the request it takes: the target language in the path, and a JSON body with either the specification itself (spec) or a URL to fetch it from (openAPIUrl), plus generator options:

TRY sends the request from your browser through the same password gate. Here it lists the client generators:

The console only ever loads this server's own API description; loading another specification into it is switched off.
Step 6: Generate code from the command line
The API is what you call from scripts and CI pipelines. These commands run on the instance. From your own machine use https://<public-ip> with your user name and password the same way. The helper writes the credentials into a curl config on the fly, so the password never appears on a command line:
H=$(sudo sed -n 's/^OAG_HOST=//p' /var/lib/cloudimg/openapi-generator-credentials.txt)
gate() { sudo awk -F= '/^OAG_AUTH_USER=/{u=$2} /^OAG_AUTH_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/cloudimg/openapi-generator-credentials.txt; }
OAG=(curl -s --cacert /etc/ssl/openapi-generator/openapi-generator.crt --resolve "$H:443:127.0.0.1")
"${OAG[@]}" -K <(gate) "https://$H/api/gen/clients" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(len(d), "client generators, including:", ", ".join(x for x in d if x in ("csharp","go","java","kotlin","python","rust","typescript-axios")))'
"${OAG[@]}" -K <(gate) "https://$H/api/gen/servers" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(len(d), "server generators, including:", ", ".join(x for x in d if x in ("aspnetcore","go-server","python-fastapi","spring")))'
89 client generators, including: csharp, go, java, kotlin, python, rust, typescript-axios
73 server generators, including: aspnetcore, go-server, python-fastapi, spring
A client from a published specification. Give the generator a URL; it fetches the specification, generates the client and answers with a download code. The download is a zip, and each code can be downloaded once:
H=$(sudo sed -n 's/^OAG_HOST=//p' /var/lib/cloudimg/openapi-generator-credentials.txt)
gate() { sudo awk -F= '/^OAG_AUTH_USER=/{u=$2} /^OAG_AUTH_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/cloudimg/openapi-generator-credentials.txt; }
OAG=(curl -s --cacert /etc/ssl/openapi-generator/openapi-generator.crt --resolve "$H:443:127.0.0.1")
cd "$(mktemp -d)"
"${OAG[@]}" -K <(gate) -H 'Content-Type: application/json' \
-d '{"openAPIUrl":"https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/_archive_/schemas/v3.0/pass/petstore.yaml","options":{"packageName":"petstore_client"}}' \
"https://$H/api/gen/clients/python" > response.json
CODE=$(python3 -c 'import json; print(json.load(open("response.json"))["code"])')
"${OAG[@]}" -K <(gate) -H 'Sec-Fetch-Site: same-origin' -o petstore-python.zip -w '%{http_code} %{content_type}\n' "https://$H/api/gen/download/$CODE"
unzip -l petstore-python.zip | awk '{print $4}' | grep -E '^python-client/petstore_client/(api|models)/[a-z_]+\.py$' | sort
unzip -p petstore-python.zip python-client/petstore_client/api/pets_api.py | grep -oE 'def (list_pets|create_pets|show_pet_by_id)\(' | sort -u
200 application/zip
python-client/petstore_client/api/__init__.py
python-client/petstore_client/api/pets_api.py
python-client/petstore_client/models/__init__.py
python-client/petstore_client/models/error.py
python-client/petstore_client/models/pet.py
def create_pets(
def list_pets(
def show_pet_by_id(
The download route only answers requests that come from this server's own pages or from tools that say so: send Sec-Fetch-Site: same-origin from scripts, as above. Browsers send it automatically from the console.
A server stub from your own specification. Send the specification in the request body instead ({"spec": ...}), here for a Go server:
H=$(sudo sed -n 's/^OAG_HOST=//p' /var/lib/cloudimg/openapi-generator-credentials.txt)
gate() { sudo awk -F= '/^OAG_AUTH_USER=/{u=$2} /^OAG_AUTH_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/cloudimg/openapi-generator-credentials.txt; }
OAG=(curl -s --cacert /etc/ssl/openapi-generator/openapi-generator.crt --resolve "$H:443:127.0.0.1")
cd "$(mktemp -d)"
cat > todo.json <<'JSON'
{
"openapi": "3.0.3",
"info": { "title": "Todo API", "version": "1.0.0" },
"paths": {
"/todos": {
"get": { "operationId": "listTodos", "tags": ["todos"],
"responses": { "200": { "description": "ok",
"content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Todo" } } } } } } }
}
},
"components": { "schemas": { "Todo": { "type": "object", "required": ["id", "title"],
"properties": { "id": { "type": "integer" }, "title": { "type": "string" }, "done": { "type": "boolean" } } } } }
}
JSON
python3 -c 'import json; print(json.dumps({"spec": json.load(open("todo.json"))}))' > body.json
CODE=$("${OAG[@]}" -K <(gate) -H 'Content-Type: application/json' -d @body.json "https://$H/api/gen/servers/go-server" | python3 -c 'import json,sys; print(json.load(sys.stdin)["code"])')
"${OAG[@]}" -K <(gate) -H 'Sec-Fetch-Site: same-origin' -o todo-go-server.zip -w '%{http_code}\n' "https://$H/api/gen/download/$CODE"
unzip -l todo-go-server.zip | awk '{print $4}' | grep -E '^go-server-server/(main\.go|go/(api_todos(_service)?|model_todo)\.go)$' | sort
200
go-server-server/go/api_todos.go
go-server-server/go/api_todos_service.go
go-server-server/go/model_todo.go
go-server-server/main.go
Every generator option the console lists for a language (GET /api/gen/clients/{language}) can be passed in options.
Step 7: What the front door refuses
Without the password every path answers 401. With it, another web site still cannot post to the generator or trigger a download in your browser, a specification URL that points back into the instance is refused, and plain HTTP only redirects:
H=$(sudo sed -n 's/^OAG_HOST=//p' /var/lib/cloudimg/openapi-generator-credentials.txt)
gate() { sudo awk -F= '/^OAG_AUTH_USER=/{u=$2} /^OAG_AUTH_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/cloudimg/openapi-generator-credentials.txt; }
OAG=(curl -s --cacert /etc/ssl/openapi-generator/openapi-generator.crt --resolve "$H:443:127.0.0.1")
"${OAG[@]}" -o /dev/null -w 'no password: %{http_code}\n' "https://$H/api/gen/clients"
"${OAG[@]}" -K <(gate) -o /dev/null -w 'cross-site POST: %{http_code}\n' -X POST -H 'Origin: https://evil.example' -H 'Content-Type: application/json' -d '{}' "https://$H/api/gen/clients/go"
"${OAG[@]}" -K <(gate) -o /dev/null -w 'cross-site download: %{http_code}\n' -H 'Sec-Fetch-Site: cross-site' "https://$H/api/gen/download/00000000-0000-0000-0000-000000000000"
"${OAG[@]}" -K <(gate) -o /dev/null -w 'spec URL on loopback: %{http_code}\n' -H 'Content-Type: application/json' -d '{"openAPIUrl":"http://127.0.0.1:8080/api-docs"}' "https://$H/api/gen/clients/go"
curl -s -o /dev/null -w 'plain http: %{http_code}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
no password: 401
cross-site POST: 403
cross-site download: 403
spec URL on loopback: 400
plain http: 301
The spec-fetch fence is checked by its own script, which also proves the check is real: the same request without the fence succeeds.
sudo /usr/local/sbin/openapi-generator-egress-check.sh
the service unit denies the metadata service (IPv4 and fd00:ec2::254) and every RFC1918 range, and allows loopback
inside that filter, as oagen: IMDSv2 token PUT 000, loopback 127.0.0.1:8080 and [::1] 000, a public host 200, DNS resolves
control: as root without the filter, the same loopback request answers 200
control: without the filter the IMDSv2 token PUT answers 200 — so the 000 above is the filter, not the network
OAG_EGRESS_OK
A specification URL on a private address in your VPC is refused the same way. If you need the generator to fetch specifications from an internal host, host them on a public URL or paste them into the request body instead.
Step 8: Run the self-test
The image ships a self-test that checks the listening ports, every refusal of the password gate, a full Python client generation and download with checks on the archive's contents, the spec-fetch fence and the self-test's own checks against known-bad inputs. It cleans up after itself, so you can run it at any time:
sudo /usr/local/sbin/openapi-generator-selftest.sh | grep -E '_OK$'
[ports] OAG_PORTS_OK
[auth] OAG_AUTH_OK
[generate] OAG_ROUNDTRIP_OK
[egress] OAG_EGRESS_OK
[gates] OAG_GATES_OK
OAG_SELFTEST_OK
Step 9: Use your own domain name
Point a DNS record at the instance, then set the name. The helper generates a new certificate for it, pins nginx to it (other host names are refused), and writes it into every download link the generator returns:
sudo /usr/local/sbin/openapi-generator-set-hostname <your-domain>
To use a CA-issued certificate, replace /etc/ssl/openapi-generator/openapi-generator.crt and .key with your own (keep the key root only, mode 0600) and run sudo systemctl reload nginx.
Step 10: Change the password
The helper reads the new password from standard input, so it never appears in your shell history or the process list. It refuses short, all-digit and well-known passwords, then proves the new password works and the old one is refused:
# type your <new-password> at the prompt
read -rs -p 'New password: ' NEWPW && printf '%s' "$NEWPW" | sudo /usr/local/sbin/openapi-generator-set-password.sh; unset NEWPW
Step 11: Security notes
The certificate generated at first boot is a server certificate only: it cannot sign other certificates.
openssl x509 -in /etc/ssl/openapi-generator/openapi-generator.crt -noout -ext basicConstraints,keyUsage,extendedKeyUsage
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
Known advisories with no fix on this release line. OpenAPI Generator Online 7.26.0 is built on Spring Framework 5.3 and Spring Boot 2.5, whose open source support has ended. The image moves every library to the newest release on its line, which clears most advisories. Ten advisories have no fix on these lines; each needs a feature this image does not use (Spring's HTTP invoker, XSLT views, functional routes, Spring Security, actuator endpoints, persistent sessions, server-side views or expression parsing of user input) or, for static file handling, is switched off because nginx serves the console files. The build checks each condition and fails if one changes. The list, with the reason for each, is in /usr/share/doc/cloudimg-openapi-generator/RESIDUALS:
grep -oE '^CVE-[0-9]{4}-[0-9]+(/[0-9]+)?' /usr/share/doc/cloudimg-openapi-generator/RESIDUALS
CVE-2016-1000027
CVE-2026-47884
CVE-2024-38816/38819
CVE-2026-41842
CVE-2026-41845
CVE-2026-41849/41850
CVE-2025-41249
CVE-2025-22235
CVE-2026-40973
CVE-2026-55760
The console is the RapiDoc page bundled with the jar. It does not include DOMPurify; it includes older copies of lodash and marked, so the image limits it to rendering this server's own API description and serves it with a Content-Security-Policy that allows only this server's scripts.
Generated code is yours: OpenAPI Generator is Apache-2.0 licensed, and the project states that generated code is not covered by its licence.
Step 12: Keep it up to date
Ubuntu security updates install automatically. To apply everything now:
sudo apt-get update && sudo apt-get -y upgrade
New OpenAPI Generator releases arrive as new versions of the AWS Marketplace listing: launch the new version and move your pipelines to it.
Support
- Email: support@cloudimg.co.uk
- Website: https://www.cloudimg.co.uk
The other versions on this listing are on the developer and API tools guide.