# SSH access agent packaging The access agent runs on the Site host as `pharos-ssh-access` with the systemd unit [`pharos-ssh-access-agent.service`](pharos-ssh-access-agent.service). It executes `python -m metallo.services.ssh_access_agent` from a dedicated Python 3.13 virtual environment with the Metallo wheel. It has no root, sudo, Slurm, Waldur or database authority. | Setting | Source | | --- | --- | | Site API URL, CA, LDAP URI, CA, bind DN, people base, intervals | `/opt/pharos-ssh-access-agent/agent.env` ([example](agent.env.example)) | | Native Site bearer (`ssh-access@`) | `/etc/pharos-ssh-access-agent/credentials/site-token`, copied into the service by `LoadCredential=` | | LDAP identity on the same host | Unix peer credentials through `ldapi` and SASL EXTERNAL | The agent reads the bearer from `CREDENTIALS_DIRECTORY` for each request. With `LoadCredential=`, systemd snapshots the source file when the service starts; changing the source while the unit is running does not replace the service credential. The HPC installer therefore stages a complete release and restarts the service during controlled activation. The supported rotation path is a maintenance operation, not a hot in-place file replacement. `ldapi:///run/slapd/ldapi` uses SASL EXTERNAL without a bind DN or password. `ldapi:///` selects the same default socket; percent-encoded absolute paths are also accepted. The service user must be able to reach the socket. The directory administrator maps its exact peer identity to the restricted access-agent DN with `olcAuthzRegexp` on `cn=config`, substituting the service user's numeric GID and UID: ```ldif olcAuthzRegexp: "^gidNumber=[+]uidNumber=,cn=peercred,cn=external,cn=auth$" "cn=pharos-ssh-access,ou=services,dc=example" ``` This mapping grants only the ACL rights below. The agent needs no directory password or management access. For a remote directory, use `ldaps://`, set `SSH_ACCESS_LDAP_BIND_DN` and optionally `SSH_ACCESS_LDAP_CA_FILE`, and add a systemd drop-in under `pharos-ssh-access-agent.service.d/`: ```ini [Service] LoadCredential=ldap-password:/opt/pharos-ssh-access-agent/ldap-password ``` The LDAP password is read on each new connection. LDAPS verifies the certificate against the configured CA or system trust. The URI must name a DNS name in the certificate's subject alternative names. The unit sets `NoNewPrivileges`, `ProtectSystem=strict`, `PrivateTmp`, an empty capability set and a restricted address family list. [`directory/openssh-lpk.ldif`](directory/openssh-lpk.ldif) adds the schema SSSD reads. [`directory/access.ldif.template`](directory/access.ldif.template) sets one writer per attribute under `ou=pharos`: `cn=pharos-ssh-access` writes only `sshPublicKey` and the `ldapPublicKey` value of `objectClass`, and reads `uid`, `uidNumber`, `entryUUID`, `entryCSN` and `objectClass`; `cn=pharos-agent` writes entries, POSIX attributes and groups but only reads `sshPublicKey`. The access agent adds the auxiliary class with the first key and removes it with the last key in the same atomic modify. This works with `MUST` and `MAY` schemas; a Site agent creating an entry without keys must omit the class under `MUST`. OpenLDAP applies no attribute rules when an entry is added, so the Site agent's bind can still create an entry carrying keys; the agent's snapshot pass reports such unmanaged values as an operational alert and never removes them. `SSH_ACCESS_SITE_API_URL` is the separate Site API origin, for example `https://site.staging.pharosfactory.ai`. The agent appends `/v1/site/ssh-access/...` itself and must not append a path prefix. The public edge routes this hostname to the Site API backend while browser traffic remains on its own origins. `SSH_ACCESS_SITE_API_CA_FILE` contains public trust only; certificate verification, hostname verification and the agent's scoped `psb_` bearer are independent controls. Native requests require no browser OIDC callback. The HPC installer stages this configuration and the native bearer in the candidate release and selects them with an installer-owned systemd drop-in. The reusable unit's standalone `/etc` defaults remain unchanged for the laboratory.