Cryptography and Message Signing

fedmsg.crypto - Cryptographic component of fedmsg.

Introduction

In general, we assume that ‘everything on the bus is public’. Even though all the zmq endpoints are firewalled off from the outside world with iptables, we do have a forwarding service setup that indiscriminantly forwards all messages to anyone who wants them. (See fedmsg.commands.gateway.gateway for that service.) So, the issue is not encrypting messages so they can’t be read. It is up to sensitive services like FAS to not send sensitive information in the first place (like passwords, for instance).

However, since at some point, services will respond to and act on messages that come across the bus, we need facilities for guaranteeing a message comes from where it ought to come from. (Tangentially, message consumers need a simple way to declare where they expect their messages to come from and have the filtering and validation handled for them).

There should also be a convenient way to turn crypto off both globally and locally. Justification: a developer may want to work out a bug without any messages being signed or validated. In production, certain senders may send non-critical data from a corner of Fedora Infrastructure in which it’s difficult to sign messages. A consumer of those messages should be allowed to ignore validation for those and only those expected unsigned messages

Two backend methods are available to accomplish this:

  • fedmsg.crypto.x509
  • fedmsg.crypto.gpg

Which backend is used is configured by the crypto_backend configuration value.

Certificates

To accomplish message signing, fedmsg must be able to read certificates and a private key on disk in the case of the fedmsg.crypto.x509 backend or to read public and private GnuPG keys in the came of the fedmsg.crypto.gpg backend. For message validation, it only need be able to read the x509 certificate or gpg public key. Exactly which certificates are used are determined by looking up the certname in the certnames config dict.

We use a large number of certs for the deployment of fedmsg. We have one cert per service-host. For example, if we have 3 fedmsg-enabled services and each service runs on 10 hosts, then we have 30 unique certificate/key pairs in all.

The intent is to create difficulty for attackers. If a low-security service on a particular box is compromised, we don’t want the attacker automatically have access to the same certificate used for signing high-security service messages.

Furthermore, attempts are made at the sysadmin-level to ensure that fedmsg-enabled services run as users that have exclusive read access to their own keys. See the Fedora Infrastructure SOP for more information (including how to generate new certs/bring up new services).

Routing Policy

Messages are also checked to see if the name of the certificate they bear and the topic they’re routed on match up in a routing_policy dict. Is the build server allowed to send messages about wiki updates? Not if the routing policy has anything to say about it.

Note

By analogy, “signature validation is to authentication as routing policy checks are to authorization.”

If the topic of a message appears in the routing_policy, the name borne on the certificate must also appear under the associated list of permitted publishers or the message is marked invalid.

If the topic of a message does not appear in the routing_policy, two different courses of action are possible:

  • If routing_nitpicky is set to False, then the message is given the green light. Our routing policy doesn’t have anything specific to say about messages of this topic and so who are we to deny it passage, right?
  • If routing_nitpicky is set to True, then we deny the message and mark it as invalid.

Typically, you’ll deploy fedmsg with nitpicky mode turned off. You can build your policy over time as you determine what services will be sending what messages. Once deployment of fedmsg reaches a certain level of stability, you can turn nitpicky mode on for enhanced security, but by doing so you may break certain message paths that you’ve forgotten to include in your routing policy.

Configuration

By convention, configuration values for fedmsg.crypto are kept in /etc/fedmsg.d/ssl.py, although technically they can be kept in any config dict in /etc/fedmsg.d (or in any of the config locations checked by fedmsg.config).

The cryptography routines expect the following values to be defined:

For general information on configuration, see fedmsg.config.

Module Contents

fedmsg.crypto encapsulates standalone functions for:

  • Message signing.
  • Signature validation.
  • Stripping crypto information for view.

See fedmsg.crypto.x509 and fedmsg.crypto.gpg for implementation details.

fedmsg.crypto.init(**config)

Initialize the crypto backend.

The backend can be one of two plugins:

  • ‘x509’ - Uses x509 certificates.
  • ‘gpg’ - Uses GnuPG keys.
fedmsg.crypto.sign(message, **config)

Insert two new fields into the message dict and return it.

Those fields are:

  • ‘signature’ - the computed message digest of the JSON repr.
  • ‘certificate’ - the base64 certificate or gpg key of the signator.
fedmsg.crypto.strip_credentials(message)

Strip credentials from a message dict.

A new dict is returned without either signature or certificate keys. This method can be called safely; the original dict is not modified.

This function is applicable using either using the x509 or gpg backends.

fedmsg.crypto.validate(message, **config)

Return true or false if the message is signed appropriately.

fedmsg.crypto.validate_signed_by(message, signer, **config)

Validate that a message was signed by a particular certificate.

This works much like validate(...), but additionally accepts a signer argument. It will reject a message for any of the regular circumstances, but will also reject it if its not signed by a cert with the argued name.