Monopam architecture and installation
What changed
Every content update is versioned. Select a version to compare it with the one before.
Version 1 → 2
Update4 lessons updatedduration
Content update. Enrolled learners were notified.
Duration changed from 11 to 15 minutes.
Monopam keeps no local passwords. A user is sent to the identity provider configured for the tenant, normally Monosign, and comes back with a signed result that Monopam stores in an auth cookie. That one design choice answers several planning questions at once.
| Question | Answer | Why |
| --- | --- | --- |
| Where do we enforce MFA? | At the identity provider | Monopam has no MFA of its own. Credentials and MFA are the provider's job, so Monosign access policies apply to privileged users too |
| Where do groups and permissions come from? | From the provider, inside the auth cookie | Monopam reads them from the cookie on every request |
| Why did a role change not apply yet? | The user has to sign in again | The cookie still carries the old groups |
::diagram pam-signin-path
### The proxy trap
Because groups and permissions travel in the cookie, the response that ends the sign-in is large, and it grows with every group a user belongs to. A reverse proxy with small response-header buffers rejects that response.
A typical case: a company puts nginx in front of Monopam. Most people sign in fine. The platform team, who are members of dozens of groups, get `502 Bad Gateway` on the way back from Monosign, and the nginx log says `upstream sent too big header`. Nothing is wrong with their accounts. The fix is to enlarge the response-header buffers (for nginx, `proxy_buffer_size` and `proxy_buffers`) on **every** proxy hop in front of Monopam. One forgotten hop is enough to break sign-in, and only for the users with many groups, which makes it look like a permission problem.
> Tip: when sign-in fails only for heavily privileged users, look at the proxies before you look at the policies.
### Licence usage flows through Monosign
Monopam never calls Monofor services itself. Every 15 minutes it counts each tenant's licensed modules and sends only the numbers, no names and no item contents, to the tenant's Monosign. Monosign adds them to its own licence heartbeat. The Monosign REST API key that Monopam uses needs the permission `System.API.System.Usage`. An installation upgraded from an older release keeps working without it, but reports nothing: the Monopam log shows `License usage could not be reported to Monosign` and the licence portal shows no Monopam usage until the permission is granted.
### What to check first
| Symptom | Check first |
| --- | --- |
| `502 Bad Gateway` on the way back from Monosign, only for users in many groups | The response-header buffers on every proxy hop |
| A new role or group is not applied | Whether the user has signed in again since the change |
| The licence portal shows no Monopam usage | The permission `System.API.System.Usage` on the Monosign REST API key, and the log line `License usage could not be reported to Monosign` |
The Monopam web server does not open sessions to targets. A **gateway** does: it sits where it can reach the targets and carries the session back to Monopam. So when production servers live in an isolated segment, you do not open ports or a VPN into the segment. You deploy a gateway that can reach it and let sessions flow through that gateway. Segmentation stays intact and every session stays brokered and recorded.
::diagram pam-gateway-path
### Sites: location-aware routing
A **site** is a location such as a plant, a branch or a data center. Gateways and resources are assigned to it (`Site` on the gateway, `Advanced` > `Site` on the resource). Sites are opt-in: an installation that never creates one behaves as before. `Unassigned` is the default.
Monopam picks the gateway for a connection in a strict order:
| Order | Rule | Result |
| --- | --- | --- |
| 1 | A gateway is selected explicitly on the resource or on the policy | That selection always wins |
| 2 | The resource is assigned to a site that has gateways | Only the site's gateways are used |
| 3 | Neither | Any active gateway |
::diagram pam-gateway-order
Two incidents follow directly from this table.
**"Gateway not found" after tidying up.** A team creates the site "Plant Bursa", assigns the plant's gateway and moves forty resources into the site. Until then those resources could use any active gateway. Now they may use only the site's gateways. If that gateway is inactive, or is not of the connection type needed, every connection fails with `Gateway not found`. The `Gateways` badge in the `Sites` list shows the number of active gateways and turns amber at zero. Sites never decide who may connect, only which gateway is used.
**A resource that ignores its site.** A server is assigned to the Istanbul site, whose gateways are healthy, yet its sessions run through Ankara. Rule 1 explains it: a gateway selected long ago on the resource or its policy still wins. Clear the explicit selection and the site takes over.
### A detail for database resources
Users of the database proxy connect their client to the gateway's proxy port, which the connect window shows. The `Port` field of the database resource is something else: it is the database's own port (5432 for PostgreSQL), which the proxy uses to reach the backend. Entering the proxy port there makes the proxy call itself and connections fail.
> Tip: when a connection fails with `Gateway not found`, walk the order from the top. Is a gateway selected explicitly on the resource or on its policy? Is the resource in a site? Does the `Gateways` badge of that site show an active gateway, and is it of the connection type you need?
One Monopam installation is **one database, one Redis and one vault key**. It can still run on several app servers. A service provider, for example, keeps a main server for most customers and adds a dedicated server for one large customer. The tenant of a request is derived from the host name of the request only, and a signed-in session is bound to the tenant it was issued for.
Each app server has two independent lists, set in its environment:
| Setting | Decides | If it is wrong |
| --- | --- | --- |
| `MONOPAM_SERVED_TENANTS` | Which tenants this server answers web requests for | A host that maps to a tenant outside the list gets a page saying the tenant is not served by this server |
| `MONOPAM_JOB_TENANTS` | For which tenants this server runs background jobs: rotation, alerts, syncs, agent updates | A tenant on two servers has its jobs run twice. A tenant on no server has its jobs wait in the queue |
| `MONOPAM_VAULT_KEY` | The key that decrypts vault data | A server with a different key cannot decrypt, so connections fail |
::diagram pam-multi-server
The first two are deliberately separate. Serving a tenant does not make a server run its jobs, and the reverse.
### Worked example
The provider adds server B for the tenant "acme" and points acme's DNS at it.
1. Users see "Tenant 'acme' is not served by this server." Database and Redis are healthy. Server B simply does not list acme in `MONOPAM_SERVED_TENANTS`. Add it.
2. A week later acme's passwords rotate twice and its alert emails arrive in duplicate. Server A still lists acme in `MONOPAM_JOB_TENANTS` and server B now lists it too. A tenant's jobs belong on **exactly one** server. Remove acme from one of them.
3. Both servers, and every gateway, must hold the same `MONOPAM_VAULT_KEY` and reach the same Redis. The two tenant lists are the part that differs per server.
### What a dedicated database does not give you
A tenant can be given its own database. Customers sometimes read that as a security wall between their data and the installation's other app servers. It is not: the vault key and the control plane are shared by the whole installation. Say so plainly when it comes up. A customer who needs that kind of separation needs a separate installation.
### What to check first
| Symptom | Check first |
| --- | --- |
| The page says the tenant is not served by this server | `MONOPAM_SERVED_TENANTS` on the server the host name points to |
| Rotations or alert emails happen twice | `MONOPAM_JOB_TENANTS` on every server: the tenant is listed on two of them |
| A tenant's jobs wait in the queue | `MONOPAM_JOB_TENANTS`: the tenant is listed on no server |
| Connections fail through one server or gateway only | `MONOPAM_VAULT_KEY` there differs from the rest |
The Monofor agent runs on a Windows, Linux or macOS host and serves local operations such as account discovery and script runs. Four moments in its life need an administrator's judgement.
::diagram pam-agent-lifecycle
### 1. Enrollment
`Import Resource with Agent` generates a one-line install script with a **setup key** baked in. The key is valid for 24 hours and can enroll many machines. Anyone who holds the script and can reach Monopam can enroll a machine into your tenant until the key expires, so treat the script like a credential: hand it to the person doing the install, do not park it in a wiki, and generate a fresh one next time.
### 2. Connectivity
| Path | Direction | Use it when |
| --- | --- | --- |
| Agent Channel | The agent dials out to Monopam and keeps the connection open. Monopam sends requests down it | Always preferred. Works behind NAT and with no inbound firewall rule |
| Inbound listener | A gateway calls the agent | Fallback, only where a gateway can reach the host |
The `Connection` column on the `Agents` page shows `Channel` or `Inbound` as of that moment.
### 3. Trust
Every request from Monopam to an agent is signed. The agent pinned Monopam's public key at enrollment and checks each signature, a timestamp and a single-use number, so a stolen agent API key alone cannot drive an agent. Two operational consequences:
- **Clocks.** Agent and server clocks must be within 300 seconds. If a freshly enrolled agent answers 401 to everything although enrollment worked, check time synchronization on both ends first.
- **Restores.** Monopam's private signing key is protected by the application's data-protection key. Restore Monopam on a new host without that key and the signing key cannot be read, a new one is generated, and every agent rejects it until it is enrolled again. Put the data-protection key in your backup plan.
### 4. Updates
`Update agent` on a row, or `Update all agents`, tells agents which version to install. The agent downloads the package, verifies its checksum, replaces its own binary and restarts. Configuration and enrollment are not touched, and nothing updates on its own.
Plan for two things. A fleet update is paced at five agents per tenant every 30 seconds, so a hundred hosts take about ten minutes. And each agent restarts for a few seconds, so an operation running through it at that moment fails and must be retried. An agent with `"AllowUpdate": false` in its `config.json` refuses and says so in the job result: change the setting on the host or update with the installer.
### What to check first
| Symptom | Check first |
| --- | --- |
| A freshly enrolled agent answers 401 to everything | Time synchronization on both ends: the clocks must be within 300 seconds |
| Every agent rejects Monopam after a restore on a new host | The data-protection key was not restored, so a new signing key was generated. The agents must be enrolled again |
| An agent refuses `Update agent` | `"AllowUpdate": false` in its `config.json`. The job result says so |
| An operation failed while the fleet was updating | The agent was restarting at that moment. Run the operation again |