# Security

> Harden a Proxus installation. What is encrypted, network ports to open and to close, HTTPS, default credentials, gateway trust, MCP access and a checklist.

Source: https://proxus.io/docs/operate/security

At the end of this guide, your Proxus installation has no default passwords left, only the ports it needs are
reachable, and users reach the hub over HTTPS. Work through the [checklist](#hardening-checklist) before the hub is
used in production, and again after every move to a new machine.

## Before you start

- The hub runs with Docker (see [Docker](https://proxus.io/docs/operate/docker)) or Kubernetes.
- You can sign in as **Admin**, edit `docker-compose.yml` and the [configuration file](https://proxus.io/docs/administer/configuration),
  and change the host's firewall.
- A [backup](https://proxus.io/docs/operate/backup-and-restore). Several steps below change credentials.

## What is encrypted

Proxus 2.0.0 uses TLS on every connection it opens or accepts, with certificates from the
[Certificate Store](https://proxus.io/docs/administer/certificates). The store is the platform's own certificate authority
(**Proxus Platform CA**), or your corporate CA if you choose one.

| Connection | Protection |
| --- | --- |
| Browsers and API clients to the hub | HTTPS on port `8443`, on by default. HTTP on `8080` stays for local access. See [HTTPS](#https). |
| Hub processes and edge gateways to the hub's NATS server | TLS on every port: `4222`, `7422` and, on Kubernetes HA, `6222`. Every connection checks the server against the platform CA. |
| Gateway install command | Downloaded over HTTPS and pinned to the hub's HTTPS key, so the script and the gateway's identity cannot be swapped on the way. See [Install a gateway](https://proxus.io/docs/connect/edge-gateways/install-a-gateway). |
| MQTT devices and subscribers to a gateway's broker | TLS on port `8883` with the gateway's own certificate. Port `1883` stays for devices without TLS. See [MQTT broker](#mqtt-broker). |
| Gateways to outbound targets | The server is always verified: its name must match the address and its certificate must chain to a trusted root. See [Outbound targets](https://proxus.io/docs/connect/outbound-targets). |
| Gateways to OPC UA servers | Only servers whose certificate is trusted in the Certificate Store. See [Certificates](https://proxus.io/docs/administer/certificates#trust-or-reject-an-opc-ua-server-certificate). |

The private keys of all these certificates are stored encrypted. The key that decrypts them is in the config folder
(`keys/STORE/certificate-store.key`) or in the environment variable `PROXUS_CERTIFICATE_STORE_KEY`. Back it up with
the config folder.

Users, roles and the Certificate Store are in the **Security** menu.

## Network ports

Ports of the shipped compose stack on the hub's host:

| Port | Service | Who needs to reach it |
| --- | --- | --- |
| `8443` (TCP) | Hub over HTTPS: web interface, REST API, MCP server, gateway install commands | Users, API clients, AI agents, gateway hosts during installation |
| `8080` (TCP) | Hub over HTTP | Local access only, or a reverse proxy. Close it to other machines once everyone uses HTTPS. |
| `7422` (TCP, TLS) | Message broker, connections from edge gateways | Every remote edge gateway host |
| `1883` (TCP) | MQTT broker of the **SYSTEM** gateway, without TLS | Only MQTT clients and devices that publish to Proxus and cannot use TLS |
| `8083` (TCP) | **SYSTEM** gateway: health check, HTTP telemetry input, metrics | Only devices that send data over HTTP, and your monitoring |
| `4222` (TCP, TLS) | Message broker, client port | Nobody outside the host. The hub and the SYSTEM gateway use it inside the compose network. |
| `8222` (TCP) | Message broker monitoring | Only your monitoring, or nobody |
| `5442` (TCP) | PostgreSQL | Nobody outside the host |
| `8123`, `9000`, `9004`, `9005` (TCP) | ClickHouse | Nobody outside the host |

An edge gateway host only connects out to the hub on port `7422` (and `8443` while it is installed). It accepts
connections on `8883`, `1883` and `8083` only if devices on its network publish to it.

The shipped compose file publishes `8883` for the **SYSTEM** gateway, so devices on the hub's network can use MQTT
over TLS without changes.

The shipped compose file publishes all of these ports on every network interface. Close the ones that nobody
outside the host needs: either in the host firewall, or by binding them to the loopback address in
`docker-compose.yml`, for example `"127.0.0.1:5442:5442"`. The services reach each other over the internal compose
network, so this does not affect Proxus. A complete list of ports and endpoints is in
[Ports](https://proxus.io/docs/reference/ports).

## HTTPS

The hub serves HTTPS on port `8443` by default, with TLS 1.2 or 1.3 and the default certificate of the
[Certificate Store](https://proxus.io/docs/administer/certificates). On a new installation this certificate is issued by the
platform CA and covers the machine's name, its IP addresses, `localhost` and the names in `[Service]`
`PublicHostNames`. Browsers trust it once the platform CA is installed on users' computers (**Download CA** in the
Certificate Store), or when your corporate CA signs the platform's certificates.

1. Add every name and address users type to reach the hub to `PublicHostNames`, for example
   `PublicHostNames = "proxus.lyonfoods.local, 203.0.113.10"`.
2. On an upgraded installation, renew the hub's old self-signed certificate: in **Security > Certificate Store**,
   select **Proxus (Self-Signed)** and select **Renew**.
3. Install the platform CA on users' computers, or [use your corporate CA](https://proxus.io/docs/administer/certificates#use-your-corporate-ca).

These `[Service]` keys control HTTPS:

| Key | Default | What it does |
| --- | --- | --- |
| `UI-HTTPS-Port` | `"8443"` | The HTTPS port. An empty value turns HTTPS off. |
| `RedirectHttpToHttps` | `"false"` | `"true"` sends every HTTP request to HTTPS and turns on HSTS. |
| `PublicHttpsPort` | not set | The port others reach HTTPS on, when it is published on another port of the host. |
| `PublicHostNames` | not set | Extra names and addresses for the hub's certificates, separated by commas. |

HTTP on `8080` stays on for local access. `RedirectHttpToHttps` is off by default because HSTS makes browsers use
HTTPS for every port of the host, including other services on the same machine. Turn it on when the hub has the
host to itself.

The shipped compose file publishes `8443`. If you publish HTTPS on another port of the host, for example
`"443:8443"`, set `PublicHttpsPort = "443"` so the hub knows the port others reach it on.

To publish the hub on the internet under your own domain with a certificate you already have, directly or
behind Cloudflare, follow [Serve the hub on your domain](https://proxus.io/docs/operate/custom-domain).

**On a reverse proxy.** You can also let your company's reverse proxy or load balancer handle TLS and forward to the
hub. The web interface keeps a live connection open, so the proxy must allow WebSocket connections and long
timeouts (the Kubernetes ingress uses one hour). Then publish the hub's ports only to the proxy.

## Default credentials to change

| Credential | Shipped value | How to change it |
| --- | --- | --- |
| Hub user **Admin** | Empty password | Sign in as **Admin**, **Platform > My Profile**, **Change My Password**. |
| Hub user **APIAdmin** (only on hubs upgraded from an earlier version) | A fixed password, the same in every installation | Open it on **Security > Users** and use **Reset Password**, or clear **Is Active** if you do not use it. Until then the hub writes a warning to **System Logs** at every start. |
| PostgreSQL | User, password and database `proxus` | Before the first start: `POSTGRES_PASSWORD` in the compose file and the password in `[Database]` `ConnectionString`. On a running installation, change the password in the database first (`ALTER USER`), then in the connection string. |
| ClickHouse | User `admin`, password `proxus` | `CLICKHOUSE_ADMIN_PASSWORD` and the health check in the compose file, and `ClickHousePassword` in `[ClickHouse]`. Plan this change; Proxus cannot store values while the passwords differ. |

API tokens are valid for one year by default (`TokenExpireTime = "1y"`). Shorten this if your policy requires it.

Database passwords set through `POSTGRES_PASSWORD` take effect only when the database volume is created. Keep the
database ports closed (see above) so the database is not reachable even with the default password.

## Edge gateway trust

Edge gateways and the hub talk through the message broker over TLS, without shared passwords:

- On the first start, the hub creates the broker's root credentials on the `config` volume (folder `nats/`). Only
  the hub reads them.
- When you install a gateway, the gateway host creates its own key and sends only the public part. The hub signs it
  with permissions for that one gateway. The resulting credentials work only for a gateway connection on port
  `7422`; a copy cannot be used as a normal broker client.
- The gateway's link to the hub is encrypted and checked against the platform CA. Each gateway also gets its own
  certificate from the Certificate Store for its HTTPS port and its MQTT broker. A gateway can only get its own.
- An install link is valid for one use and one hour, and the install command is pinned to the hub's HTTPS key.
- Removing or reinstalling a gateway revokes its old credentials at once.

Protect the `config` volume like a password store, and include it in every backup. It also holds the
Certificate Store key. See
[Install a gateway](https://proxus.io/docs/connect/edge-gateways/install-a-gateway).

## MQTT broker

The **SYSTEM** gateway's MQTT broker refuses clients without credentials (`[MQTTBroker]` `AllowAnonymous = false`).
Keep it that way.

Every gateway's broker also accepts MQTT over TLS on port `8883`, with the gateway's own certificate. Devices check
the broker against the platform CA (**Download CA** in the Certificate Store), and the host name they connect to
must be in the gateway's certificate. Once all devices use TLS, set `[MQTTBroker]` `PlainEnabled = false` so the
broker accepts TLS only. `TlsPort = 0` turns TLS off. If you set a `MasterToken`, use a long random value and treat it like a password. The
[UNS permissions](https://proxus.io/docs/administer/uns-permissions) of roles do not apply to MQTT clients. See
[MQTT](https://proxus.io/docs/develop/mqtt).

## MCP access for AI agents

The MCP server at `/mcp` on the hub lets AI agents read and change Proxus data with a user's permissions. Each agent
signs in as a Proxus user and can do only what that user's roles allow. The `[MCP]` section adds limits:

| Key | Shipped value | Recommendation |
| --- | --- | --- |
| `Enabled` | `true` | Set `false` if you do not use AI agents. |
| `AdminOnly` | `false` | `true` lets only users of an administrative role use MCP. |
| `AllowedMethods` | `"GET,POST,PATCH,PUT"` | `"GET"` for agents that may only read. |
| `BlockDelete` | `true` | Keep. |
| `IPWhitelist` | `[]` (all addresses) | List the addresses of your agent hosts, for example `["10.20.0.0/24"]`. |
| `ServerBaseUrl` | `""` | Set to the hub's external address, for example `"https://proxus.lyonfoods.local:8443"`, when agents connect from other machines. |
| `AnonymousDiscoveryMode` | `"ToolsAndOverview"` | `"Disabled"` hides the tool list from clients that have not signed in. |
| `AuditLogging` | `true` | Keep. |

Give each agent its own Proxus user with a role made for it, and limit that role with
[UNS permissions](https://proxus.io/docs/administer/uns-permissions). For the full setup, see [MCP](https://proxus.io/docs/develop/mcp).

## Hardening checklist

- **Admin** has a strong password. On an upgraded hub, **APIAdmin** has a new password or is disabled.
- The shared configuration folder, which holds the key that signs API tokens, is readable only by the people who run the hub.
- Every person has their own user. Nobody works as **Admin** day to day.
- Roles start from **Deny all by default** and give only what is needed. See [Users and roles](https://proxus.io/docs/administer/users-and-roles).
- Password rules in `[Security.Password]` match your policy, and new users have **Force Password Change** ticked.
- Account lockout is on (`[Security]` `Lockout.Enabled = true`).
- The REST API signing key is a new random value.
- PostgreSQL and ClickHouse passwords are changed, or their ports are reachable only from the host.
- Only `8443`, `7422` and, where needed, `8883`, `1883` and `8083` are reachable from other machines.
- Users reach the hub over HTTPS, and the platform CA (or your corporate CA) is trusted on their computers.
- The Certificate Store key (`keys/STORE/certificate-store.key`, or `PROXUS_CERTIFICATE_STORE_KEY`) is backed up
  with the config folder.
- MQTT devices use port `8883`, and `PlainEnabled = false` once none needs `1883`.
- MCP is off, or limited by `AdminOnly`, `AllowedMethods` and `IPWhitelist`.
- Metrics are off, or ports `8080`/`8443` and `8083` are reachable only by your monitoring.
- The `config` volume and the databases are backed up, and the backups are stored away from the host.
- Only administrators can reach the host: the hub container can control Docker through the mounted Docker socket.

## What's next

- [Users and roles](https://proxus.io/docs/administer/users-and-roles)
- [Certificates](https://proxus.io/docs/administer/certificates)
- [Monitoring](https://proxus.io/docs/operate/monitoring)
