Operator's interface reference
Every network port, socket and file this gateway either opens or dials, one subsystem per section, in the same three parts each time: where the value is set, what the value occupies, and the command that measures it.
The three ports ISG opens itself — the admin UI, the CA renew endpoint and the diagnostic listener — have defaults in the code, and the numbers below are those defaults. Every other port on this page comes from a value an operator wrote into a configuration file. There is no default to quote for those, so what is quoted instead is the usual value in this installation, marked as such. Reading one of them as a default is the one way to misuse this page.
Each section names its keys and flags in tables rather than in sentences,
deliberately: those cells are checked against the code by
make operations-check, and a value welded into a paragraph is a value no gate
can find.
webUI
The browser admin view: approvals, sessions and the audit trail. It serves
HTTPS once isg ca init has run, because that is what issues its serving
certificate; without one it will serve plain HTTP on a loopback bind and refuse
to start on any other.

Screenshot: reference environment (env1), 2026-10-17.
Where it is set
| Command | Flag | Reference |
|---|---|---|
isg serve |
--bind |
isg serve |
isg serve |
--port |
isg serve |
isg serve |
--secure-cookie |
isg serve |
isg serve |
--ui-hostname |
isg serve |
| Config file | Key | Reference |
|---|---|---|
ldap.yaml |
url |
ldap.yaml |
jira.yaml |
webui_base_url |
jira.yaml |
tls.yaml |
webui.cert_file |
tls.yaml |
tls.yaml |
webui.key_file |
tls.yaml |
ldap.yaml's url is on this list because it decides whether the UI asks for a
login at all: with a directory configured the UI is form-authenticated, without
one it runs open. With sso.yaml present the login page also offers the
identity provider next to the password form (web: optional) or offers nothing
else (web: required); the directory is required either way, because what the
provider returns is only a lookup key into it — see SSO (OIDC identity
provider) below. webui_base_url is the address a JIRA ticket links back to,
which is the same UI seen from somebody else's browser.
tls.yaml's two keys are the opt-out from the ISG CA as the source of this
port's certificate — see Serving certificate below. They are the only keys on
this page whose absence is itself the documented default: without the file the
ISG CA issues the certificate, which is what every deployment did before the
file existed.
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port | 8765 | the default of --port |
| Bind address | 127.0.0.1 |
the default of --bind |
| Serving certificate | signed by the X.509 CA published at $ISG_HOME/pub/x509_ca_cert.pem |
the CA is created by isg ca init; the serving certificate is issued at startup |
| Serving certificate, external | the PEM pair tls.yaml names |
present only when tls.yaml is; the ISG CA then issues nothing for this port |
The certificate has two possible sources, and isg system check's
webui_cert_source row always says which one is in use.
The ISG CA (the default). With no tls.yaml, isg serve issues this port's
certificate from the gateway's own X.509 CA at startup, into
ca/server/server_<ui-hostname>.crt. It needs no PKI on your side and works on
the first run; the cost is that every browser reaching this UI has to trust
pub/x509_ca_cert.pem.
That cost is what bounds the CA. A trust-store import makes the CA key able to
vouch for any name it is allowed to certify, so a NEW X.509 CA is created only
with that set named: isg ca init --permitted-dns <name> --permitted-ip <CIDR>
writes them into the certificate as nameConstraints, and with neither flag the
command creates nothing. Two rows report the result afterwards:
x509_ca_name_constraints says whether this CA is bounded and to what —
unbounded is WARN, never FAIL, because the only fix is a new CA and replacing
one invalidates every certificate it signed — and issuance itself refuses a
hostname outside the set rather than minting a certificate that every
chain-checking client would reject.
Your own PKI (tls.yaml). Name a certificate and a key issued by a CA the
estate already trusts and this port serves those instead — no ISG root to
import anywhere. cert_file holds the leaf first and any intermediates after
it; key_file must be 0600 and owned by the daemon's account. The pair is
checked at startup against the name this listener is dialled by, and a
tls.yaml that is present and unusable refuses the start — there is no quiet
fall back to an ISG-issued certificate, because that would hand you a working
UI with a browser warning and never say your configuration was ignored.
Replacing the external pair needs no restart: the daemon re-reads the files
(at most one stat every 30 seconds) and the next handshake gets the new
certificate, with a WEBUI_CERT_RELOADED audit entry. A replacement that fails
the same checks is refused, the previous certificate keeps serving, and
WEBUI_CERT_RELOAD_REJECTED plus a red webui_cert_source row say so.
What ISG does not check is the chain: it does not hold your root, so whether a browser accepts the certificate is answerable only from a client.
How to measure it
isg system check --verbosewebui_cert_source— which source is in use, the issuer and SANs of an external pair, and whether a replacement was refused- ISG CA source:
curl -sS -o /dev/null -w '%{http_code}\n' --cacert $ISG_HOME/pub/x509_ca_cert.pem https://<ui-hostname>:8765/ - external source: the same
curlwith--cacert <customer-root.pem>instead — the ISG root is not involved and importing it proves nothing - the serving certificate's own expiry:
server_cert_expiryis the ISG-issued set on disk underca/server/,webui_cert_expirythe certificate this port actually presents. With an external pair the two are about different certificates, andwebui_cert_sourceis what reports the external one's deadline - ISG CA source — replacing it is a manual act and nothing does it for you:
isg ca server-cert renew --hostname <name>writes a fresh certificate and key underca/server/, andisg servehas to be restarted before this port presents them — until that restartserver_cert_expiryandwebui_cert_expirydisagree by design, and the disagreement is how you see the window
CA renew
The mTLS endpoint a service machine's agent calls to renew its access certificate. It is the one ISG port that binds to every interface by default, because the machines that renew are by definition not this one; the protection is the mutual TLS, not the bind address.
Where it is set
| Command | Flag | Reference |
|---|---|---|
isg serve |
--ca-renew-bind |
isg serve |
isg serve |
--ca-renew-port |
isg serve |
isg serve |
--ca-renew-hostname |
isg serve |
| Config file | Key | Reference |
|---|---|---|
agent.yaml |
ca_renew_url |
agent.yaml |
agent.yaml |
renew_interval |
agent.yaml |
ca.yaml |
renewable_ttl_days |
ca.yaml |
The first two keys live on the service machine, not on the gateway: they are
the client half of this endpoint, and ca_renew_url is where the port number
written above has to be repeated.
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port | 8766 | the default of --ca-renew-port |
| Bind address | 0.0.0.0 |
the default of --ca-renew-bind |
| Path | /ca/renew |
fixed by the endpoint |
How to measure it
isg agent status— on the service machine, against its own certificateisg system check --verbose— on the gateway, for the CA's own expiry
Web Access
The listener that puts web consoles behind the ISG login, and the connections it makes to them. Off when
apps.yaml is missing; how a request travels is on how Web Access works.
Where it is set
| Config file | Key | Reference |
|---|---|---|
apps.yaml |
listen |
apps.yaml |
apps.yaml |
tls_cert_file |
apps.yaml |
apps.yaml |
tls_key_file |
apps.yaml |
apps.yaml |
webui_url |
apps.yaml |
apps.yaml |
apps[].upstream |
apps.yaml |
apps.yaml |
apps[].upstream_ca_file |
apps.yaml |
apps.yaml |
dynamic_apps_domain |
apps.yaml |
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port | 8444 in this installation — not a default | the port in listen (isg_apps_port in the shipped Ansible) |
| Direction | inbound, browser → gateway (TLS; the host name picks the application) | — |
| TCP port | each application's own port — not a default | the URL in upstream |
| Direction | outbound, gateway → web console | — |
| File | the listener's certificate and key | tls_cert_file, tls_key_file (the key 0600 or 0400) |
| File | the assertion signing key, with an identity: jwt application |
assertion_key_file |
The application sessions and approvals live in the record store, like the SSH sessions; a kind: form account's
upstream session cookies live in the serving broker's memory only, and are gone with a restart (the next request
signs in again).
How to measure it
-
isg system check --verbose— theapps.yamlrow (the file loads), theWeb Access listenerrow (listening, the certificate covers every application's host name, the last reload), theWeb Access credentialsrow (every shared account's secret is readable). -
From a client, with the application's host name and the listener's port — an unknown host name gets 421, a known one sends a browser without an application session to the web UI's sign-in:
curl -sI https://<app host>:<port>/
Diagnostics and metrics
The health and metrics listener: liveness for an orchestrator, readiness for a load balancer, and a Prometheus-format scrape. It is off unless switched on, and it binds to loopback unless told otherwise — a scraper on another host is an explicit decision, not a side effect of enabling the feature.
Where it is set
| Config file | Key | Reference |
|---|---|---|
diag.yaml |
enabled |
diag.yaml |
diag.yaml |
listen |
diag.yaml |
diag.yaml |
auth_token |
diag.yaml |
diag.yaml |
per_host_labels |
diag.yaml |
diag.yaml |
scrape_staleness_warn |
diag.yaml |
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port | 8767 | the default of listen |
| Bind address | 127.0.0.1 |
the default of listen |
| Paths | /healthz, /readyz, /metrics |
fixed by the listener |
auth_token, when set, is required on /readyz and /metrics, and never on
/healthz.
How to measure it
curl -fsS http://127.0.0.1:8767/healthzcurl -fsS -H "Authorization: Bearer <token>" http://127.0.0.1:8767/readyzcurl -fsS http://127.0.0.1:8767/metricsisg system check --verbose
One measurement, three surfaces. isg system check does not measure on
its own: it asks the running daemon for
the report over the session socket and renders the answer. It is the same
report the admin view's /system page shows and the same checks /readyz
serves a projection of, measured with the daemon's own inputs — so the output
does not depend on which account runs the command. The report's first line
says so (Measured by the daemon (pid N, user isg) over /run/isg/session.sock).
Whoever may open the socket may ask; nothing more is required.
Without a daemon — not running, not reachable from this account, or too old
to know the request — the command measures locally with the same code and
opens with Measured locally as user … naming the reason; --local forces
that path for comparison. On the local path the rows that read files only
the daemon may read — the record store's credential behind the
Lockout store, Revocation list, Login state store, Broker version skew and Retention artifact integrity rows — say not measurable from this account and read WARN: not measured is never a false green. They are
never a FAIL for the caller's own permissions, and they never disappear.
Color is asked for, not guessed at. The report colors its [OK ],
[WARN] and [FAIL] markers on a real terminal only, so a pipe, a file and
cron keep the escape-free, one-row-per-line form parsers read. Reading it from
another host with ssh gateway isg system check holds no remote terminal
either, so that output is colorless as well — --color=always asks for the
color anyway, which is what a logged non-interactive run wants, and ssh -t
is the other way round; --color=never refuses it even on a terminal. Color
never brings the wrapping with it: --color=always on a pipe still gives one
row per line. In the default auto mode NO_COLOR (present with any value)
wins over CLICOLOR_FORCE (set to anything but 0), which in turn wins over
the terminal test.
Record store (PostgreSQL)
Where the session records, broker leases, login state, lockout counters and revocations live when more than one broker shares them. The connection string is read from a file rather than from the config, so the password never sits in a world-readable YAML.
Where it is set
| Config file | Key | Reference |
|---|---|---|
storage.yaml |
records.backend |
storage.yaml |
storage.yaml |
records.postgres.dsn_file |
storage.yaml |
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port | 5432 in this installation — not a default | the DSN in the file named by records.postgres.dsn_file |
| Direction | outbound, gateway → database | — |
How to measure it
isg system brokersisg system check --fleetisg system check --verbose
Object store (S3)
Where a closed session's recording is moved to. An empty endpoint means AWS S3
proper and the SDK resolves the region's own address; anything else is the
address of an S3-compatible service, and that address carries the port.
Where it is set
| Config file | Key | Reference |
|---|---|---|
storage.yaml |
recordings.backend |
storage.yaml |
storage.yaml |
recordings.s3.endpoint |
storage.yaml |
storage.yaml |
recordings.s3.bucket |
storage.yaml |
storage.yaml |
recordings.s3.path_style |
storage.yaml |
storage.yaml |
recordings.s3.credentials_file |
storage.yaml |
storage.yaml |
recordings.dir.path |
storage.yaml |
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port | 9000 in this installation — not a default | the URL in recordings.s3.endpoint |
| Direction | outbound, gateway → object store | — |
| Local alternative | the directory named by recordings.dir.path |
used when recordings.backend is the directory backend |
How to measure it
isg storage verifyisg storage verify --deep
Secret store (OpenBao)
Read when the certificate signer is delegated — in that mode the SSH CA
private key is never on this disk, and every certificate ISG issues is signed
over this connection — when the multi-factor secrets are kept in its KV
store instead of the local totp/ directory, and when a Web Access application
uses a shared account (identity: credential), whose secret is read from the KV
store on each sign-in or request. The X.509 CA is unaffected and stays local.
Where it is set
| Config file | Key | Reference |
|---|---|---|
vault.yaml |
address |
vault.yaml |
vault.yaml |
token_file |
vault.yaml |
vault.yaml |
ssh_mount |
vault.yaml |
vault.yaml |
tls_ca_file |
vault.yaml |
vault.yaml |
kv_mount |
vault.yaml |
ca.yaml |
signer |
ca.yaml |
mfa.yaml |
store |
mfa.yaml |
apps.yaml |
credential_kv_prefix |
apps.yaml |
ca.yaml's signer and mfa.yaml's store are the two switches: with the
built-in signer and the file secret store, vault.yaml is never opened and
none of this section applies.
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port | 8200 in this installation — not a default | the URL in address |
| Direction | outbound, gateway → secret store | — |
How to measure it
isg system check --verbose
SSO (OIDC identity provider)
The organisation's OIDC identity provider as the login for two surfaces: the
browser login to the admin UI (Authorization Code with PKCE), and the gateway
login over ssh isg-login@gateway (the RFC 8628 device flow — the daemon prints
a code and a URL, the operator approves it in a browser of their own, and the
daemon polls after Enter). What the provider decides is who is logging in. What
it does not decide is whether that person exists here or what they may do: the
value of username_claim is a lookup key the directory has to resolve, and the
role, the groups and the approver right come from the directory as before.
Where it is set
| Config file | Key | Reference |
|---|---|---|
sso.yaml |
enabled |
sso.yaml |
sso.yaml |
issuer |
sso.yaml |
sso.yaml |
client_id |
sso.yaml |
sso.yaml |
client_secret_file |
sso.yaml |
sso.yaml |
redirect_url |
sso.yaml |
sso.yaml |
username_claim |
sso.yaml |
sso.yaml |
mfa |
sso.yaml |
sso.yaml |
web |
sso.yaml |
sso.yaml |
cli |
sso.yaml |
issuer is where discovery is read from, and it has to be HTTPS.
client_secret_file is a file of mode 0600 — the secret itself is never in the
YAML. redirect_url has to end in /login/sso/callback and match what the
provider has registered byte for byte. web and cli are independent axes:
what the login page offers, and what the SSH login prompt does.
SAML 2.0 instead of OIDC
With protocol set to saml, the browser login speaks SAML 2.0 instead, and
only the browser login: the SSH login stays password plus the gateway's own
second factor, so cli has to stay off. The OIDC keys above are then not
read.
| Config file | Key | Reference |
|---|---|---|
sso.yaml |
protocol |
sso.yaml |
sso.yaml |
saml_idp_metadata_url |
sso.yaml |
sso.yaml |
saml_idp_metadata_file |
sso.yaml |
sso.yaml |
saml_sp_entity_id |
sso.yaml |
sso.yaml |
saml_acs_url |
sso.yaml |
sso.yaml |
saml_username_attribute |
sso.yaml |
sso.yaml |
saml_use_name_id |
sso.yaml |
sso.yaml |
saml_idp_mfa_authn_context |
sso.yaml |
sso.yaml |
saml_clock_skew_seconds |
sso.yaml |
Exactly one of saml_idp_metadata_url (HTTPS) and saml_idp_metadata_file
names the provider's metadata, which has to carry a signing certificate and an
HTTP-Redirect sign-on endpoint. saml_acs_url has to end in
/login/sso/saml/acs. Only logins this gateway started are accepted, and the
assertion itself has to be signed. The username is the value of the attribute
saml_username_attribute names (default uid), or the NameID when
saml_use_name_id is set. Under mfa: idp the assertion's
AuthnContextClassRef has to be one of saml_idp_mfa_authn_context, otherwise
the local second factor runs. The gateway publishes its own metadata at
/sso/saml/metadata, for the provider's administrator to import.
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port | no default at all | the port in the issuer URL |
| Direction | outbound, gateway → identity provider (HTTPS: discovery, JWKS, token and device endpoints) | — |
| Paths, inbound | /login/sso/start, /login/sso/callback |
fixed by the UI, on the webUI port |
| TCP port, SAML | no default at all | the port in the saml_idp_metadata_url URL; none with saml_idp_metadata_file |
| Direction, SAML | outbound, gateway → identity provider (HTTPS: the metadata, once at startup) | — |
| Paths, inbound, SAML | /login/sso/start, /login/sso/saml/acs, /sso/saml/metadata |
fixed by the UI, on the webUI port |
The browser's own traffic to the provider is not the gateway's and is not in this table.
How to measure it
isg system check --verbose— theSSO (OIDC)row: discovery and JWKS; underprotocol: samltheSSO (SAML)row instead: the provider's metadata and the expiry date of its signing certificate- the audit log:
LOGIN_SSO_START,LOGIN_SSO_OKandLOGIN_SSO_FAILED, each with asurfaceofwebuiorgatewayand aprotocolofoidcorsaml; a refused gateway login names one ofsso_denied,sso_expiredorsso_unavailable, a refused SAML login the step that failed, such asaudience,in_response_toorreplay
A provider that cannot be reached at startup is not fatal: the daemon starts
without SSO — the password form appears even under web: required, and
cli: required still prompts for a password — and the check row stays red until
the provider is reachable again. Whether anyone can enter a running gateway must
not depend on a JSON document being fetchable.
LDAP
The primary identity source: who a user is, which groups they are in, and — for the admin UI's login — a password bind. Without it the gateway falls back to a local identity file, which is a supported mode rather than a gap.
Where it is set
| Config file | Key | Reference |
|---|---|---|
ldap.yaml |
url |
ldap.yaml |
ldap.yaml |
start_tls |
ldap.yaml |
ldap.yaml |
tls_ca_file |
ldap.yaml |
ldap.yaml |
allow_plaintext |
ldap.yaml |
ldap.yaml |
bind_dn |
ldap.yaml |
ldap.yaml |
bind_password |
ldap.yaml |
ldap.yaml |
base_dn |
ldap.yaml |
ldap.yaml |
group_base_dn |
ldap.yaml |
ldap.yaml |
timeout_seconds |
ldap.yaml |
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port | 636 in this installation — not a default | the URL in url |
| Direction | outbound, gateway → directory | — |
636 is the standard LDAPS port and the recommended mode; 389 is the standard
plain port, which only carries passwords safely with start_tls: true. This
reference environment binds the privileged port rootless via an ip_unprivileged_port_start
sysctl on the directory host, which the isg_directory Ansible role sets
itself — only downwards, and never above an already lower floor. A rootless
container without that sysctl typically sits on a high port such as 1389. All
of these are the operator's value in url, not a number ISG chooses.
The directory side has a half the gateway cannot enforce: a directory that still answers on its plain port leaves that transport available to anything else on the network. This installation therefore publishes 636 to the fleet and keeps the plain port reachable only from inside the directory host, for components that live there and have no TLS path of their own yet.
The connection is encrypted, or the daemon does not start. An ldaps://
url, or an ldap:// url with start_tls: true, are the two normal shapes;
tls_ca_file names the issuing CA's PEM when it is not in the host's trust
store. An ldap:// url with neither key refuses the start, and
allow_plaintext: true is the explicit, WARN-reported consent to bind in the
clear — which matters here because this connection carries operator passwords,
not just the service account's. isg system check states the transport in its
ldap.yaml row, and its LDAP bind row measures the real handshake and the
certificate's expiry; the full rule is in the
access model.
The bind account is READ-ONLY.
ldap.yaml lives on every broker as 0660 isg:isg-operators — every operator
on the machine can read it, and so can anyone holding a backup of it. The
bind_dn in it must therefore be a dedicated, read-only service account,
never the directory's administrator: with an admin bind that file is write
access to the whole directory — password changes, group membership, deletions.
A read-only bind needs two things, and the second one is easy to forget: the
account, and an ACL rule that lets it read. On this directory image the
shipped ACL gives a plain bound account nothing but itself, so
without the rule the account binds and then reads zero entries — the install
stays green while identity resolution and the IdP's user import come up empty.
The rule's position decides the rest: before the directory's
userPassword rule the account can read every password hash in the tree; after
the catch-all rule it is a silent no-op.
In the reference installation the Ansible roles own all of this: the accounts
and the ACL rule are created in the running directory (isg_directory), the
rendered ldap.yaml carries the read-only bind, and the gateway's verify
measures on each broker that the bind DN is that account and that the
directory administrator's password appears nowhere in the file. The directory
side is measured from both ends — bound as the account and as root over the
directory's own socket — with four assertions: it reads what the tree holds, it
gets no password hashes back, a real write attempt is refused with
50 Insufficient access, and the service accounts do not show up among the
users. Details: ansible/README.md, "Csak olvasó LDAP-bind".
How to measure it
isg system check --verbose
SMTP
The delivery channel for the MFA code. Absent, the code is written to a local outbox file instead, which is a development mode and says so in the check output.
Where it is set
| Config file | Key | Reference |
|---|---|---|
smtp.yaml |
host |
smtp.yaml |
smtp.yaml |
port |
smtp.yaml |
smtp.yaml |
from |
smtp.yaml |
smtp.yaml |
tls |
smtp.yaml |
smtp.yaml |
timeout_seconds |
smtp.yaml |
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port | 1025 in this installation — not a default | the value of port |
| Direction | outbound, gateway → mail relay | — |
1025 is the local test relay. A production relay is 587 for STARTTLS submission
or 25 for plain; the value is port either way.
How to measure it
isg system check --verbose
JIRA
The ticket integration: a session can be required to name a ticket, and the gateway verifies that ticket exists. The credentials file is owner-only, so an operator shell measures less here than the daemon does — the rows say so rather than passing silently.
Where it is set
| Config file | Key | Reference |
|---|---|---|
jira.yaml |
endpoint |
jira.yaml |
jira.yaml |
project_key |
jira.yaml |
jira.yaml |
auth_email |
jira.yaml |
jira.yaml |
auth_token |
jira.yaml |
jira.yaml |
timeout_seconds |
jira.yaml |
jira.yaml |
ticket_min_level |
jira.yaml |
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port | 443 for a hosted instance — not a default | the URL in endpoint |
| Direction | outbound, gateway → JIRA | — |
How to measure it
isg jira verifyisg system check --verbose
Audit export (syslog)
An opt-in copy of every audit entry, forwarded to a SIEM as RFC 5424 syslog over TCP with TLS. The copy is a copy: a successful send does not satisfy the strict audit guarantee, and the file on disk stays the audit of record.
Separately, and unrelated to the SIEM export, this gateway can also receive forwarded break-glass log lines from target hosts. That receiver is an rsyslog drop-in shipped with the packaging, not an ISG listener, and it is the reason a second port appears in the table below.
Where it is set
| Config file | Key | Reference |
|---|---|---|
audit.yaml |
syslog.enabled |
audit.yaml |
audit.yaml |
syslog.address |
audit.yaml |
audit.yaml |
syslog.tls_ca_file |
audit.yaml |
audit.yaml |
syslog.timeout_seconds |
audit.yaml |
audit.yaml |
syslog.queue_size |
audit.yaml |
breakglass.yaml |
breakglass_log_target |
breakglass.yaml |
breakglass.yaml |
breakglass_log_protocol |
breakglass.yaml |
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port, outbound | no default at all | the host:port in syslog.address |
| TCP port, inbound | 20514 in this installation — not a default | /etc/rsyslog.d/10-isg-breakglass-receiver.conf, an rsyslog drop-in |
| Direction | outbound to the SIEM; inbound from target hosts | — |
How to measure it
isg system check --verbose
License
Which modules the gateway may open new sessions for: ssh_broker and
web_access. Without a license file the gateway runs in a 30-day evaluation
mode with every module; after it, and after a license's 14-day grace period,
new sessions are refused. Running sessions, break-glass and /readyz are never
affected. The file is issued and signed by Inshipia, installed as is, and
picked up within 30 seconds, without a restart.
Where it is set
| Config file | Key | Reference |
|---|---|---|
license.yaml |
key_id |
license.yaml |
license.yaml |
payload |
license.yaml |
license.yaml |
signature |
license.yaml |
With the Ansible installation the file goes into the environment's
configuration directory in the repository, next to policy.yaml;
playbooks/config-src.yml carries it into the config source, and
playbooks/site.yml onto the brokers.
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| File | license.yaml in the state directory |
the fixed name |
| Record | one record in the shared record store: the evaluation clock | written on the fleet's first start without a license, never rewritten |
| Network | none — the license is checked offline | — |
How to measure it
isg license showisg system check --verbose— theLicenserow- the
isg_license_state,isg_license_expiry_timestamp_secondsandisg_license_named_usersmetrics
Brokers
In a multi-broker installation every gateway runs the same daemon against one shared record store, behind one shared name. Nothing here opens a new port on the gateway — the SSH port below belongs to the operating system's own sshd, which started before ISG did.
Where it is set
| Config file | Key | Reference |
|---|---|---|
broker.yaml |
id |
broker.yaml |
broker.yaml |
public_name |
broker.yaml |
broker.yaml |
ssh_port |
broker.yaml |
What it occupies
| Resource | Value | Where the value comes from |
|---|---|---|
| TCP port | 22 | the default of ssh_port; the port is sshd's, not ISG's |
| Local socket | /run/isg/session.sock |
the daemon's session socket |
| Local socket | /run/isg/issue.sock |
the daemon's certificate-issuance socket |
| Local socket | /run/isg/gateway.sock |
the daemon's gateway-login socket |
| Config root | /var/lib/isg |
the system-unit layout, overridden by $ISG_HOME |
How to measure it
isg system brokersisg system check --fleetisg system check— theBroker version skewrow compares the FLEET COMPATIBILITY NUMBER every live broker lease carries with this binary's; it exists only on a shared (PostgreSQL) record store. The versions are listed too, but sincev0.476.0they are evidence, not the verdict.isg system check— theBroker target listrow compares the fingerprint of the staticservers.yaml(default level and host→level entries) every live broker LOADED AT START, as its lease advertises it: OK when all agree, WARN listing each broker's host count and digest when they differ, and a separate sentence when this host's own file has changed since its restart. The file is per broker and hand-maintained — nothing ships or synchronises it — and the daemon reads it once at start, so the fix is to align the file on the differing broker and restart it. Same store gate as the row above.
Mixed versions during a rolling upgrade. The brokers may be upgraded in any
order. Two measured properties make that safe: the PostgreSQL schema is
additive (every migration is a CREATE TABLE IF NOT EXISTS with its own
indexes and sequences, and make migrations-check refuses a DROP, a RENAME, a column retype or a NOT NULL
column without a DEFAULT), and a record's members an older binary does not
know survive its read-modify-write unchanged (internal/recordjson, one test
per record family). Both hold for as long as the brokers agree about the shared
state, and since v0.476.0 that agreement is measured by an explicit number
rather than by the distance between releases: the FLEET COMPATIBILITY NUMBER
(internal/fleetcompat), which a human bumps when the brokers' shared state or
broker-to-broker protocol changes and which nothing else bumps. It is published
on the second line of isg --version, in the compat label of
isg_build_info, and in the fleet_compat member of a broker's lease. Two
brokers on the same number may be any number of minors apart; the
Broker version skew row is OK there, WARN while the fleet spans ONE
compatibility step (a rolling upgrade in progress — it is meant to be short) or
when a broker's version cannot be parsed, and FAIL beyond one step or when two
brokers disagree on the major version — nothing is promised across a major. A
lease written before v0.476.0 carries no number, and for those the row falls
back to the older N-1 minor rule and says so. The first new binary to start
applies the migration, under an advisory lock, in one transaction.
Web Access across the app id upgrade (B491). From that release on, an
application is referred to by its id (apps.yaml id) instead of its name.
Accepted without a compatibility bump: while old and new brokers serve side by
side, a Web Access sign-in that is half done (a hand-off code, an application
session) and a still pending approval request filed under the name ask the user
to start again. An incident lock made on an application name keeps holding, and
a policy.yaml http_rules entry naming an application still matches, with a
warning at load.
Firewall
This section is not measured by any gate. make operations-check compares
port numbers, key names and command lines against the code; it has no way to
know what a firewall was actually asked to permit, and no reading of this
repository could tell it. Treat the table as the intent, and the firewall's own
configuration as the fact.
Direction is written from this gateway's point of view. In a deployment
installed by the Ansible roles these inbound rules are not hand-written: a
baseline role installs and starts firewalld, and each port is opened by the
role that owns it, restricted to the address ranges of one environment file
(ansible/README.md, „Tűzfal-baseline (isg_firewall)"). An empty source list
there means the port stays closed, never open to everyone.
| Port | Direction | Peer | Needed for |
|---|---|---|---|
| 8765 | inbound | operator workstations, and the other brokers | the admin UI; isg system check --fleet dials a peer's advertised endpoints |
| 8766 | inbound | service machines running the agent, and the other brokers | certificate renewal; the same fleet check |
| 8767 | inbound | the monitoring scraper, and the other brokers | health and metrics; leave closed if the scraper is local and there is no peer |
| 22 | inbound | operator workstations | brokered SSH login to the gateway |
| 22 | outbound | target hosts | the sessions the gateway brokers |
| LDAP | outbound | the directory | identity and group resolution |
| SMTP | outbound | the mail relay | MFA code delivery |
| PostgreSQL | outbound | the record store | shared session records and leases |
| S3 | outbound | the object store | session recording archival |
| OpenBao | outbound | the secret store | certificate signing, when delegated |
| JIRA | outbound | JIRA | ticket verification |
| IdP | outbound | the identity provider | SSO discovery, token and device endpoints |
| SIEM syslog | outbound | the SIEM | the audit export copy |
| 20514 | inbound | target hosts | forwarded break-glass log lines |
The eight unnumbered rows are unnumbered on purpose: their ports are whatever the operator wrote into the configuration keys named in the sections above, and printing a number here would invite somebody to open that one instead of the one their own deployment actually uses.