GSoC 2017 - PGPMailman plugin


  • pgpmailman - A Core plugin.

    • styles - Both styles generate a list keypair based on plugin settings on list creation as well as set other attributes for an encrypted mailing list. Such as the custom encrypted chain.

      • EncryptedDefaultStyle
      • EncryptedAnnounceStyle
    • pgp

    • rules

      • EncryptionRule - Decrypts message and enforces per-list encryption requirements.
      • SignatureRule - Checks message signature and enforces per-list signature requirements. Strips signature to msgdata.
    • chains

      • EncryptedOwnerChain - Passes message through EncryptionRule before letting it continue default-owner-chain.
      • EncryptedPostingChain - Passes message through EncryptionRule and SignatureRulebefore letting it continue default-posting-chain.
    • commands

      • KeyEmailCommand - Handles user key management through the key command.
      • KeyCLICommand
    • runners

      • EncryptedOutgoingRunner - Encrypts and optionally signs for configured lists. This is a runner and not a Pipeline since we need to encrypt all outgoing messages, so digests, virgin messages, posts…
    • archivers

      • EncryptedHyperKittyArchiver - Fetches list archive public keys from pgphyperkitty, uses them to send messages to archive encrypted, for encrypted lists.
  • pgphyperkitty- Django app, receives messages encrypted with list archive keys, decrypts them and passes them to HyperKitty. Also generates, holds and provides(public part) list archive keypairs.


Rules shouldn’t change the message as they process it, since, well they are “rules” not “handlers”. However other Mailman’s rules need the message plaintext after decrypting for processing, so they cannot receive the message as it arrived encrypted. One solution would be to use a custom Incoming runner to sort-of unwrap the PGP/MIME message from encryption and signature (storing it in msgdata) and then pass it to the default Incoming runner to run chains. This would also make sense from a PGP/MIME standpoint as the “real” message is really the encrypted content so for that’s what other Mailman rules should get in the msg object.

Storing of user and list related metadata is unsolved. This means storing the user key fingerprint for each user / address, as well as storing per-list configuration. The list key fingerprint might not need to be stored as the key can be looked up by gnupg.user_id == list_fqdn. The plugin can have it’s own db although this means data duplication. A solution I see would be to allow plugins to store arbitrary metadata in the core db attached to certain models. So that the mailinglist model would have another attribute plugin_data of PickleType which would really be a dict with the plugin name as keys, with additional api allowing a plugin to access it’s metadata stored in this object.

Extending the Postorious and Hyperkitty interface is unsolved. Since plugins cannot add routes to the REST api. A solution I see that would be general and also require little changes to core would be to let plugins add custom routes to /plugins/<plugin_name>/... which could be then accessed either from a Postorious plugin or a custom app. Maybe with falcon.API.add_sink()?

Even when extending the REST api at core side is possible as just described, that still leaves integrating into HyperKitty and Postorius somehow, out-of-tree, as a django app. Looking at how HyperKitty and Postorius plug into each other currently, we see direct references at each other in the templates:

    {% if 'hyperkitty' in INSTALLED_APPS %}
    {# insert hyperkitty link #}
    {% endif %}

While that might be okay for integrating HyperKitty and Postorius together, and it’s even used at very few places and seems to not be the best solution, it’s not practically usable for integrating this plugin. Ideal integration will encrypt messages when sending them to HyperKitty while showing them decrypted to users. I am thinking of having a custom encrypted HyperKitty archiver that will handle encrypting with proper archive key at the core side and a custom django app at the HyperKitty side that will receive the message, decrypt it and pass it to HyperKitty decrypted. That however leaves the messages decrypted in HyperKitty’s DB so doesn’t really fulfill project requirements. In addition a custom DB engine can be used in HyperKitty’s settings that wraps whatever DB engine and encrypts/decrypts objects on the fly, although I am not sure of the doability of this.


Just installing the plugin into the same virtualenv as the Mailman instance, and setting up the proper options in mailman.cfg should work.