Hockeypuck OpenPGP Public Keyserver

1. Configuration

Hockeypuck reads configuration from a TOML-format configuration file, which should be supplied on the command line using the `-c` flag. This file is preprocessed as a Go template (See Templating below).

1.1. Hosting

[hockeypuck]
contact="DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
hostname="keys.example.com"
nodename="node1.keys.example.com"
enableVhosts=true

These settings are displayed on the stats page (/pks/lookup?op=stats).

1.1.1. Logging

[hockeypuck]
logfile="/path/to/logfile"
loglevel=<one of: DEBUG,INFO,WARNING,ERROR,FATAL,PANIC>

If not configured, hockeypuck will log INFO level messages and higher severity to standard error.

1.1.2. Static HTML files

Hockeypuck will serve static files from / out of the webroot path, so long as the path names do not conflict with HKP routed requests (like /pks/lookup).

[hockeypuck]
webroot="/path/to/www/files"

index.html will be served by default if the path resolves to a directory and the file exists.

1.1.3. Custom HTML templates

By default, Hockeypuck will respond to HKP operations op=index, op=vindex and op=stats with an application/json response. The underlying structs for these responses can be used in HTML templates of your own design to customize the output.

Specify these templates with:

[hockeypuck]
indexTemplate="/path/to/template"
vindexTemplate="/path/to/template"
statsTemplate="/path/to/template"

The path must be to a file containing a valid Go html/template.

indexTemplate and vindexTemplate operate on a struct containing two top-level fields,

statsTemplate operates on an instance of sks.Stats.

See the packaged templates for an example.

1.1.4. Remote administration

[hockeypuck]
adminKeys=[
    "DECAFBADDECAFBADDECAFBADDECAFBADDECAFBAD",
]

The listed fingerprints identify keys that may sign administration requests for this server. It is strongly recommended that the server contact advertised on the stats page (see above) is not one of the admin keys.

Currently, administration requests are restricted to key deletion and key replacement. See Remote Administration for details.

1.2. HKP

[hockeypuck.hkp]
bind=":11371"
advertisedBind="keys.example.com:80"
logRequestDetails=false

1.2.1. Queries

[hockeypuck.hkp.queries]
selfSignedOnly=false
keywordSearchDisabled=false

1.2.2. HKPS

If this section is populated, Hockeypuck will listen on HKPS.

[hockeypuck.hkps]
bind=":443"
logRequestDetails=false
cert="/path/to/cert"
key="/path/to/key"

1.3. OpenPGP

[hockeypuck.openpgp]
maxPacketLength=8192
maxKeyLength=1048576
blacklist=[
    # "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
]
nworkers=8

1.3.1. Storage

Hockeypuck is supported on any version of PostgreSQL ≥ 13. Older versions of PostgreSQL (≥ 9.6) may work, however bugs that only affect older PostgreSQL versions will not be fixed. Some fields are broken out into separate columns for indexing. For details, refer to the PostgreSQL storage backend, pghkp/storage.go. Hockeypuck has been tested with versions of PostgreSQL up to and including 17.

To use PostgreSQL:

[hockeypuck.openpgp.db]
driver="postgres-jsonb"
dsn="database=hkp host=/var/run/postgresql port=5432 sslmode=disable"

See the pq driver package documentation for details on how to construct the connection string.

When upgrading between minor versions, the database schema may change, and reindexing may be required. By default (since version 2.3) hockeypuck will perform reindexing automatically on startup unless configured otherwise. While hockeyuck will continue to function if reindexing is disabled, search performance may be degraded. In addition, upgrades to newer minor versions will not work correctly unless the current minor version has reindexed the database at least once.

To upgrade across major versions, or more than one minor version, hockeypuck-reload should be invoked after upgrading and before starting up.

1.3.2. Headers

The headers added to the ASCII-armored output can be optionally customised.

[hockeypuck.openpgp.headers]
comment="Hockeypuck"
version="2.3"

1.3.3. PKS

If this section is populated, Hockeypuck will send PKS updates to other PKS-compatible servers.

[hockeypuck.openpgp.pks]
from="pks-updates@keys.example.com"
to=[
  "mailto:pks-updates@keys.contoso.com",
  "hkp://keyserver.example"
]
[hockeypuck.openpgp.pks.smtp]
host="localhost:25"
id=""
user="alice"
pass="secret"

1.4. Recon

Hockeypuck supports the SKS synchronisation protocol. SKS consists of two interlinked protocols, "recon" and "recovery". Recon is initiated unidirectionally, and identifies differences between the datasets of the two servers based on hash digests of the entries. Recovery takes place bidirectionally after recon is complete, and transfers the actual differences in the datasets.

Recon is handled by the conflux package, which then initiates recovery over HKP.

[hockeypuck.conflux.recon]
httpAddr=":11371"
reconAddr=":11370"
allowCIDRs=["127.0.0.1/8"]
filters=["preProduction"]
seenCacheSize=256
gossipIntervalSecs=60
maxOutstandingReconRequests=100
logname="conflux.recon"

1.4.1. Partners

[hockeypuck.conflux.recon.partner.peer1]
httpAddr="keys.example.com:11371"
reconAddr="keys.example.com:11370"
pksFailover=false
[hockeypuck.conflux.recon.partner.peer2]
httpAddr="juju-azure-dev-y9157oo521.cloudapp.net:11371"
reconAddr="juju-azure-dev-y9157oo521.cloudapp.net:11370"
pksFailover=true
...

Create a section for each partner [hockeypuck.conflux.recon.partner.NAME], where NAME contains only characters from [A-Za-z0-9_].

Each partner section must include a httpAddr and a reconAddr entry, in host:port format. These are usually the same host, but they might differ, especially if the HKP service is reverse-proxied.

If pksFailover is set to true, and the partner in question fails to recon, it will be temporarily added to the list of PKS servers. This can be used to ensure continuity of updates, particularly during upgrades. Note that since PKS is a one-way sync protocol, for full sync pksFailover will need to be configured on both partners.

1.4.2. Prefix tree

[hockeypuck.conflux.recon.leveldb]
path="/path/to/prefix/tree"

The prefix tree is used to keep track of which keys the peer has, for synchronization purposes.

1.5. Metrics

Hockeypuck exposes a metrics endpoint for aggregation by Prometheus.

[hockeypuck.metrics]
metricsAddr=":9626"
metricsPath="/metrics"

2. Templating

The configuration file supports templating using the Go text/template package with Sprig extensions. The template is rendered before the configuration file is parsed, so the configuration file can contain environment variables.

For example, setting the database configuration to use environment variables:

[hockeypuck.openpgp.db]
driver="postgres-jsonb"
dsn="database=hkp host=postgres user={{ .POSTGRES_USER }} password={{ .POSTGRES_PASSWORD }} port=5432 sslmode=disable"

You can also use the osenv custom function to read environment variables by prefix:

{{ range $key, $value := osenv "HKP_" }}
{{ $key }}={{ $value }}
{{ end }}

Sprig extensions are powerful, and can be used to construct quite complex configuration files. For example, given an environment variable:

GOSSIP_PEERS='{"ks1_example_com": {"httpAddr": "ks1.example.com:11371", "reconAddr": "ks1.example.com:11370"},'\
'"ks2_example_com": {"httpAddr": "ks2.example.com:11371", "reconAddr": "ks2.example.com:11370"}}'

We can populate the list of recon partners by parsing it as JSON and iterating over the resulting structure:

{{- if .GOSSIP_PEERS }}
{{- range $name, $peer := fromJson .GOSSIP_PEERS }}
[hockeypuck.conflux.recon.partner.{{ $name }}]
httpAddr="{{ $peer.httpAddr }}"
reconAddr="{{ $peer.reconAddr }}"
{{- end }}
{{- end }}

Authors

Casey Marshall, Andrew Gallagher