Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,13 @@ Getting Started
===============

To get started and play around with Ghostunnel you will need X.509 client
and server certificates. If you don't already maintain a PKI, a good way to get
started is to use a package like [cloudflare/cfssl](https://github.com/cloudflare/cfssl).
and server certificates. If you already maintain a PKI, you can use your
existing certificates. Otherwise, you can use tools like
[mkcert](https://github.com/FiloSottile/mkcert) or
[cloudflare/cfssl](https://github.com/cloudflare/cfssl) to build one.

For testing and development purposes, you can generate test certificates using:
For quick testing and development, you can also generate throwaway test
certificates using the built-in generator:

# Generate test certificates and keys
go tool mage test:keys
Expand Down Expand Up @@ -335,9 +338,11 @@ See [METRICS](docs/METRICS.md) for details.
### HSM/PKCS#11 support

Ghostunnel has support for loading private keys from PKCS#11 modules, which
should work with any hardware security module that exposes a PKCS#11 interface.
should work with any hardware security module that exposes a PKCS#11 interface,
including YubiKeys (via the YKCS11 module).

See [HSM-PKCS11](docs/HSM-PKCS11.md) for details.
See [HSM-PKCS11](docs/HSM-PKCS11.md) for details, including a step-by-step
guide for using Ghostunnel with a YubiKey.

### Windows/macOS Keychain Support

Expand Down
19 changes: 11 additions & 8 deletions certstore/certstore_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,18 @@ func openStore(logger *log.Logger) (*winStore, error) {

// Additional stores to use to look for certificates in Identities().
// The identity we want to load might be in the "current service" or "local
// machine" stores, so we need to open those to check.
extraStores := map[string]C.DWORD{
"CURRENT_SERVICE": C.CERT_SYSTEM_STORE_CURRENT_SERVICE,
"LOCAL_MACHINE": C.CERT_SYSTEM_STORE_LOCAL_MACHINE,
}
for friendlyName, storeIdent := range extraStores {
store := C.CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0, storeIdent, storeName)
// machine" stores, so we need to open those to check. The order here is
// fixed: CURRENT_SERVICE is searched before LOCAL_MACHINE.
for _, extra := range []struct {
name string
ident C.DWORD
}{
{"CURRENT_SERVICE", C.CERT_SYSTEM_STORE_CURRENT_SERVICE},
{"LOCAL_MACHINE", C.CERT_SYSTEM_STORE_LOCAL_MACHINE},
} {
store := C.CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0, extra.ident, storeName)
if store == nil {
logger.Printf("certstore: failed to open key store '%s', skipping", friendlyName)
logger.Printf("certstore: failed to open key store '%s', skipping", extra.name)
continue
}

Expand Down
26 changes: 19 additions & 7 deletions docs/ACCESS-FLAGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,23 @@ but the backend doesn't require mutual authentication.

## Open Policy Agent

Ghostunnel has support for Open Policy Agent (OPA), both in server and client
mode. The policy bundle must be present on disk for Ghostunnel to use it and the
use of OPA is mutually exclusive with any other `allow` (or `verify`) flags.
Policy bundles can be reloaded at runtime much like certificates, with the
`--timed-reload` flag or via `SIGHUP`.
Ghostunnel has support for [Open Policy Agent][opa] (OPA), both in server and
client mode. The policy must be provided as an [OPA bundle][opa-bundles] on
disk and the use of OPA is mutually exclusive with any other `allow` (or
`verify`) flags. Policy bundles can be reloaded at runtime much like
certificates, with the `--timed-reload` flag or via `SIGHUP`.

[opa]: https://www.openpolicyagent.org/
[opa-bundles]: https://www.openpolicyagent.org/docs/latest/management-bundles/

To build a bundle from a `.rego` file, use the `opa build` command:

```bash
opa build policy.rego -o bundle.tar.gz
```

See the [OPA bundle documentation][opa-bundles] for details on bundle
structure and manifest options.

To use it in server mode, specify the `--allow-policy` and `--allow-query` flags.

Expand Down Expand Up @@ -226,8 +238,8 @@ for more about the policy language.
different process.
* Older versions of Ghostunnel allowed specifying a Rego file rather than a
bundle as an argument to the `--allow-policy` and `--verify-policy` flags. This
still works, but the policy will be treated as a V0 policy for compatibility
versions. It's recommended to specify a bundle so you can set the language
still works, but the policy will be treated as a V0 policy for backward
compatibility. It's recommended to specify a bundle so you can set the language
version directly in the bundle manifest.
* By standard OPA convention, we consider a policy to be "allowed" if the query
is exactly one result with exactly one element that has the value `true`.
Expand Down
59 changes: 42 additions & 17 deletions docs/ACME.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ weight: 30
---

In server mode, Ghostunnel can automatically obtain and renew a public TLS
certificate via the ACME protocol. This is powered by
[certmagic](https://github.com/caddyserver/certmagic), which handles
certificate storage, renewal, and OCSP stapling.
certificate via the [ACME][acme-rfc] protocol. This is powered by
[certmagic][certmagic], which handles certificate storage, renewal, and OCSP
stapling.

### Basic usage
## Basic usage

To enable ACME, use the `--auto-acme-cert` flag with the FQDN to obtain a
certificate for. You must also specify an email address with
Expand All @@ -26,33 +26,58 @@ ghostunnel server \
--allow-cn client
```

Ghostunnel defaults to using Let's Encrypt as the ACME CA. You can specify a
different ACME CA URL using `--auto-acme-ca`. To test against a non-production
CA (e.g. Let's Encrypt's staging environment), use `--auto-acme-testca` — when
set, the `--auto-acme-ca` flag is ignored.
Ghostunnel defaults to using [Let's Encrypt][letsencrypt] as the ACME CA. You
can specify a different ACME CA URL using `--auto-acme-ca`. To test against a
non-production CA (e.g. Let's Encrypt's staging environment), use
`--auto-acme-testca`. When set, the `--auto-acme-ca` flag is ignored.

### Requirements
## Requirements

ACME is only supported in server mode. Ghostunnel must either be listening on
a public interface on tcp/443, or have tcp/443 forwarded to it (e.g. via a
systemd socket or iptables). Public DNS records must exist for the FQDN that
resolve to the public listening interface IP.

Ghostunnel uses the TLS-ALPN-01 challenge type (HTTP-01 is disabled), so port
443 must be reachable.
Ghostunnel uses the [TLS-ALPN-01][tls-alpn-01] challenge type (HTTP-01 is
disabled), so port 443 must be reachable.

### Certificate storage and renewal
## Certificate storage and renewal

Certificates are stored locally by certmagic in its default storage directory
(typically `~/.local/share/certmagic` or the equivalent on your OS). Certmagic
automatically renews certificates before they expire — no manual intervention
or `--timed-reload` is needed for ACME certificates.
Certmagic stores certificates and account keys on disk. The default location
depends on your OS:

| OS | Default path |
|----|-------------|
| Linux / macOS | `~/.local/share/certmagic` (or `$XDG_DATA_HOME/certmagic`) |
| Windows | `%USERPROFILE%\.local\share\certmagic` |

Certmagic automatically renews certificates before they expire, so no manual
intervention or `--timed-reload` is needed for ACME certificates.

If a valid certificate already exists locally, Ghostunnel loads it from cache
on startup without contacting the CA.

### Startup retry behavior
## Revoking or force-renewing

Certmagic handles renewal automatically, but if you need to force a renewal
(e.g. after a key compromise), delete the certificate and key files from the
certmagic storage directory and restart Ghostunnel. It will obtain a fresh
certificate on startup.

To revoke a certificate with Let's Encrypt directly, use the
[certbot revoke][certbot-revoke] command or the ACME revocation endpoint
described in [RFC 8555 Section 7.6][acme-revoke].

[certbot-revoke]: https://eff-certbot.readthedocs.io/en/latest/using.html#revoking-certificates
[acme-revoke]: https://datatracker.ietf.org/doc/html/rfc8555#section-7.6

## Startup retry behavior

On startup, Ghostunnel attempts to obtain the initial certificate up to 5
times with exponential backoff (starting at 5 seconds, capped at 2 minutes).
If all attempts fail, Ghostunnel exits with an error.

[acme-rfc]: https://datatracker.ietf.org/doc/html/rfc8555
[letsencrypt]: https://letsencrypt.org/
[tls-alpn-01]: https://datatracker.ietf.org/doc/html/rfc8737
[certmagic]: https://pkg.go.dev/github.com/caddyserver/certmagic
175 changes: 175 additions & 0 deletions docs/CERTIFICATES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
---
title: Certificate Formats
description: Supported certificate and key formats, how to prepare them, and how Ghostunnel selects the right loader.
weight: 12
---

Ghostunnel supports several certificate and private key formats. The format
is auto-detected from the file extension or by inspecting the first few
bytes, so you don't need to specify it explicitly.

## Formats at a glance

| Format | Extensions | Flag | Notes |
|--------|-----------|------|-------|
| PEM (separate files) | `.pem`, `.crt` + `.pem` | `--cert` + `--key` | Most common; leaf cert must be first in chain |
| PEM (combined) | `.pem` | `--keystore` | Single file with cert chain and private key |
| PKCS#12 | `.p12`, `.pfx` | `--keystore` | Binary bundle; optional `--storepass` for password |
| JCEKS | `.jceks`, `.jks` | `--keystore` | Java keystore; requires `--storepass` |
| DER | `.der` | `--keystore` | Raw X.509 or PKCS#7; less common |

These options are mutually exclusive with each other and with `--use-workload-api`,
`--keychain-identity`, and PKCS#11 flags.

## PEM files (separate cert and key)

Pass the certificate chain and private key as two separate PEM files:

```bash
ghostunnel server \
--cert server-chain.pem \
--key server-key.pem \
--listen localhost:8443 \
--target localhost:8080 \
--cacert cacert.pem \
--allow-cn client
```

The certificate file must contain the **leaf certificate first**, followed by
any intermediate CA certificates:

```
-----BEGIN CERTIFICATE-----
(leaf / end-entity certificate)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(intermediate CA certificate)
-----END CERTIFICATE-----
```

The key file must contain a single PEM-encoded private key (RSA, ECDSA,
or Ed25519).

## PEM keystore (combined file)

A single PEM file containing both the certificate chain and private key can
be passed with `--keystore`. The private key can appear anywhere in the file,
but the leaf certificate must still come before any intermediates:

```bash
ghostunnel server \
--keystore server-combined.pem \
--listen localhost:8443 \
--target localhost:8080 \
--cacert cacert.pem \
--allow-cn client
```

To create a combined PEM file:

```bash
cat server-cert.pem intermediate.pem server-key.pem > server-combined.pem
```

## PKCS#12

PKCS#12 (`.p12` / `.pfx`) bundles the certificate chain and private key into a
single password-protected binary file. This is also the format used when
importing into the macOS Keychain or Windows Certificate Store (see
[Keychain Support]({{< ref "KEYCHAIN.md" >}})).

```bash
ghostunnel server \
--keystore server.p12 \
--storepass <password> \
--listen localhost:8443 \
--target localhost:8080 \
--cacert cacert.pem \
--allow-cn client
```

To create a PKCS#12 file from PEM files:

```bash
openssl pkcs12 -export \
-in server-cert.pem \
-inkey server-key.pem \
-certfile intermediate.pem \
-out server.p12 \
-passout pass:<password>
```

See the [openssl-pkcs12][openssl-pkcs12] man page for all options.

[openssl-pkcs12]: https://docs.openssl.org/master/man1/openssl-pkcs12/

## JCEKS

Ghostunnel can read Java keystores in JCEKS or JKS format. This is mainly
useful when migrating from a Java-based TLS terminator:

```bash
ghostunnel server \
--keystore server.jceks \
--storepass <password> \
--listen localhost:8443 \
--target localhost:8080 \
--cacert cacert.pem \
--allow-cn client
```

## CA bundle

The `--cacert` flag accepts a PEM file containing one or more trusted CA
certificates. If omitted, Ghostunnel uses the system trust store.

To build a CA bundle from individual certificates:

```bash
cat root-ca.pem intermediate-ca.pem > cacert.pem
```

## Format auto-detection

Ghostunnel detects the format in this order:

1. **File extension**: `.pem`/`.crt` → PEM, `.p12`/`.pfx` → PKCS#12,
`.jceks`/`.jks` → JCEKS, `.der` → DER.
2. **Magic bytes**: if the extension is ambiguous, the first bytes of the file
are inspected (e.g. `-----BEGIN` → PEM, ASN.1 sequence → PKCS#12 or DER).

In practice, just use the right file extension and Ghostunnel will do the
right thing.

## Common operations

### Inspect a PEM certificate

```bash
openssl x509 -in server-cert.pem -noout -text
```

### Inspect a PKCS#12 file

```bash
openssl pkcs12 -in server.p12 -info -nokeys
```

### Convert PKCS#12 to PEM

```bash
# Extract the leaf certificate
openssl pkcs12 -in server.p12 -clcerts -nokeys -out server-cert.pem

# Extract CA/intermediate certificates
openssl pkcs12 -in server.p12 -cacerts -nokeys -out ca-chain.pem

# Extract private key
openssl pkcs12 -in server.p12 -nocerts -nodes -out server-key.pem
```

### Verify a certificate chain

```bash
openssl verify -CAfile cacert.pem server-cert.pem
```
Loading
Loading