From 5fe1cb8ff725ed81c3629c698f01c4c97ac555a6 Mon Sep 17 00:00:00 2001 From: Volodymyr Kazmyrchuk Date: Tue, 27 Jan 2026 12:20:21 +0100 Subject: [PATCH] docs: add CLICKHOUSE_TLS_MODE documentation - Add CLICKHOUSE_TLS_MODE to README mTLS section with valid options - Update ClickHouseConfig docstring with TLS_MODE variable feat: add CLICKHOUSE_TLS_MODE support for proxy/strict modes docs: add mTLS configuration documentation Document new environment variables for mutual TLS authentication: - CLICKHOUSE_CA_CERT - CLICKHOUSE_CLIENT_CERT - CLICKHOUSE_CLIENT_CERT_KEY Include example configuration for mTLS setup. feat: add mTLS (mutual TLS) support Add support for client certificate authentication (mTLS) via new environment variables: - CLICKHOUSE_CA_CERT: Path to CA certificate file - CLICKHOUSE_CLIENT_CERT: Path to client certificate file - CLICKHOUSE_CLIENT_CERT_KEY: Path to client private key file These parameters are passed to clickhouse-connect's get_client() function to enable secure connections to ClickHouse servers that require mutual TLS authentication. --- README.md | 75 ++++++++++++++++++++++++++++++++++++++- mcp_clickhouse/mcp_env.py | 57 +++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dc2c1cf8..e90924bd 100644 --- a/README.md +++ b/README.md @@ -308,7 +308,7 @@ The following environment variables are used to configure the ClickHouse and chD * `CLICKHOUSE_VERIFY`: Enable/disable SSL certificate verification * Default: `"true"` * Set to `"false"` to disable certificate verification (not recommended for production) - * TLS certificates: The package uses your operating system trust store for TLS certificate verification via `truststore`. We call `truststore.inject_into_ssl()` at startup to ensure proper certificate handling. Python’s default SSL behavior is used as a fallback only if an unexpected error occurs. + * TLS certificates: The package uses your operating system trust store for TLS certificate verification via `truststore`. We call `truststore.inject_into_ssl()` at startup to ensure proper certificate handling. Python's default SSL behavior is used as a fallback only if an unexpected error occurs. * `CLICKHOUSE_CONNECT_TIMEOUT`: Connection timeout in seconds * Default: `"30"` * Increase this value if you experience connection timeouts @@ -335,6 +335,30 @@ The following environment variables are used to configure the ClickHouse and chD * Default: `"true"` * Set to `"false"` to disable ClickHouse tools when using chDB only +##### mTLS (Mutual TLS) Variables + +These variables enable client certificate authentication for ClickHouse servers that require mutual TLS: + +* `CLICKHOUSE_CA_CERT`: Path to CA certificate file + * Default: None + * Set this to specify a custom CA certificate for SSL verification + * Example: `/path/to/ca.crt` +* `CLICKHOUSE_CLIENT_CERT`: Path to client certificate file + * Default: None + * Required for mTLS authentication + * Can be a `.pem` file containing both the certificate and private key + * Example: `/path/to/client.crt` or `/path/to/client.pem` +* `CLICKHOUSE_CLIENT_CERT_KEY`: Path to client private key file + * Default: None + * Optional if `CLICKHOUSE_CLIENT_CERT` is a `.pem` file containing both the certificate and private key + * Example: `/path/to/client.key` +* `CLICKHOUSE_TLS_MODE`: TLS mode for client certificate authentication + * Default: None (auto-detected based on `CLICKHOUSE_CLIENT_CERT`) + * Valid options: + * `"mutual"` - Use client certificate for authentication (default when `CLICKHOUSE_CLIENT_CERT` is set) + * `"proxy"` - TLS termination at proxy, use Basic Auth with client certs for TLS only + * `"strict"` - Strict TLS mode with Basic Auth + #### chDB Variables * `CHDB_ENABLED`: Enable/disable chDB functionality @@ -382,6 +406,55 @@ CLICKHOUSE_PASSWORD= # Uses secure defaults (HTTPS on port 8443) ``` +For ClickHouse with mTLS (Mutual TLS): + +```env +# Required variables +CLICKHOUSE_HOST=your-secure-clickhouse.example.com +CLICKHOUSE_PORT=8443 +CLICKHOUSE_USER=your-user +CLICKHOUSE_PASSWORD=your-password + +# mTLS configuration +CLICKHOUSE_SECURE=true +CLICKHOUSE_CA_CERT=/path/to/ca.crt +CLICKHOUSE_CLIENT_CERT=/path/to/client.crt +CLICKHOUSE_CLIENT_CERT_KEY=/path/to/client.key + +# Or if using a combined .pem file: +# CLICKHOUSE_CLIENT_CERT=/path/to/client.pem +``` + +Example Claude Desktop configuration with mTLS: + +```json +{ + "mcpServers": { + "mcp-clickhouse": { + "command": "uv", + "args": [ + "run", + "--with", + "mcp-clickhouse", + "--python", + "3.10", + "mcp-clickhouse" + ], + "env": { + "CLICKHOUSE_HOST": "your-secure-clickhouse.example.com", + "CLICKHOUSE_PORT": "8443", + "CLICKHOUSE_USER": "your-user", + "CLICKHOUSE_PASSWORD": "your-password", + "CLICKHOUSE_SECURE": "true", + "CLICKHOUSE_CA_CERT": "/path/to/ca.crt", + "CLICKHOUSE_CLIENT_CERT": "/path/to/client.crt", + "CLICKHOUSE_CLIENT_CERT_KEY": "/path/to/client.key" + } + } + } +} +``` + For chDB only (in-memory): ```env diff --git a/mcp_clickhouse/mcp_env.py b/mcp_clickhouse/mcp_env.py index 1ea53cd6..678f5bc6 100644 --- a/mcp_clickhouse/mcp_env.py +++ b/mcp_clickhouse/mcp_env.py @@ -45,6 +45,10 @@ class ClickHouseConfig: CLICKHOUSE_DATABASE: Default database to use (default: None) CLICKHOUSE_PROXY_PATH: Path to be added to the host URL. For instance, for servers behind an HTTP proxy (default: None) CLICKHOUSE_ENABLED: Enable ClickHouse server (default: true) + CLICKHOUSE_CA_CERT: Path to CA certificate file for SSL verification (default: None) + CLICKHOUSE_CLIENT_CERT: Path to client certificate file for mTLS authentication (default: None) + CLICKHOUSE_CLIENT_CERT_KEY: Path to client private key file for mTLS authentication (default: None) + CLICKHOUSE_TLS_MODE: TLS mode for client certificate usage - "mutual", "proxy", or "strict" (default: None) """ def __init__(self): @@ -132,6 +136,46 @@ def send_receive_timeout(self) -> int: def proxy_path(self) -> str: return os.getenv("CLICKHOUSE_PROXY_PATH") + @property + def ca_cert(self) -> Optional[str]: + """Get the path to CA certificate file for SSL verification. + + Default: None + """ + return os.getenv("CLICKHOUSE_CA_CERT") + + @property + def client_cert(self) -> Optional[str]: + """Get the path to client certificate file for mTLS authentication. + + Default: None + """ + return os.getenv("CLICKHOUSE_CLIENT_CERT") + + @property + def client_cert_key(self) -> Optional[str]: + """Get the path to client private key file for mTLS authentication. + + This is optional if the client_cert file contains both the certificate + and the private key (e.g., a combined .pem file). + + Default: None + """ + return os.getenv("CLICKHOUSE_CLIENT_CERT_KEY") + + @property + def tls_mode(self) -> Optional[str]: + """Get the TLS mode for client certificate usage. + + Valid values: + - 'mutual': Use client certificate for authentication (default when client_cert is set) + - 'proxy': TLS termination at proxy, use Basic Auth with client certs for TLS only + - 'strict': Strict TLS mode, use Basic Auth with client certs for TLS only + + Default: None (auto-detected by clickhouse-connect) + """ + return os.getenv("CLICKHOUSE_TLS_MODE") + def get_client_config(self) -> dict: """Get the configuration dictionary for clickhouse_connect client. @@ -162,6 +206,19 @@ def get_client_config(self) -> dict: if self.proxy_path: config["proxy_path"] = self.proxy_path + # Add mTLS configuration if set + if self.ca_cert: + config["ca_cert"] = self.ca_cert + + if self.client_cert: + config["client_cert"] = self.client_cert + + if self.client_cert_key: + config["client_cert_key"] = self.client_cert_key + + if self.tls_mode: + config["tls_mode"] = self.tls_mode + return config def _validate_required_vars(self) -> None: