smtp-fp
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.
Installation
To install this software, you will need a recent version of Go (>=1.2)
properly set up, with GOPATH 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:
#!/bin/sh 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
config:
- valid_after: 2013-08-04 12:00
domains:
- 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
domains:
- 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:
poivron.org:
urls:
- https://git.poivron.org/repository.git
- http://git.poivron.org/repository.git
fingerprints:
- 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:
smtp: spec: postfix:/etc/postfix/tls_policy xmpp: 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.