Share TLS information among peers.

Name Last Update
cmd/smtpfp Loading commit data...
commands Loading commit data...
output Loading commit data...
testdata Loading commit data...
third_party/go-commander Loading commit data...
vendor Loading commit data...
README.rst Loading commit data...
errors.go Loading commit data...
fingerprint.go Loading commit data...
fingerprint_test.go Loading commit data...
keyring.go Loading commit data...
keyring_test.go Loading commit data...
logger.go Loading commit data...
metaconfig.go Loading commit data...
metaconfig_test.go Loading commit data...
source.go Loading commit data...
source_test.go Loading commit data...
tls.go Loading commit data...
tls_test.go Loading commit data...
yaml_time.go Loading commit data...


This software is a tool for decentralized authenticated data exchange between a group of peers, for the purpose of automatically distributing service configuration on top of an existing network of trust.

The service configuration consists of X509 certificate information necessary to safely verify TLS connections. See the Service configuration section for further details.


To install this software, you will need a recent version of Go (>=1.2) properly set up, with GOPATH properly defined and the go binary in your PATH. Then run the following:

$ go get git.autistici.org/ale/smtp-fp
$ go install git.autistici.org/ale/smtp-fp/...
$ sudo cp $GOPATH/bin/smtpfp /usr/local/bin/smtpfp

(note: you will need to install the Autistici/Inventati SSL CA system-wide for the go get command to succeed).

The result is a pseudo-static standalone binary that can be easily copied to other machines if necessary.

This document contains a description of the configuration format and some other practical considerations.

Setting up your repository

To participate in the network as a provider you'll need to create a repository describing your service configuration. So, first you will need to create a new repository:

$ mkdir myservice && cd myservice
$ git init .

Then, choose which PGP key you will use to sign commits to this repository. This key will then be shared with the other peers to authenticate your configuration: unless the commits are signed, the configuration will not be considered valid, so it is extremely important not to forget this step!

Find the key ID and run:

$ git config user.signingkey $KEYID

It is also very useful to install a pre-commit hook, that will check your configuration for errors before every commit. Create a file named pre-commit in the .git/hooks directory of your repository, with the following contents:

exec /usr/local/bin/smtpfp pre-commit

and make sure it is executable:

$ chmod +x .git/hooks/pre-commit

The repository is now ready for commits. Set a remote URL for sharing, and create YAML descriptions of your services.

Service configuration

Configuration data for each service is described in a YAML file. Dates are in the UTC timezone.

Version 1

The following is an example smtp.yml file for the SMTP service:

version: 1
- valid_after: 2013-08-04 12:00
   - domain: potager.org
     cert: certs/potager.org-2013.pem
   - domain: anargeek.net
     cert: certs/poivron.org-2013.pem
   - domain: example.org
     hidden_service: aaaaaaaaaaa.onion
- valid_after: 2014-07-01 12:00
   - domain: potager.org
     cert: certs/potager.org-2014.pem
   - domain: anargeek.net
     cert: certs/poivron.org-2014.pem
   - domain: example.org
     hidden_service: aaaaaaaaaaa.onion

version is the data format version to allow future revision. This document describes version 1.

config contains a list of configurations.

Each configuration must have a valid_after field. Only one configuration will be considered: the one which has the most recent validity which is not in the future. Rationale: this allow timed key rollover.

Each configuration must contain a domains field as a list. Each entry must contain a domain field.

For STARTTLS authentication, a certificate must be provided as a file in the Git repository and the cert field must contain the filename relative to the root of the Git repository.

For domains that are reachable behind a Tor hidden service, the hidden_service provides a Tor hidden service address.

Meta configuration

The global configuration consists, for each peer, of:

  • one or more Git repository URL,
  • one or more OpenPGP fingerprints that can validate the config.

Git repositories are meant as fallbacks and will be tried one after the other. The first pull that works is good.

Changes to the location of the Git repository or to the validating keys must be announced externally somehow.

The list of all known groups and their repositories is maintained in a YAML file, where every group (identified by an arbitrary name) has urls and fingerprints. For example:

    - https://git.poivron.org/repository.git
    - http://git.poivron.org/repository.git
    - 1234567890123456789012345678901234567890

Commits to the git repository must be signed by one of the specified fingerprints.

GPG fingerprints are the hashes of the public key, obtainable from your key ID with the following command:

$ gpg --with-colons --with-fingerprint --list-keys $KEYID \
     | awk -F: '/^fpr:/ {print $10}'

Integrating with local services

The smtpfp tool is meant to automatically update the configuration of your services according to what the remote repositories specify.

Local storage

In order to be able to regenerate the TLS maps when a source is temporarily unreachable, smtpfp needs a directory on the local filesystem to store the checked-out repositories and other information. It's also a good idea to avoid having to fully download each remote repository on every invocation.

Use the --state-dir command-line option to tell smtpfp where it should store its local state. If the option is not specified, like in the examples below in this document, a temporary directory will be used which will be removed when the program exits.

GPG Keyring

In order to validate the remote configurations, you will need all the necessary PGP public keys. This software does not provide a method to obtain them, so you'll have to deal with that independently.

The smtpfp tool will look for PGP keys in your personal keyring in ~/.gnupg, but before that it will search for a file named keyring in the same directory as the meta-configuration file (this can be changed with the --keyring option). This can be useful in combination with configuration management systems, or if your personal keyring is very large: since it has to be loaded in memory on every run, keyring size has an impact on performance. Use the update-keyring command to keep this standalone keyring file up-to-date, by copying the required keys from your personal keyring:

$ smtpfp --config=config.yml --keyring=keyring update-keyring

This will create a file named keyring. If you run the command without the --keyring option, it will report the fingerprints of the keys that are missing in your personal keyring.

Updating a single service

With the above configuration file, one can update the configuration for a service with the update-service command of smtpfp. You need to point it at the global configuration file using the --config option (which should be specified before the update-service command).

Currently, the tool can only generate Postfix TLS maps, and it will not automatically reload the Postfix service - you will need to write that automation yourself.

An example:

$ smtpfp --config=config.yml update-service --service=smtp

this will generate a file named tls_policy in the current directory. Use the --output option to control the destination.

Updating many services

Assuming you need to update multiple services at once (for example, smtp and xmpp), it makes sense to save the redundant remote accesses of multiple update-service invocations. It is then possible to describe the service layout of the local system in a YAML file, and use the update command of smtpfp to run all the updates at once.

The file format is quite simple, consisting of a dictionary of service names, each containing a spec element that is parsed identically to the --output option of update-service. For example:

  spec: postfix:/etc/postfix/tls_policy
  spec: prosody:/etc/prosody/tls_policy

Assuming this file is called system.yml, the following command will efficiently update all the configurations at once:

$ smtpfp --config=config.yml  update --layout=system.yml

Both the update and update-service command support the --diff option if you only want to look at at the updates instead of applying them (useful for those cases where you want to apply changes with manual supervision).

Update errors

A failure in updating a source should not prevent updates from other sources to be reflected in the service configuration. The tool is designed to report individual failures, but it won't exit with a non-zero status unless all the sources failed to update successfully.

Sources have a TTL to allow for temporary problems, a configuration will be ignored as stale only if a certain amount of time passes without a successful update. This value can be controlled by the command-line flag --source-ttl.

Known Issues

  • The git pre-commit hook can't verify that the commit is signed, which is the easiest mistake to make.