Husk is written in Rust, so you need a recent rust toolchain to compile the source. If your distribution doesn't provide such a toolchain (or it is to old), you can get an up-to-date version from here.
To compile Husk from source, clone its repository and compile:
$ git clone https://gitlab.com/husk-project/husk-milter
$ cd husk-milter
$ cargo install --locked --path .
The configuration file of Husk uses the toml format.
Husk is a milter (mail filter), which means it's a background process (daemon) which
listens on a TCP port for incoming requests. This port is configured
under the name connection.
connection = "localhost:3000"
This example tells Husk to listen on 127.0.0.1 (localhost), port 3000.
There is a corresponding configuration for the MTA, so that the MTA
knows where to find Husk (see below).
Husk uses OpenPGP certificates for encryption. It needs a storage location
for these certificates. As Husk uses Sequoia PGP,
the name for this parameter is sequoia_home.
sequoia_home = "/var/lib/husk/"
Husk queries online sources for missing certificates. For WKD and DANE
the URL of the online source is derived from the email. Keyservers
have to be configured separately.
keyservers = [ "hkps://keys.openpgp.org/", "hkps://mail-api.proton.me" ]
Husk uses log4rs for logging. The configuration of log4rs is merged
into the main configuration file under the node named logging. That
means that you take a log4rs configuration and prepend logging to
each section:
[logging.appenders.console]
kind = "console"
[logging.appenders.console.encoder]
pattern = "{d(%Y-%m-%d %H:%M:%S)(local)} {h({l})} [{t}] {m}{n}"
[logging.appenders.file]
kind = "file"
path = "/tmp/husk.log"
[logging.appenders.file.encoder]
pattern = "{d(%Y-%m-%d %H:%M:%S)(local)} {h({l})} [{t}] {m}{n}"
[logging.root]
level = "debug"
appenders = ["file"]
[logging.loggers."husk"]
level = "debug"
so that [appenders.console] becomes [logging.appenders.console]
and so on.
If you do not specify any logging, Husk will default to log to the console.
You can put the configuration file at any place and start Husk with
$ husk ... --config <LOCATION> ...
or use the default location /etc/husk/config.toml. In this case you
can skip --config.
This is a description on how to add Husk to the postfix MTA. If you want to
use Husk with a different MTA, please refer to its documentation on how to add
a milter.
For postfix you have to tell the MTA where it can find the milter port.
The configuration entries are smtpd_milters and non_smtpd_milters.
smtpd_milters = inet:localhost:3000
non_smtpd_milters = $smtpd_milters
inet:localhost:3000 corresponds to the connection configuration parameter
in the Husk configuration.
The example above configures non_smtpd_milters by using the setting of
smtpd_milters (via the $ operator).
Please keep in mind that in case you configured multiple milters, the sequence is important, as Husk will change the content of the mail.
Husk has two aspects:
The CLI syntax follows the scheme NOUN (more NOUNs), VERB and then
parameters (if sensible). Like
$ husk introducer add --cert-file FILE --domains DOMAIN
or
$ husk daemon start --config config.toml
Husk includes a help system, which can be used by passing --help.
$ husk --help
or
$ husk introducer add --help
and so on. It shows you the available subcommands and parameters.
The daemon is the background process which processes mails and encrypts them if possible. It has to be started separately, the MTA expects it to be reachable and doesn't start it on it's own.
$ husk daemon start
or use
$ husk daemon start --config /path/to/the/configuration/file.toml
If you want to start the daemon as a background process add --background
to the command:
$ husk daemon start --background
Husk can (and should) run as an unprivileged process (keep in mind to use a port number above 1024 as ports below require a privileged process).
Stopping means terminating the process. Please note that your MTA will start producing error messages if a registered milter disappears.
$ husk daemon stop
$ husk daemon status
If the daemon is running, daemon status will also report the pid of the process.
TBD
For Husk to use a certificate for encryption, it has to be authenticated - meaning the user id of the certificate (usually an email address) is indeed the user id of the key holder of said certificate.
This authentication can only be done by humans, but once it's done, its result can be shared.
This is what an introducer does - it authenticates certificates and publishes the result in form of a certification which becomes part of the certificate.
Husk allows to use introducers to benefit from their work. At the same time, introducers are limited to a list of domains for which certifications are accepted. This is a restriction Husk imposes. The list of domains is specified by the administrator, not the introducer.
To add an introducer you need its certificate and specify which domains certifications done with this certificate should be accepted.
$ husk introducer add --cert-file FILE --domains example.com
You can specify a list of domains by
... ---domains domain1 domains2 domain3
FILE must contain a valid certificate, not expired and not revoked.
In case you already added the certificate to Husk, you can promote it by using its fingerprint.
$ husk introducer add --cert FINGERPRINT --domains example.com
$ husk introducer list
$ husk introducer remove --cert FINGERPRINT
If you remove an introducer, the certificate will stay in the certificate
store - only it's special powers will be removed. You can reactivate an
introducer by using husk introducer add --cert FINGERPRINT ..., if you
accidentally removed one.
Introducers are maintained separately from Husk. The following is an example
on how to create and use one using the command line tool sq from Sequoia PGP.
For the sake of this example we have two sides: Husk as consumer and CA
as a producer. CA stands for certificate authority. CA is an ordinary
OpenPGP key, what makes it a certificate authority is its usage.
In most cases, CA is not you, but the one who's work you want to use - see above.
Create a key:
$ sq key generate --own-key --userid 'My CA'
You can create a more complex key with more user ids and capabilities,
but for a CA this is enough.
This CA can now be used to certify other keys (or more precise: certificates).
Let B0F698D7419862EAC6F435B0D5FC683922578868 be the fingerprint of a
certificate.
$ sq pki vouch add --certifier-userid 'My CA' \
--cert B0F698D7419862EAC6F435B0D5FC683922578868 \
--all
This example certifies all user ids of B0F698D7419862EAC6F435B0D5FC683922578868.
You can be more selective by using --userid or --email instead of --all.
Now publish the certified key:
$ sq network keyserver publish --cert B0F698D7419862EAC6F435B0D5FC683922578868
Repeat the last two steps for all certificates you want to certify.
Now let Husk know and use your CA. First copy the certificate of CA
to where Husk is running.
$ sq cert export --cert-userid 'My CA' > ca.pgp
$ scp ca.pgp $HUSK_SERVER
On the $HUSK_SERVER:
$ husk introducer add --cert-file ca.pgp --domains ...
Choose the list of domains you want to trust CA to introduce carefully.
Lets assume the B0F698D7419862EAC6F435B0D5FC683922578868 is the certificate
of alice@example.com and you thrust CA for that domain.
$ husk introducer add --cert-file ca.pgp --domains example.com
If now Husk encounters a mail addressed to alice@example.com (and her
certificate isn't yet locally available), Husk will query online sources
and check the results for a certification of CA (as CA is registered
as an introducer for *@example.com).
If such a certification can be found, the right certificate is identified and Husk will use it for encryption (and stores it locally).
Locals are certificates which are added to Husk by the administrator
and do not require to be introduced.
If you want to test Husk or want to use it only with a few dedicated
certificates, you can skip creating an introducer and use locals.
$ husk locals add --cert-file FILE
FILE must contain a valid certificate, not expired and not revoked.
Locals can be promoted to introducers by using
husk introducer add --cert FINGERPRINT ....
$ husk locals list
$ husk locals remove --cert FINGERPRINT
If you remove a local, its certificate will stay in the certificate store, but will be dormant.
Husk can sign/encrypt mails instead of only encrypting them. For that, Husk needs the appropriate key.
Signing is done when a signing key is configured, without that key Husk will only encrypt a message.
If you already have a key, you can import it into the keystore of Husk by:
$ husk signkey set --cert-file FILE
FILE must contain an OpenPGP key (public and private key material) which
is not expired or revoked and must be able to sign messages.
If there is already a key in the keystore which should be used as signing key:
$ husk signkey set --cert FINGERPRINT
If you are using a FINGERPRINT which has no corresponding key in the keystore,
Husk will return an error and leaves the current key in place.
If there is already a signing key in place, signkey set will replace it.
The new key will be used instantly, a restart of the daemon is not necessary.
You can tell Husk to create a signing key by:
$ hust signkey create
This will create a key which can only sign messages. With --userid you can
specify a custom user id:
$ hust signkey create --userid "My special signing key"
Without --userid Husk will use Husk signing key as user id.
$ husk signkey show
You might want to make the signing key public so that users receiving a signed/encrypted message from Husk can identify the signer.
$ husk signkey dump
will print the certificate (public key) to the console so that you can share it with the world.