Installation
Add Helm repository
helm repo add ssl-pinning https://ssl-pinning.github.io/charts
helm repo update
Generate RSA signing keys
The server signs the pin registry with a 4096-bit RSA private key. The public key is embedded in the Android APK for signature verification.
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out prv.pem
openssl rsa -pubout -in prv.pem -out pub.pem
Create values.yaml
apps-secrets:
ssl-pinning-tls:
data:
prv.pem: <base64 -i prv.pem | tr -d '\n'>
apps-configmaps:
ssl-pinning-config:
data:
config.yaml: |
keys:
- fqdn: api.example.com
domainName: "api.example.com"
server:
listen: 0.0.0.0:7500
apps-stateless:
ssl-pinning:
replicas: 1
containers:
app:
image:
name: ghcr.io/ssl-pinning/ssl-pinning
staticTag: v1.1.0
ports: |
- name: http
containerPort: 7500
env:
SSL_PINNING_SERVER_LISTEN: 0.0.0.0:7500
SSL_PINNING_STORAGE_TYPE: redis
SSL_PINNING_STORAGE_DSN: redis://valkey:6379/0
SSL_PINNING_TLS_DIR: /opt/ssl-pinning/tls
SSL_PINNING_TLS_DUMP_INTERVAL: 1s
volumeMounts: |
- name: tls
mountPath: /opt/ssl-pinning/tls
readOnly: true
- name: config
mountPath: /etc/ssl-pinning
readOnly: true
volumes: |
- name: tls
secret:
secretName: ssl-pinning-tls
- name: config
configMap:
name: ssl-pinning-config
service:
enabled: true
apps-ingresses:
ssl-pinning:
class: nginx
rules: |
- host: ssl-pinning.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ssl-pinning
port:
number: 7500
Install
helm install ssl-pinning ssl-pinning/ssl-pinning \
--namespace ssl-pinning \
--create-namespace \
-f values.yaml
Get public key for the Android / iOS SDK
Pass the base64-encoded public key as signingKeyBase64 in SslPinningConfig.
base64 -i pub.pem | tr -d '\n'
Keys configuration
Each entry under keys defines a domain to track. Multiple entries can share the same file to group them into one API response.
| Field | Default | Description |
|---|---|---|
fqdn | — | Required. Domain to dial on port 443 for certificate tracking. |
domainName | *.{fqdn} | Hostname pattern for OkHttp CertificatePinner on the client side. |
file | {fqdn}.json | Groups this entry into a named response file. Multiple FQDNs can share the same file. |
keys:
- fqdn: google.com
domainName: "www.google.com"
- fqdn: mail.google.com
file: google.com.json # grouped into the same endpoint
domainName: "mail.google.com"
Both entries are served at GET /api/v1/google.com.json
Storage backends
| Type | DSN format | Notes |
|---|---|---|
memory | — | Ephemeral. Lost on restart. Development only. |
fs | — | Writes signed JSON to dump_dir. Serves files directly. |
redis | redis://:pass@host:6379/0 | Shared state across multiple instances. |
postgres | postgres://user:pass@host/db | Persistent. Migrations run automatically on startup. |
Configuration reference
All settings can be set via config file, environment variable, or CLI flag. Priority: flags > env vars > config file > defaults. Env var format: SSL_PINNING_ prefix + upper snake case (e.g. log.level → SSL_PINNING_LOG_LEVEL).
Server
| Env var / Flag | Default | Description |
|---|---|---|
SSL_PINNING_SERVER_LISTEN | 127.0.0.1:7500 | HTTP listen address and port |
SSL_PINNING_SERVER_READ_TIMEOUT | 5s | Request read timeout |
SSL_PINNING_SERVER_WRITE_TIMEOUT | 5s | Response write timeout |
Storage
| Env var / Flag | Default | Description |
|---|---|---|
SSL_PINNING_STORAGE_TYPE--storage-type | memory | memory · fs · redis · postgres |
SSL_PINNING_STORAGE_DSN--storage-dsn | — | Connection string for redis or postgres |
SSL_PINNING_STORAGE_DUMP_DIR--storage-dump-dir | /tmp | Directory for fs/memory persistence dumps |
SSL_PINNING_STORAGE_MAX_IDLE_CONNS--storage-max-idle-conns | 5 | Max idle connections in pool |
SSL_PINNING_STORAGE_MAX_OPEN_CONNS--storage-max-open-conns | 5 | Max open connections in pool |
SSL_PINNING_STORAGE_CONN_MAX_IDLE_TIME--storage-conn-max-idle-time | 5m | Max idle time for a connection |
SSL_PINNING_STORAGE_CONN_MAX_LIFETIME--storage-conn-max-lifetime | 30m | Max lifetime of a connection |
TLS
| Env var | Default | Description |
|---|---|---|
SSL_PINNING_TLS_DIR | {config-path}/tls | Directory containing prv.pem and pub.pem |
SSL_PINNING_TLS_DUMP_INTERVAL | 5s | How often keys are flushed to storage |
SSL_PINNING_TLS_TIMEOUT | 5s | TLS dial timeout per domain |
Logging
| Env var / Flag | Default | Description |
|---|---|---|
SSL_PINNING_LOG_FORMAT--log-format | json | json · text |
SSL_PINNING_LOG_LEVEL--log-level | info | debug · info · warn · error |
SSL_PINNING_LOG_PRETTY--log-pretty | false | Pretty-print log output |
Global CLI flags
| Flag | Default | Description |
|---|---|---|
--config-file | config.yaml | Configuration file name |
--config-path | /etc/ssl-pinning | Configuration file directory |
API
| Endpoint | Description |
|---|---|
GET /api/v1/{file} | Signed JSON pin registry (e.g. /api/v1/api.example.com.json) |
GET /health/liveness | Liveness probe |
GET /health/readiness | Readiness probe |
GET /health/startup | Startup probe |
GET /metrics | Prometheus metrics (port 9090) |