# cert-trust

[![PyPI version](https://badge.fury.io/py/cert-trust.svg)](https://badge.fury.io/py/cert-trust)
[![CI](https://github.com/chuongmep/cert-trust/workflows/CI/badge.svg)](https://github.com/chuongmep/cert-trust/actions)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Easy CA certificate management for Python and curl.

## Introduction

If you work in a corporate environment, you've probably seen this error:

```
curl: (60) SSL certificate problem: unable to get local issuer certificate
requests.exceptions.SSLError: certificate verify failed
```

This happens when your company's firewall intercepts HTTPS traffic using a custom CA certificate. Instead of disabling SSL verification (insecure!) or manually configuring every tool, `cert-trust` handles it for you.

One command fixes Python requests, urllib3, httpx, and curl. No code changes needed.

## Installation

```bash
pip install cert-trust[all]
```

Requires Python 3.10 or later.

## Quick Start

Enable system CA certificates:
```bash
cert-trust enable
```

Check if it's working:
```bash
cert-trust status
```

Add your company's CA certificate:
```bash
cert-trust add /path/to/company-ca.crt
```

Test an HTTPS connection:
```bash
cert-trust verify https://internal.company.com
```

Disable when not needed:
```bash
cert-trust disable
```

## How It Works

**Python:** Installs a .pth file that automatically runs at Python startup. This injects system CA certificates into the SSL context. Works with requests, urllib3, httpx, and any library using Python's ssl module.

**curl:** Adds certificates to your OS certificate store.

| Platform | Method | Admin Required |
|----------|--------|----------------|
| Windows | User Certificate Store | No |
| macOS | User login keychain | No |
| Linux | System CA directories | Yes (sudo) |

On Linux, you'll need sudo to modify system-wide certificate stores.

## Commands

**enable** - Enable system CA trust for Python
```bash
cert-trust enable
```

**disable** - Disable system CA trust
```bash
cert-trust disable
```

**status** - Show current status
```bash
cert-trust status          # Pretty output
cert-trust status -v       # Verbose
cert-trust status --json   # JSON output
```

**add** - Add a CA certificate
```bash
cert-trust add corporate-ca.crt
```
Supports .crt, .pem, .cer files

**verify** - Test HTTPS connection
```bash
cert-trust verify https://example.com
```

## Python API

```python
import cert_trust

cert_trust.enable()
cert_trust.status()
cert_trust.add_cert('/path/to/ca.crt')
cert_trust.verify('https://example.com')
cert_trust.disable()
```

## Troubleshooting

**Python still getting SSL errors?** Restart your Python session. The .pth file only loads at startup.

**curl errors on Windows?** Make sure you're using the native curl.exe, not WSL or Git Bash curl.

**Permission errors on Linux?** Use sudo when running `cert-trust add` for curl support.

## Environment Variables

Set `CERT_TRUST_DISABLED=1` to temporarily disable without uninstalling:

```bash
export CERT_TRUST_DISABLED=1
python your_script.py
```

## Development

```bash
git clone https://github.com/chuongmep/cert-trust.git
cd cert-trust
pip install -e ".[all,dev]"
pytest
```

## License

MIT

| Step | Action |
|---|---|
| Python `.pth` | Runs `enable` if not already active |
| Python `certifi` | Appends the cert to the certifi bundle so `requests` picks it up immediately |
| curl — Linux (Debian/Ubuntu) | Copies to `/usr/local/share/ca-certificates/`, runs `update-ca-certificates` |
| curl — Linux (RHEL/Fedora) | Copies to `/etc/pki/ca-trust/source/anchors/`, runs `update-ca-trust extract` |
| curl — macOS | Adds to System Keychain via `security add-trusted-cert` |
| curl — Windows | Runs `certutil -addstore -f ROOT <cert>` → Windows Certificate Store |

### `verify <url>`

Test that a URL is trusted by both `curl` and Python's `ssl` module after adding your cert.

```bash
cert-trust verify https://internal.corp.example.com
cert-trust verify https://internal.corp.example.com --json
```

---

## Temporary disable (without uninstalling)

Set the `CERT_TRUST_DISABLED` environment variable to skip injection for a single process:

```bash
# Linux / macOS
CERT_TRUST_DISABLED=1 python my_script.py

# Windows (PowerShell)
$env:CERT_TRUST_DISABLED=1; python my_script.py

# Windows (Command Prompt)
set CERT_TRUST_DISABLED=1 && python my_script.py
```

---

## How it works

```
cert-trust enable
      │
      ▼
cert_trust_bootstrap.pth   ← written to site-packages
      │
      │  Python reads all .pth files at interpreter startup
      ▼
cert_trust.bootstrap._inject()
      │
      ├─ truststore installed? → truststore.inject_into_ssl()   (cleanest)
      │
      └─ fallback → monkey-patch ssl.create_default_context
                    to call ctx.load_default_certs() on every new context
```

`cert-trust disable` simply deletes the `.pth` file — no other state is modified.

---

## Platform support

| Platform | Python trust | curl trust | Requires elevated privileges? |
|---|---|---|---|
| Linux (Debian/Ubuntu) | `.pth` + certifi | `update-ca-certificates` | sudo for curl |
| Linux (RHEL/Fedora) | `.pth` + certifi | `update-ca-trust extract` | sudo for curl |
| macOS | `.pth` + certifi | System Keychain | sudo for curl |
| Windows 10/11 | `.pth` + certifi | Windows Certificate Store (`certutil`) | Run as administrator for curl |

> **Windows target:** CPython from [python.org](https://www.python.org) + the native `curl.exe` built into Windows 10 1803+. Native curl.exe uses WinSSL/Schannel and reads the Windows Certificate Store automatically after `certutil` imports the cert.

> **macOS note:** curl installed via Homebrew (`brew install curl`) links against Homebrew's OpenSSL and does **not** read the System Keychain. In that case, pass the cert directly: `curl --cacert /path/to/corp-ca.crt ...`

---

## Optional dependencies

| Extra | Package | Effect |
|---|---|---|
| `[rich]` | `rich` | Coloured, formatted terminal output |
| `[truststore]` | `truststore` | Cleaner system cert injection (Python 3.10+) |
| `[all]` | both | Recommended |

```bash
pip install "cert-trust[all]"
```

---

## License

MIT
