Deployment guide

Altair GraphQL Client 8.5.11

Overview

Altair GraphQL Client is an open-source GraphQL IDE. You use it to build, send and inspect GraphQL queries, mutations and subscriptions, with autocompletion and inline documentation from live schema introspection. This image runs Altair 8.5.11 on Ubuntu 24.04 on Amazon EC2 and puts it behind HTTPS and a login, so your team gets a private IDE. It is one version of the SecureAnyCloud developer and API tools listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.

The self-hosted build is a static web application. Your browser sends every GraphQL request straight to the endpoint you enter, and the server only hands out the application files. There is no backend and no database, and nothing runs except a web server.

Upstream self-hosted Altair is a public web page with no login. What you get: the SecureAnyCloud image turns it into a private IDE.

  • Release 8.5.11. Altair 8.5.7 and earlier are affected by GHSA-752m-7p75-49gf, where a crafted plugin sandbox URL could run attacker JavaScript in the web site that serves Altair. Release 8.5.11, the newest, carries the fix.
  • Plugin sandbox kept off your instance. The instance does not serve Altair's plugin sandbox page at all. Altair runs any plugin you add in the Altair project's own hosted sandbox, a separate web origin, so plugin code can never run in your instance's origin.
  • HTTPS from the first boot. nginx serves Altair on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
  • A password unique to your instance. HTTP Basic Auth is required for the user admin. The password is generated on first boot and written to /var/lib/sac/data/altair-credentials.txt (root only). nginx stores only a bcrypt hash of it, and no password is baked into the image.
  • Fail closed. nginx refuses to start until first boot has generated the certificate and the password, so the IDE is never served without a login.
  • Hardened defaults. The instance drops requests for any host name it does not serve and accepts only GET and HEAD. It refuses cross-site requests and framing. nginx runs in a systemd sandbox and is blocked from the instance metadata service, and the AMI requires IMDSv2. The optional Altair cloud account is switched off, so no account token is ever stored for your instance's address.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Altair GraphQL Client 8.5.11 (the project's own altair-static build in /var/www/altair, checked file by file against the published package), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. There is no Node.js runtime.

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 with a route to the internet (or a private route you use for management).
  • A security group that allows TCP 22 (SSH), TCP 443 (Altair) and TCP 80 (redirects to HTTPS) from your own networks only.
  • GraphQL APIs that are reachable from your browser and allow requests from your Altair address (CORS). The browser sends the requests.
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Altair GraphQL Client 8.5.11 on Ubuntu 24.04 as the software version, select your Region, then choose Launch through EC2.

Choose m5.large, your key pair and a security group as described above. Altair itself needs very little, because the work happens in your users' browsers.

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=altair}]'
2

Connect to your instance

Connect over SSH as the ubuntu user, which is the SSH user for Altair GraphQL Client 8.5.11 on Ubuntu 24.04:

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services

On first boot, the instance generates the certificate and the password, then starts nginx. Check both units and the first boot marker:

systemctl is-active nginx altair-graphql-firstboot
sudo test -f /var/lib/sac/data/altair-graphql-firstboot.done && echo FIRSTBOOT_DONE

Both units are active and the marker is present:

active active FIRSTBOOT_DONE

Confirm that only SSH and nginx listen on the network:

sudo ss -lntp | awk 'NR>1 {print $4}' | grep -v '^127\.0\.0\.5' | sort
0.0.0.0:22 0.0.0.0:443 0.0.0.0:80 [::]:22 [::]:443 [::]:80
4

Get your URL and password

Read the credentials file on the instance:

sudo cat /var/lib/sac/data/altair-credentials.txt

Only root can read the file. It holds the URL, the user name and this instance's password:

ALTAIR_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ ALTAIR_USERNAME=admin ALTAIR_PASSWORD=<ALTAIR_PASSWORD>

The URL uses the instance's public DNS name from EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too. To serve Altair under your own DNS name, go to Step 9.

5

Sign in from your browser

Open the ALTAIR_URL in your browser. The certificate is self-signed, so your browser asks you to confirm it the first time. You can install your own certificate instead (Step 9).

Sign in as admin with the password from Step 4. The browser remembers the login for the session.

Altair opens as a full GraphQL IDE with these areas:

  • The URL bar, with the HTTP method, at the top.
  • The query editor on the left, with tabs for Query, Pre-request, Post-request and Auth.
  • The Result pane on the right.
  • A left rail for headers, request handlers, history, extensions and collections. The cog at the top right opens the settings.

A banner may offer the optional Altair AI plugin. You can close it.

6

Send a query

Enter your GraphQL endpoint in the URL bar. This walkthrough uses the public demo API https://countries.trevorblades.com/. Altair introspects the schema, so the editor autocompletes field names.

Type a query and press Send Request (or Ctrl+Enter):

query CountryLookup { country(code: "GB") { name native capital currency languages { name } } }

The result appears on the right with the HTTP status and the time taken.

Click Docs to open the schema explorer built from introspection. It lists the root types and every field with its arguments and description, and it has a search box.

7

Environments and scripts

Pre-request scripts run in your browser before each request. On the Pre-request tab, tick Enable pre-request script.

In this example, the script sets the environment value code. The query uses it as country(code: "{{code}}"), so the same query now returns Japan:

altair.helpers.setEnvironment("code", "JP"); altair.log("sac pre-request script ran");

Use environments (the No environment menu at the top) to keep base URLs and tokens for each of your APIs. Use the Auth tab or Set Headers to send an Authorization header.

Your queries, environments and history are stored in your own browser, for this instance's address only. Nothing is stored on the server.

8

Check the security posture from the command line

Run these checks on the instance. Without a login, the IDE is refused. Port 80 redirects to HTTPS, and the health check stays open for load balancers. A request for a host name the instance does not serve is dropped without an answer.

H=$(sudo grep '^ALTAIR_URL=' /var/lib/sac/data/altair-credentials.txt | sed -E 's#^ALTAIR_URL=https://([^/]+)/.*#\1#')
curl -s -o /dev/null -w 'http:// -> %{http_code} %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -o /dev/null -w 'no login -> HTTP %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/"
curl -sk -o /dev/null -w 'health -> HTTP %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/healthz"
curl -sk -o /dev/null -w 'unknown host -> %{http_code}\n' --resolve "evil.example:443:127.0.0.1" "https://evil.example/" || true
curl -sk --resolve "$H:443:127.0.0.1" "https://$H/" | grep -o '401 Authorization Required'
http:// -> 301 https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ no login -> HTTP 401 health -> HTTP 200 unknown host -> 000 401 Authorization Required

000 means nginx closed the connection without answering, which is how it treats any host name it does not serve.

With the password, Altair is served and the plugin sandbox page is not. The sandbox returns HTTP 404, including at the address used by the GHSA-752m-7p75-49gf proof of concept. The command passes the password to curl on standard input, so it never appears in the process list or your shell history:

sudo bash -c ' F=/var/lib/sac/data/altair-credentials.txt P=$(grep "^ALTAIR_PASSWORD=" $F | cut -d= -f2-) H=$(grep "^ALTAIR_URL=" $F | sed -E "s#^ALTAIR_URL=https://([^/]+)/.*#\1#") auth() { printf "user = \"admin:%s\"\n" "$P"; } auth | curl -K - -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w "with login -> HTTP %{http_code}\n" "https://$H/" auth | curl -K - -sk --resolve "$H:443:127.0.0.1" "https://$H/" | grep -o "<title>[^<]*</title>" auth | curl -K - -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w "plugin sandbox -> HTTP %{http_code}\n" "https://$H/iframe-sandbox/?sandbox_type=plugin" auth | curl -K - -skI --resolve "$H:443:127.0.0.1" "https://$H/" | grep -iE "^(x-frame-options|content-security-policy):" '
with login -> HTTP 200 <title>Altair</title> plugin sandbox -> HTTP 404 x-frame-options: SAMEORIGIN content-security-policy: frame-ancestors 'self'

nginx keeps only a bcrypt hash of the password. The plain text is in the credentials file, which only root can read:

sudo sed -E 's/\$2y\$12\$.*/$2y$12$<bcrypt-hash>/' /etc/nginx/.altair-htpasswd
sudo stat -c '%a %U:%G %n' /etc/nginx/.altair-htpasswd /var/lib/sac/data/altair-credentials.txt
admin:$2y$12$<bcrypt-hash> 640 root:www-data /etc/nginx/.altair-htpasswd 600 root:root /var/lib/sac/data/altair-credentials.txt

The certificate was generated on this instance and names its own addresses:

H=$(sudo grep '^ALTAIR_URL=' /var/lib/sac/data/altair-credentials.txt | sed -E 's#^ALTAIR_URL=https://([^/]+)/.*#\1#')
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
subject=CN = ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost

nginx cannot reach the instance metadata service, and it does not start until first boot has finished:

systemctl show nginx -p IPAddressDeny | grep 169.254.169.254
systemctl cat nginx | grep -E '^ExecStartPre=/usr/bin/test'
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32 ExecStartPre=/usr/bin/test -f /var/lib/sac/data/altair-graphql-firstboot.done ExecStartPre=/usr/bin/test -s /etc/nginx/.altair-htpasswd

nginx only serves files and forwards nothing. A request that tries to use it as a proxy to the metadata address gets no answer, and never returns instance metadata:

curl -s -o /dev/null -w 'proxy request for 169.254.169.254 -> %{http_code}\n' -x http://127.0.0.1:80 http://169.254.169.254/latest/meta-data/ || true
proxy request for 169.254.169.254 -> 000

nginx also refuses a request whose Origin is another web site (HTTP 403), because browsers resend Basic Auth credentials on cross-site requests:

H=$(sudo grep '^ALTAIR_URL=' /var/lib/sac/data/altair-credentials.txt | sed -E 's#^ALTAIR_URL=https://([^/]+)/.*#\1#')
curl -sk --resolve "$H:443:127.0.0.1" -H 'Origin: https://evil.example' -d x=1 "https://$H/" | grep -o '403 Forbidden'
403 Forbidden
9

Your own DNS name and certificate

Point a DNS record at the instance, then set the name that Altair is served under. The command creates a new self-signed certificate for the name, updates nginx and the URL in the credentials file, and keeps the name across reboots:

sudo altair-set-hostname <your-domain>

To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx. Running altair-set-hostname again replaces it with a self-signed certificate:

sudo install -m 0644 <your-domain>.crt /etc/ssl/altair/altair.crt
sudo install -m 0600 <your-domain>.key /etc/ssl/altair/altair.key
sudo nginx -t && sudo systemctl reload nginx

Your browser keeps saved queries and environments per address, so they do not carry over when you change the name.

10

Change the password or add users

The login is an nginx Basic Auth file. To set a new password for admin, pass it to htpasswd on standard input, then reload nginx. printf is a shell builtin, so the password never appears in the process list. Start the line with a space so most shells keep it out of your history.

printf '%s\n' '<new-password>' | sudo htpasswd -i -B -C 12 /etc/nginx/.altair-htpasswd admin
sudo systemctl reload nginx

Running sudo htpasswd -B -C 12 /etc/nginx/.altair-htpasswd <user> without -i prompts for the password instead. Use it with another user name to give a colleague their own login. Run sudo htpasswd -D /etc/nginx/.altair-htpasswd <user> to remove a login.

Afterwards, update or delete ALTAIR_PASSWORD in /var/lib/sac/data/altair-credentials.txt so the file does not hold an old password.

11

Server components

These are the main components and where they live on the instance:

  • Altair static build: /var/www/altair
  • Bootstrap (AltairGraphQL.init): /var/www/altair/sac-init.js
  • License, version record and file checksums: /usr/share/doc/altair-static/
  • nginx site: /etc/nginx/sites-available/sac-altair
  • Basic Auth file: /etc/nginx/.altair-htpasswd (bcrypt)
  • Certificate and key: /etc/ssl/altair/
  • First boot script: /usr/local/sbin/altair-graphql-firstboot.sh
  • Host name helper: /usr/local/sbin/altair-set-hostname
  • Credentials: /var/lib/sac/data/altair-credentials.txt (mode 0600)

The published files match the altair-static 8.5.11 package byte for byte. Verify:

head -1 /usr/share/doc/altair-static/VERSION cd /var/www/altair && grep -v 'index.html.upstream' /usr/share/doc/altair-static/altair-static-8.5.11.sha256sums | sha256sum -c --quiet - && echo "all files match the 8.5.11 package" cat /var/www/altair/sac-init.js | grep AltairGraphQL command -v node || echo "no node runtime" nginx -v 2>&1
altair-static 8.5.11 all files match the 8.5.11 package AltairGraphQL.init({ baseURL: "/", disableAccount: true }); no node runtime nginx version: nginx/1.24.0 (Ubuntu)
12

Security recommendations

  • Allow ports 22, 80 and 443 in the security group from your own networks only.
  • Keep the per-instance password secret, and rotate it if it may have been shared (Step 10).
  • Use your own DNS name and a certificate your clients trust (Step 9).
  • Altair sends requests from the browser of whoever is signed in, so it can reach anything that browser can reach. Add only plugins you trust. They run in the Altair project's hosted sandbox and load their code from the npm registry.
  • Keep the operating system patched. Unattended security upgrades are on, and sudo apt-get update && sudo apt-get upgrade applies everything else.

Support and licensing

Altair GraphQL Client is open source under the MIT license, with no per-user or per-CPU license fee. The license text is in /usr/share/doc/altair-static/LICENSE. The AMI price covers SecureAnyCloud's hardening, first boot setup and support.

  • Email: sales@secureanycloud.com
  • Website: https://secureanycloud.com
  • Support: 24/7, with a 24 hour response SLA

Deploy on AWS

Launch Altair GraphQL Client 8.5.11 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

Deploy on AWS