Gateway Security¶
The Gateway secures each listener in 3 layers: mutual TLS admits only known certificates, an authentication plugin verifies the connecting user, and an optional entitlement filter narrows what each user may query or subscribe to. This page describes how those layers combine and which properties control them.
Overview¶
The 2 listeners are secured independently at the transport layer, each with its own keystore and authorized-clients list, but they share one authentication plugin and one entitlement filter. Transport security is not optional: a listener with a keystore always enforces mutual TLS.
Mutual TLS¶
Whenever a listener has an SSL keystore configured, it enforces mutual TLS. A client is admitted only if the public key of the certificate it presents matches an entry in that listener's authorized-clients file. The server authorizes its own key automatically; every external client must be listed. A listener with no keystore configured stays plain TCP.
There is no property to turn this off. Mutual TLS is mandatory once a keystore is set.
The relevant properties, documented in full in Configuration:
ami.gateway.ssl.keystore.file/ami.gateway.ssl.authorized.clients.filefor the native listener.ami.gateway.jdbc.ssl.keystore.file/ami.gateway.jdbc.ssl.authorized.clients.filefor the JDBC listener.
Both authorized-clients files default to the shared keys/3forge.auth. Entries are ssh-rsa public
keys.
If a listener's keystore file does not exist when the Gateway starts, the Gateway generates a
self-signed keystore at that path, with a .pub public key file beside it. It also adds its own public
key to the listener's authorized-clients file, creating the file if needed, which is how it authorizes
its own key. The Gateway never adds external client keys. Editing the authorized-clients file does not take effect while the Gateway is running: reload
the Gateway component for a change to the file to apply.
Authorizing a client on the Gateway¶
Obtaining a client certificate and converting its public key to the ssh-rsa form the allowlist
takes is common to every 3forge listener and is not covered here. Two parts of the process are
specific to the Gateway:
Applying a change to the allowlist. Append the client's ssh-rsa line to the file named by the
listener's authorized-clients property, then reload the Gateway component:
Reload, not restart. shutdownComponent, startupComponent, and restartComponent do not work on
a Gateway, see Stopping and starting a Gateway.
Until the reload, a newly added key is still rejected at the handshake, with nothing to indicate
that the file has changed.
Pointing a client at the JDBC listener. The client's certificate, its key, and the server's
public key go in the connection URL as sslKeystoreFile, sslKeystorePassword, and sslPubFile,
with isSecure=true. See JDBC Access.
Testing the allowlist with the shipped keystore
The Gateway authorizes its own certificate automatically. Because the shipped keys/3forge.jks
holds the server's own key, a client that connects with that keystore is admitted even when its key
is not in keys/3forge.auth. To see the allowlist reject a client, test with a separate client
certificate, not the shipped one.
Authentication¶
A single authentication plugin applies to both listeners, selected by ami.gateway.auth.plugin.class.
By default it follows the instance-wide ami.auth.plugin.class (ACCESS_FILE). There is no separate
JDBC authentication plugin. For the login plugins themselves (LDAP, OAuth, SAML), see
Authentication; for the certificate exchange behind mutual TLS, see
Mutual TLS.
When the authentication plugin is left blank, the Gateway installs a disabled authenticator and does not require a login.
A client authenticating over a listener presents both a client certificate authorized for the listener (for the mandatory mutual TLS) and valid credentials for the authentication plugin.
Entitlement filtering¶
An optional entitlement filter plugin, set with ami.gateway.filter.plugin.class, is applied to every
query and subscription from a client that has logged in. The filter is attached to the session when the
client logs in, so a client that has not logged in is not filtered. A client can skip the login only
when the Gateway does not require one (see Authentication). When the plugin is not set, no filter is applied and all
authenticated traffic passes.
A filter can, per session:
- Reject, pass, or rewrite a query before it is routed.
- Rewrite a subscription: narrow its columns or tighten its
WHEREclause. - Filter individual rows, showing, hiding, or modifying each one.
The filter fails closed. If it errors while supplying its row filter, the whole snapshot request is rejected; if it errors on one subscription, that subscription is dropped. Neither case serves data unfiltered.
Writing a filter plugin is outside the scope of this page; the shipped SampleGatewayFilterPlugin
demonstrates the available hooks and can be enabled with:
The sample filter changes results
The sample is a demonstration, not a neutral filter. Enable it only on a test instance. It:
- Appends
WHERE symbol=='MSFT'to the text of every query. - For a table named
Orders, hides newIBMrows, setspxto-1on newFrows, and doublespxon every update. - Writes the logged-in username into a
desccolumn on every new row, in any table, exceptOrdersrows forMSFTandIBM.
What this does not support¶
- Mutual TLS has no off switch while a keystore is configured.
- The 2 listeners cannot use different authentication plugins. One plugin serves both.
- The Gateway does not issue certificates for clients. It generates only its own keystore, when that file is missing, and lists only its own key in the authorized-clients file.