> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Privilege model

> Exactly what the ClickHouse Connector can and cannot do: outbound connections, ClickHouse grants, Kubernetes RBAC, attribution, and data minimization

This page is the security reference for the ClickHouse Connector: the complete set of connections it opens, the exact privileges it holds, what it structurally cannot do, and how every action is attributed. For how the pieces fit together, see [architecture](/docs/products/bring-your-own-cloud/connector/architecture).

<h2 id="what-the-connector-can-do">
  What the connector can do
</h2>

<h3 id="outbound-connections">
  Outbound connections
</h3>

This is the complete list of connections the connector opens. All of them originate inside your environment.

| Destination                            | Protocol                                   | Purpose                                                                                                                                                                                                                                        |
| -------------------------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Your org's connector API endpoint      | HTTPS with mTLS, every request HMAC-signed | `POST /v1/metrics`, `/v1/self-metrics`, `/v1/status`, `/v1/instance/sync`, `/v1/infra/sync`, `/v1/backup/sync`, `/v1/pcm/cert/renew`                                                                                                           |
| Your org's connector API endpoint      | Outbound WebSocket, `/v1/commands/ws`      | The troubleshooter's command channel, gated by support-session state                                                                                                                                                                           |
| Your org's enrollment endpoint         | HTTPS (enrollment token or HMAC; no mTLS)  | Token redemption and certificate signing (`/v1/pcm/cert/sign`) at install time                                                                                                                                                                 |
| Your ClickHouse instances              | ClickHouse native protocol                 | Read-only queries as `pcm_scraper` and `pcm_troubleshooter`, plus the scraper's log-flush statement (see [grants](#clickhouse-grants))                                                                                                         |
| The Kubernetes API server              | HTTPS                                      | Namespace-scoped reads and ServiceAccount token requests (both targets); exact-name read and update of the connector's own mTLS Secret to persist renewed certificates (Kubernetes installs only; a VM writes renewals to its local TLS files) |
| Your identity provider's JWKS endpoint | HTTPS                                      | Operator token validation, only when the session gateway is enabled                                                                                                                                                                            |

Inbound, the connector exposes only local health and metrics ports plus the opt-in session gateway. Nothing else listens, and ClickHouse Cloud never connects into your environment: it can only answer the troubleshooter's outbound WebSocket.

<h3 id="clickhouse-grants">
  ClickHouse grants
</h3>

Provisioning creates one read-only user per component. The single exception to pure reads is the scraper's `SYSTEM FLUSH LOGS` grant listed below, which cannot read or modify anything; it only forces log tables to persist entries they already buffer. The users are created with `IDENTIFIED WITH bcrypt_hash`, so only a salted bcrypt hash exists in the provisioning SQL; the plaintext password lives only in the credential file the daemon reads at runtime. The grants are exactly these, with the default table sets:

```sql theme={null}
CREATE USER IF NOT EXISTS `pcm_scraper` IDENTIFIED WITH bcrypt_hash BY '<bcrypt-hash>';

GRANT SELECT ON `system`.`asynchronous_metric_log` TO `pcm_scraper`;
GRANT SELECT ON `system`.`metric_log` TO `pcm_scraper`;
GRANT SELECT ON `system`.`server_settings` TO `pcm_scraper`;
GRANT SELECT ON `system`.`tables` TO `pcm_scraper`;
GRANT SELECT ON `system`.`warnings` TO `pcm_scraper`;
GRANT SELECT ON `system`.`user_directories` TO `pcm_scraper`;
GRANT READ ON REMOTE TO `pcm_scraper`;
GRANT SYSTEM FLUSH LOGS ON *.* TO `pcm_scraper`;
```

`READ ON REMOTE` is required because scrape queries wrap each system table in `clusterAllReplicas()`. `SYSTEM FLUSH LOGS` must be granted at the global scope because ClickHouse rejects narrower scopes for that privilege; ClickHouse only honors it for `*_log` system tables, so the grant is broader than the actual capability.

```sql theme={null}
CREATE USER IF NOT EXISTS `pcm_troubleshooter` IDENTIFIED WITH bcrypt_hash BY '<bcrypt-hash>';

GRANT SELECT ON `system`.`asynchronous_metrics` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`build_options` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`clusters` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`columns` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`databases` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`detached_parts` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`disks` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`events` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`formats` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`functions` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`grants` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`merges` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`metrics` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`mutations` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`parts` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`parts_columns` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`parts_summary` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`processes` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`replicas` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`replication_queue` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`roles` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`settings` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`settings_profile_elements` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`settings_profiles` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`storage_policies` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`table_engines` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`tables` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`users` TO `pcm_troubleshooter`;
GRANT SELECT ON `system`.`user_directories` TO `pcm_troubleshooter`;
```

The `system.user_directories` grant on both users exists for one diagnostic: `clicklink clctl preflight` runs with the connector's own credentials and checks how the instance stores its ClickHouse users (replicated or local). The table holds user-storage configuration metadata, not user data, and neither the scrape set nor the session table allowlist includes it, so no scrape or session output path reads it; without the grant, that one preflight check reports itself skipped and everything else proceeds.

Beyond per-table `SELECT`, the one system-level grant is the scraper's `SYSTEM FLUSH LOGS`: it forces the `*_log` system tables to persist buffered entries to disk so scrapes see current data, and does nothing else; ClickHouse only honors it for log tables even though it only accepts the grant at the global scope. There are no `INSERT`, DDL, user-management, settings, or process-control grants. When a second connector deployment shares an instance, its users carry a suffix (`pcm_scraper_<suffix>`) with the same grant sets.

<h3 id="kubernetes-rbac">
  Kubernetes RBAC
</h3>

The chart creates namespace-scoped Roles only; there is no ClusterRole or ClusterRoleBinding.

| Resources                                                                                       | Verbs                  | Scope                                                                                                        |
| ----------------------------------------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------ |
| `secrets`                                                                                       | `get`                  | Exact names only: the mTLS Secret, the HMAC Secret, and each per-instance access-bundle Secret               |
| `secrets`                                                                                       | `update`               | The mTLS Secret only, by exact name, so the daemons can persist the automatically renewed client certificate |
| `serviceaccounts/token`                                                                         | `create`               | Exact names only: the component's own ServiceAccount and each per-instance access-bundle ServiceAccount      |
| `pods`, `pods/log`, `pods/status`, `services`, `configmaps`, `events`, `persistentvolumeclaims` | `get`, `list`, `watch` | Troubleshooter only                                                                                          |
| `deployments`, `statefulsets`, `replicasets` (`apps`)                                           | `get`, `list`, `watch` | Troubleshooter only                                                                                          |

<h2 id="what-the-connector-cannot-do">
  What the connector cannot do
</h2>

* **No writes to ClickHouse data or state.** The grants above contain no `INSERT`, no DDL, and no user-management, settings, or process-control privileges; the single system-class grant, the scraper's `SYSTEM FLUSH LOGS`, only makes log tables persist what they already buffer. The connector cannot modify data, schemas, users, or settings.
* **No exec.** The RBAC contains no `pods/exec`; the connector cannot run commands in your pods.
* **No delete, no patch.** The RBAC allows two mutations: the exact-name `update` on the connector's own mTLS Secret, and `create` on `serviceaccounts/token`, which mints short-lived tokens for the connector's own ServiceAccounts and modifies no stored object.
* **No cluster scope.** Every Role is bound in a namespace; the connector cannot list or read resources outside the namespaces you granted.
* **Nothing inbound.** ClickHouse Cloud never opens a connection into your environment. The only command path is the troubleshooter's outbound WebSocket, and the troubleshooter refuses every command unless a support session you enabled is active. Even during a session, the scope is bounded on both sides: ClickHouse queries are limited to the table allowlist, with `query_log` and `text_log` denied by the validator regardless of configuration, and Kubernetes access is separately limited to the read-only views and pod logs the namespace-scoped Roles grant.

<h2 id="what-requires-your-action">
  What requires your action
</h2>

* **Support sessions.** Interactive troubleshooting happens only inside a session you enable, time-boxed to 4 hours by default and 24 hours at most. Disabling takes effect immediately. See [support sessions](/docs/products/bring-your-own-cloud/connector/support-sessions).
* **The operator allowlist.** Every gateway request must carry an OIDC token whose attested email is on your allowlist. An empty allowlist is closed. You manage the list; see the [configuration guide](/docs/products/bring-your-own-cloud/connector/configuration).
* **Gateway exposure.** The session gateway is off unless you enable it, and reachable only by port-forward unless you opt into an Ingress. On a VM, its self-signed certificate must be fingerprint-pinned by each operator before the session commands will talk to it.
* **Network egress.** Under an enforcing CNI, the connector has no egress until you allowlist the endpoint CIDRs in the chart's NetworkPolicy.

<h2 id="how-access-is-attributed">
  How access is attributed
</h2>

* **Deployment identity.** The mTLS client certificate's common name is your org ID, with a single DNS name bound to your endpoint host, so every API connection is attributable to your org. Renewal is automatic and in-daemon; no operator handles the key material.
* **Request integrity.** Every API request additionally carries an HMAC-SHA256 signature (`Authorization: HMAC-SHA256 AccessKey=..., Signature=..., Timestamp=...`) computed over the method, path, timestamp, and body hash, using the key pair issued at enrollment.
* **Operator identity.** Gateway calls are attributed to the email attested by the operator's OIDC ID token, verified against your identity provider's JWKS; a self-reported name is never trusted where a token is available.
* **Audit trail.** Every gateway call and every troubleshoot command, accepted or blocked, is appended to the NDJSON audit log: gateway entries with the attested operator email, VM local session changes with the invoking host user, and session commands with the org identity carried on the authenticated channel. Read it with `clicklink clctl troubleshoot audit tail`; see the [CLI reference](/docs/products/bring-your-own-cloud/connector/reference/cli).

<h2 id="data-minimization-defaults">
  Data minimization defaults
</h2>

* **`query_log` is excluded from scrapes by default.** Its columns carry raw SQL with literal values, which can contain personal data or secrets, so it does not leave your boundary unless you add it deliberately.
* **The troubleshooter reads only allowlisted tables**, and the validator denies `query_log` and `text_log` unconditionally, so query history is never readable. The default allowlist does include `system.processes` (live query text); trim the session table allowlist (`troubleshooter.allowedTables` on Kubernetes, `troubleshooter.allowed_tables` on a VM) if that must stay hidden during sessions.
* **All troubleshooter output is redacted** with built-in patterns for IPv4 and IPv6 addresses, bearer tokens, AWS access keys, emails, JWTs, SSH private keys, and connection-string credentials, plus any patterns you define. The daemon refuses to start with an invalid patterns file rather than run unredacted.
* **Credentials are minimized at rest.** Provisioning SQL contains bcrypt hashes, never plaintext passwords; the enrollment token is never written to the command line, disk, or logs; keys live in Kubernetes Secrets or files with mode 0600.
