Ostgate security overview
Ostgate is a native macOS app that reaches Compute Engine VMs through Google's Identity-Aware Proxy (IAP). This page is for the people who approve it: network and security engineers, and the security teams of Google Cloud partners. It says where traffic goes, which identity it uses, where secrets are kept, what listens on the Mac, what we receive, and how the app is signed.
By Ostgate · Updated
In short
- No Ostgate server is in the data path. Google API calls and IAP tunnels go from the Mac directly to Google.
- Google's IAP and your IAM decide what can be reached. Ostgate acts as the signed-in Google user; it has no service account and no access policy of its own.
- Refresh tokens and saved passwords are kept in the macOS Keychain, per account. SSH user keys are generated in the Secure Enclave as P-256 and cannot be exported.
- Tunnel listeners bind
127.0.0.1only. Each connection is traced to one process and admitted by the tunnel's access level; anything that cannot be traced is refused. - In-app SSH verifies the VM's host key: against the keys the VM publishes, or a key you accepted on first use after seeing its fingerprint.
- No telemetry, analytics or crash reporting. The licence server receives a hardware hash, a device public key and your licence key, nothing about your Google Cloud.
- Releases are signed with a Developer ID certificate (team
C83FBCDMHY), built with the hardened runtime, and notarized by Apple.
Where traffic goes
Every connection the app makes is TLS. These are all of its destinations:
| Destination | What travels | When |
|---|---|---|
| Google sign-in, in your browser | Google's own consent screen; the authorisation code returns to
Ostgate on 127.0.0.1. |
Adding or re-authorising an account. |
| Google's token and userinfo endpoints | The code exchange, refresh-token grants, and your email address. | Sign-in, then when an access token is about to expire. |
| Google Cloud APIs | API calls authorised by your own access token. | Browsing projects and instances, and before each connection. |
IAP relay, tunnel. |
One secure WebSocket per TCP connection, carrying the SSH, RDP or tunnel bytes, with your access token. | Every session, tunnel and forward. |
Licence server, license. |
Hardware hash, device public key, licence key. No Google data. | At launch and every few hours. |
From the relay on, the path is Google's: IAP checks your IAM permission on the instance and
connects to the VM's port from 35.235.240.0/20, which your VPC firewall has to
admit (see IAP TCP forwarding: firewall rule
and IAM). The VM needs no external IP and no Ostgate agent. IAP's relay protocol is described on the home
page.
A forward through a bastion is one SSH session over that tunnel: the bastion VM, not the
Mac, opens the connection to the private address. The same holds for the Cloud Run proxy,
which reaches *.run.app from a VM of the service's project.
Identity and permissions
Sign-in
- A Desktop-type OAuth client, your system browser, a loopback redirect to
127.0.0.1on a random port, PKCE and astatecheck. No embedded web views and no custom URI schemes. - The redirect listener accepts only the redirect carrying the attempt's
state; any other request gets an empty400and the sign-in keeps waiting, so another local process cannot end it. - Ostgate never reads gcloud's files or Application Default Credentials. This OAuth flow is its only credential source.
- Access tokens are held in memory only and refreshed within five minutes of expiry. When Google refuses the refresh token, for example after you revoke it or when your Workspace session control requires reauthentication, Ostgate stops refreshing it and asks you to sign in again; sessions already open keep running.
Scopes
Ostgate requests three OAuth scopes. Google shows them on its consent screen, and Ostgate refuses a sign-in in which any of them was left unticked.
| Scope | What it is used for |
|---|---|
| openid | Identifies which Google account completed the sign-in, so several signed-in accounts stay apart as separate profiles. |
| Reads the account's email address: it determines your SSH username (the OS Login username, or one derived from the address when the key goes through instance metadata) and identifies the account in the app. | |
| https:// |
Calls Compute Engine, Resource Manager, OS Login, IAP TCP forwarding, Cloud SQL Admin, Cloud Run Admin, Cloud Logging and, only when you pick a key or password from it, Secret Manager on your behalf. Secret Manager accepts only this scope and the IAP relay publishes no narrower one, so one scope covers every call. |
The privacy policy lists every Google API method Ostgate calls and the action of yours that triggers it.
IAM
Every call and every tunnel runs with the signed-in user's own IAM permissions, so Ostgate reaches nothing that account could not already reach with gcloud. The roles that matter:
roles/for any connection: SSH, RDP, files, tunnels and forwards. IAP refuses a tunnel without it.iap. tunnelResourceAccessor roles/to list instances.compute. viewer roles/(orcompute. osLogin osAdminLoginfor sudo) to SSH to instances that use OS Login.roles/to SSH to instances without OS Login, set a Windows password, and start or stop instances.compute. instanceAdmin. v1
The full list, including Cloud SQL, Cloud Run, Cloud Logging and Secret Manager, is in Prepare your Google Cloud project. To limit who reaches what, scope the IAP role to projects or instances and add IAM conditions, such as an end date or a destination port; Google evaluates them when the tunnel opens.
Secrets and keys
Where each secret lives
- Refresh tokens: Keychain generic-password items, one per account under that account's namespace, accessible after first unlock on this device only and not synchronised to iCloud. Never in preference files, other files or logs.
- SSH user keys: one per account, generated in the Secure Enclave as P-256 and used
as
ecdsa-sha2-. The private key cannot be exported by any process, Ostgate included; the Keychain holds only the Enclave's encrypted handle to it. A software ed25519 key in the Keychain is used only on a Mac without a Secure Enclave.nistp256 - Windows passwords: Set Windows Password uses the mechanism Google documents.
Ostgate writes an RSA-2048 public key to the instance's
windows-keysmetadata; Google's guest agent creates or resets the account and writes the new password, encrypted to that key, to serial port 4; Ostgate decrypts it on the Mac. It stays in memory unless you choose to save it, and a saved password goes to the Keychain with the same attributes as the refresh token. - SSH passwords, for VMs that log in with one: saved to the Keychain only when you ask and the server accepted it; otherwise held in memory for that session.
- Secret Manager: a password or key you set to come from Secret Manager is never stored. Only the secret's name is kept in the connection settings; the value is read from Google when a connection starts, held in memory for that connection and never logged.
- Your own key files (Connect As…): read when the connection starts and held in memory. Ostgate does not store the key or its passphrase.
- Database credentials: Ostgate does not ask for, read or store them. A Cloud SQL tunnel carries the connection your own client makes.
Google API responses are not cached on disk: the app's HTTP session has no URL cache.
How the SSH key reaches a VM
Ostgate decides per connection the way gcloud compute ssh does: the instance's
enable-oslogin metadata, else the project's, else OS Login is off. Windows VMs
never use OS Login.
- OS Login: the public key is imported to your OS Login profile with a one-hour expiry, and you log in as your OS Login username.
- Instance metadata: the key is added to that instance's
ssh-keysonly, never to project metadata, with a 24-hour expiry. Every entry that is not Ostgate's is kept unchanged, and expiredgoogle-sshentries are removed, as gcloud does. This needscompute..instances. setMetadata - Connect As… with your own key publishes nothing.
What listens on the Mac
Tunnels, forwards and Cloud Run proxies listen on 127.0.0.1 and nothing else.
On every accepted connection Ostgate finds the one process holding the other end of that
exact loopback connection (address and port on both sides), then applies the access level
set for that tunnel or forward:
| Access level | Admits | Default for |
|---|---|---|
| Ostgate only | Ostgate's own process. | SSH, RDP, custom ports and bastion forwards you set up yourself. |
| My processes | Processes whose effective user ID is yours. | Database and web presets, Cloud SQL tunnels and Cloud Run proxies. |
| Any local process… | Every process on the Mac. | Never; only after you confirm a shared tunnel. |
- A connection that matches no process, or more than one, is refused. An unprivileged app cannot read another macOS user's or root's processes, so those are refused at every level except Any local process.
- The boundary of My processes is your macOS user account: your other login sessions, sandboxed apps and any malware running as you are admitted. Choose Ostgate only where that matters.
- A tunnel saved as Any local process and set to start automatically starts again after a relaunch without asking a second time.
- A refusal shows on the tunnel's row, for example “Refused nc — only Ostgate may connect”, and is logged with the process name and ID only.
- During sign-in, the OAuth redirect listener described above runs on
127.0.0.1until the sign-in ends. sshfrom Terminal through Ostgate opens no listener: ssh starts Ostgate as itsProxyCommandand talks to it over standard input and output.
Ostgate changes files outside the app only when you ask: the SSH integration adds one block
to ~/.ssh/config, and the Hosts tool and host-name aliases for forwards edit
/etc/hosts after macOS asks for an administrator's approval.
Verifying the VM
SSH host keys
Every in-app SSH connection, terminal tabs and bastion forwards alike, checks the server's host key in this order:
- If the VM publishes its host keys in guest attributes, the presented key must be one of them.
- Otherwise it must equal the key you accepted on first use for that instance.
- Otherwise Ostgate asks, showing the project, zone, instance, key type and
SHA256:fingerprint, and stores the key only if you accept.
A mismatch stops the connection with “Host key mismatch”: the VM was rebuilt or the
connection was intercepted. Accepted host keys are public keys, stored per account. For
ssh from Terminal, OpenSSH checks its own known_hosts.
Remote Desktop
Ostgate does not check the RDP server certificate: Windows presents a self-signed certificate, and the address Ostgate connects to is its own loopback listener. With Network Level Authentication on, the default, Ostgate offers only CredSSP and refuses a VM that selects a weaker protocol, so the password is not sent; CredSSP binds the authentication to the TLS key, so it cannot be relayed to the real VM. Restricted Admin mode, off by default, keeps the password off the VM. With NLA turned off, the password is sent over TLS to an endpoint whose certificate is not checked.
What Ostgate receives
The app contacts the licence server when it starts and every few hours, and sends only what the licence check needs:
- a hardware hash: a SHA-256 hash of the Mac's hardware identifier, so a trial or key is counted once per Mac. The identifier itself is not sent;
- the public half of a device key created in the Mac's Secure Enclave, which signs each request. The private half never leaves the Secure Enclave, and there is no software fallback;
- the licence key you entered, if any.
The server stores the hardware hash, the device public key, when the trial started, which Mac a key is activated on, the name the key was issued to, the key's end date if it has one, and when each was last seen. It receives no Google account, project or instance names and no session contents. It runs on Cloudflare Workers; Cloudflare processes request details such as your IP address to deliver the requests, and the licence server does not store the IP address.
- A signed lease lets Ostgate work offline for up to 72 hours after the last successful check. A Mac that has never reached the licence server cannot start connections.
- The licence gates new connections only. Open sessions keep running when a trial or key ends, and tunnel reconnects never wait for the licence server.
- Licences are sold by Paddle.com as reseller and Merchant of Record; payment details go to Paddle, not to us.
- The app has no telemetry, analytics or crash reporting. This website sets no cookies and runs no analytics.
Details, retention and your rights are in the privacy policy.
Hosts to allow
For a corporate firewall or egress filter: the app connects out on TCP 443 to these hosts.
| Host | Used for | Needed |
|---|---|---|
tunnel. |
IAP relay, secure WebSocket | Every connection |
accounts. |
Sign-in, opened in your browser | Adding an account |
oauth2. |
Token exchange and refresh | Always |
openidconnect. |
Account email | Sign-in |
compute. |
Instances, metadata, host keys, serial output | Always |
cloudresourcemanager. |
Project search | Browsing projects |
oslogin. |
OS Login profile and keys | SSH to OS Login VMs |
sqladmin. |
Cloud SQL instance list | Cloud SQL |
run. |
Cloud Run services and their IAM policy | Cloud Run |
logging. |
A VM's audit log entries | Event log |
secretmanager. |
Secrets you pick | Only with Secret Manager |
license. |
Trial and licence check | Always |
Behind a corporate proxy: Ostgate has no proxy setting of its own and uses the macOS
system proxy settings (System Settings › Network › Details › Proxies) for Google's APIs,
the IAP relay and the licence server. The relay's WebSocket works through an HTTP
CONNECT proxy (tested 7 October 2026). Not tested yet: proxies that ask for a
user name and password, and proxies that inspect TLS; those need the proxy to pass
WebSocket upgrades to tunnel. and, for inspection, a
root certificate the Mac trusts.
Open in Cloud Console and the links in
About open console. and
ostgate. in your browser, not in the app. Context-aware access levels
that require a device certificate are not supported yet.
Signing and updates
- Releases are signed with a Developer ID Application certificate of team
C83FBCDMHY, with the hardened runtime and a secure timestamp. The app and the disk image are each notarized by Apple, with the ticket stapled. - The app is not sandboxed: a sandboxed build cannot bind its tunnel and sign-in listeners.
- Each release is built after the full test suite passes; the release build refuses to sign or notarize if any check fails. Only the latest release receives fixes.
- The disk image and its SHA-256 are published as GitHub Releases of
ostgate/
ostgate-iap-app . - The SSH libraries resolve from mirrors we control rather than upstream tags that can move, and every bundled third-party licence ships with the app.
- Ostgate does not update itself. A new version is a new disk image on the releases page, listed in the release notes.
To check a download:
shasum -a 256 Ostgate-<version>.dmg spctl -a -vv -t open --context context:primary-signature Ostgate-<version>.dmg codesign -dv /Applications/Ostgate.app 2>&1 | grep TeamIdentifier
Compare the hash with the one in the release notes on GitHub. The last command prints
TeamIdentifier=.
Reporting a vulnerability
Report a security problem privately to support@ostgate.app with “Security” in the subject, not as a public issue. Include the Ostgate version (Ostgate › About Ostgate), your macOS version and the steps to reproduce. The policy is in SECURITY.md in the releases repository.
Questions
Does any traffic pass through Ostgate's servers?
No. Google API calls and IAP tunnels go from your Mac directly to Google. The only Ostgate server the app contacts is the licence server at license., which receives a hardware hash, the public half of a Secure Enclave device key and the licence key you entered, if any. It never receives your Google account, projects, instances or session traffic.
Can other users or apps on the Mac use my tunnels?
Only at the access level set for that tunnel. Ostgate only admits Ostgate itself; My processes admits processes running as your macOS user; Any local process admits every process on the Mac and needs a confirmation. A connection that cannot be traced to exactly one process is refused, and processes of other macOS users are refused unless you chose Any local process.
What happens to credentials when I remove an account?
Remove from This App stops the account's sessions, tunnels and forwards, then deletes its refresh token, SSH key and saved passwords from the Keychain, together with the host keys it trusted and its settings. It changes nothing on Google Cloud: keys already published expire by themselves, after one hour through OS Login and 24 hours in instance metadata. To invalidate the refresh token at Google as well, remove Ostgate on your Google Account's third-party access page.
Can we restrict which projects and VMs Ostgate can reach?
Yes, through IAM. Ostgate has no access policy of its own: it acts as the signed-in Google account and reaches exactly what that account's IAM allows. Grant roles/ only on the projects or instances people may reach, and narrow it with IAM conditions, such as an end date or a destination port.
Does Ostgate use gcloud or its credentials?
No. Ostgate never reads gcloud's files or Application Default Credentials. Every account signs in through Ostgate's own OAuth flow in your browser, and its refresh token is kept in the macOS Keychain.
Is the source code available?
No. Ostgate is closed source. The public repository ostgate/ holds the signed releases, the README and the security policy, not the code.
Try it against your own policy. Ostgate runs on Apple Silicon Macs with macOS 26.3 or later, with a 7-day trial and no sign-up. Project setup is in Prepare your Google Cloud project.