# How to Configure mTLS Client Certificates in the GeoLibre Tauri Desktop Build

> Learn how GeoLibre configures mTLS client certificates in its Tauri desktop build using environment variables. Discover certificate loading and TLS backend selection.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-22

---

**GeoLibre configures mutual TLS (mTLS) client certificates through three environment variables that the Tauri desktop process reads at startup in [`src-tauri/src/lib.rs`](https://github.com/opengeos/GeoLibre/blob/main/src-tauri/src/lib.rs), where the `client_identity()` function loads the certificate material and `build_guarded_http_client()` selects the appropriate TLS backend (rustls for PEM, native-tls for PKCS-12).**

The GeoLibre desktop application uses a native Rust HTTP client to perform privileged network operations such as tile fetching and OGC GetCapabilities requests. According to the opengeos/GeoLibre source code, this client is built once per process lifetime and cached, with mTLS configuration driven entirely by environment variables that determine the certificate format and trust store.

## Environment Variable Configuration

The Tauri backend recognizes three environment variables for TLS configuration. These are read during the initialization of the guarded HTTP client in [`apps/geolibre-desktop/src-tauri/src/lib.rs`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src-tauri/src/lib.rs) (lines 718–724).

| Variable | Purpose |
|----------|---------|
| `GEOLIBRE_HTTP_CLIENT_CERT` | Path to the client certificate file. Accepts either a PEM bundle (certificate chain + **unencrypted** PKCS#8 private key) or a PKCS-12 bundle (`.p12` or `.pfx`). |
| `GEOLIBRE_HTTP_CLIENT_CERT_PASSWORD` | Passphrase for a PKCS-12 bundle. When present, this forces the PKCS-12 loading path. |
| `GEOLIBRE_HTTP_CA_CERT` | Optional path to a PEM bundle of additional CA certificates to trust alongside the OS certificate store. |

If `GEOLIBRE_HTTP_CLIENT_CERT` is omitted or empty, the client operates without client authentication unless a password is supplied without a corresponding certificate path, which triggers a configuration error.

## Certificate Loading Logic in [`src-tauri/src/lib.rs`](https://github.com/opengeos/GeoLibre/blob/main/src-tauri/src/lib.rs)

The core logic for mTLS setup resides in the `client_identity()` function (lines 966–1032). This function follows a deterministic sequence to validate inputs and load cryptographic material.

### Step 1: Password Detection

The function first checks for the `GEOLIBRE_HTTP_CLIENT_CERT_PASSWORD` variable. An empty string is treated as unset, while non-UTF-8 passwords cause an immediate error (lines 73–80). The presence of a password forces the loader to treat the file as PKCS-12, regardless of file extension.

```rust
let password = match env::var(HTTP_CLIENT_CERT_PASSWORD_ENV) { … };

```

### Step 2: Path Validation

If the certificate path is missing but a password was provided, the code aborts with a clear error indicating misconfiguration (lines 85–92). This prevents attempting to decrypt a non-existent file.

### Step 3: Format Detection

The helper `client_cert_is_pkcs12(&path, password.is_some())` determines the parsing strategy by checking for the `.p12` or `.pfx` extension, or by confirming that a password was provided (lines 41–43).

### Step 4: Backend-Specific Loading

**PKCS-12 Path:** When loading a PKCS-12 bundle, the code uses `reqwest::Identity::from_pkcs12_der` with the native-tls backend (SChannel on Windows, Secure Transport on macOS, OpenSSL on Linux). Note that Android does not support PKCS-12 client certificates in this implementation, which triggers an explicit error (lines 1014–1022).

**PEM Path:** For PEM files, the code uses `reqwest::Identity::from_pem` with the **rustls** backend, which reads the certificate chain and private key directly (lines 1024–1030).

```rust
// PKCS-12 loading (native-tls)
let identity = reqwest::Identity::from_pkcs12_der(&cert_bytes, password)?;

// PEM loading (rustls)
let identity = reqwest::Identity::from_pem(&cert_bytes)?;

```

### Step 5: Client Initialization and Caching

In `build_guarded_http_client()` (lines 1068–1075), the builder switches TLS backends based on the presence and format of the client identity:

- **PEM identity** → `use_rustls_tls()`
- **PKCS-12 identity** → `use_native_tls()`

The resulting `reqwest::blocking::Client` is cached in a `std::sync::OnceLock` at lines 48–53, ensuring that the mTLS configuration remains static for the entire desktop session and is reused across all network requests.

## Supported Certificate Formats

GeoLibre supports two distinct certificate formats, each with specific requirements:

- **PEM Format:** Must contain the certificate chain concatenated with an **unencrypted** PKCS#8 private key. This path leverages the rustls TLS stack.
- **PKCS-12 Format:** Must be a `.p12` or `.pfx` file encrypted with a passphrase. This path leverages the native-tls stack and is not available on Android builds.

The design intentionally separates these formats by backend to maximize platform compatibility while maintaining security constraints.

## Practical Configuration Examples

Configure mTLS before launching the GeoLibre desktop application by exporting the environment variables in your shell:

```bash

# Option 1: PEM certificate (unencrypted private key)

export GEOLIBRE_HTTP_CLIENT_CERT=/path/to/client.pem

# Option 2: PKCS-12 bundle with passphrase

export GEOLIBRE_HTTP_CLIENT_CERT=/path/to/client.p12
export GEOLIBRE_HTTP_CLIENT_CERT_PASSWORD=secretpass

# Optional: Trust a private CA certificate

export GEOLIBRE_HTTP_CA_CERT=/path/to/extra-ca.pem

# Launch the application

./geolibre-desktop

```

Within the Rust codebase, the guarded client is accessed as a singleton:

```rust
// Returns the cached client instance with mTLS configured
let client = guarded_http_client()?;
let resp = client.get("https://secure.tileserver.com/tiles")
                 .timeout(Duration::from_secs(30))
                 .send()?;

```

## Architectural Security Context

The `guarded_http_client` serves as the single entry point for all privileged network operations in the native layer. Beyond mTLS, this client incorporates a **SSRF guard** that blocks connections to private, link-local, and multicast addresses via the `is_disallowed_ip` check (lines 41–78 in the same file).

By default, the client trusts the OS certificate store through the `rustls-tls-native-roots` feature, plus any additional CAs specified via `GEOLIBRE_HTTP_CA_CERT`. Because the `reqwest::blocking::Client` is cached in a `OnceLock`, the mTLS configuration is immutable for the process lifetime—any changes to certificates require a full application restart to take effect.

## Summary

- GeoLibre uses **three environment variables** (`GEOLIBRE_HTTP_CLIENT_CERT`, `GEOLIBRE_HTTP_CLIENT_CERT_PASSWORD`, `GEOLIBRE_HTTP_CA_CERT`) to configure mTLS.
- Certificate loading is handled by the **`client_identity()`** function in [`src-tauri/src/lib.rs`](https://github.com/opengeos/GeoLibre/blob/main/src-tauri/src/lib.rs) (lines 966–1032).
- The system automatically selects between **rustls** (for PEM) and **native-tls** (for PKCS-12) backends based on file format and password presence.
- The HTTP client is **cached for the process lifetime** in a `OnceLock`, making mTLS configuration static after startup.
- **Android builds do not support PKCS-12** client certificates and will emit an explicit error if attempted.

## Frequently Asked Questions

### What environment variables configure mTLS in GeoLibre Desktop?

You must set `GEOLIBRE_HTTP_CLIENT_CERT` to the file path, optionally set `GEOLIBRE_HTTP_CLIENT_CERT_PASSWORD` if using a PKCS-12 bundle, and optionally set `GEOLIBRE_HTTP_CA_CERT` to trust additional certificate authorities. These are read at startup by the Tauri process in [`src-tauri/src/lib.rs`](https://github.com/opengeos/GeoLibre/blob/main/src-tauri/src/lib.rs).

### Why does GeoLibre use different TLS backends for PEM and PKCS-12 certificates?

The implementation uses **rustls** for PEM files via `reqwest::Identity::from_pem` and **native-tls** for PKCS-12 files via `reqwest::Identity::from_pkcs12_der`. This separation ensures maximum cross-platform compatibility, as rustls handles PEM parsing efficiently while native-tls provides robust PKCS-12 support through OS-specific cryptographic libraries (SChannel, Secure Transport, or OpenSSL).

### Can I change the client certificate without restarting the GeoLibre desktop application?

No. The HTTP client is built once and stored in a `std::sync::OnceLock` when the Tauri process starts. Because the `reqwest::blocking::Client` is cached and immutable, any changes to the `GEOLIBRE_HTTP_CLIENT_CERT` environment variable require a full application restart to take effect.

### Does GeoLibre support encrypted private keys in PEM files?

No. According to the source code in [`src-tauri/src/lib.rs`](https://github.com/opengeos/GeoLibre/blob/main/src-tauri/src/lib.rs), PEM-formatted client certificates must contain an **unencrypted** PKCS#8 private key. If you require passphrase protection for your private key, you must use a PKCS-12 bundle (`.p12` or `.pfx`) with the `GEOLIBRE_HTTP_CLIENT_CERT_PASSWORD` variable instead.