New Features
Secure secret storage in Vault
Baseplate now includes support for fetching secrets in a secure, auditable, manner from Hashicorp Vault. A sidecar daemon manages the infrastructure-level authentication with Vault and fetches secrets to a file on disk. Helpers in Baseplate then allow your application to fetch these secrets efficiently from the sidecar daemon with some helpful conventions for versioning/key rotation. This is now the right way to get secret tokens into your application going forward. [See the docs for more info](http://baseplate.readthedocs.io/en/v0.19.0/baseplate/secrets.html).
Error reporting via Sentry
A new span observer integrates Baseplate applications with Raven, the Sentry client. This makes it easy to automatically report exceptions in the application to Sentry. Extra context like the trace ID, git revision of the running application, and context from the incoming request are automatically included in the exception event sent to Sentry. The raven client is also attached to the context object as `context.sentry` for sending custom events as desired.
Changes
* Rework [message signer API](http://baseplate.readthedocs.io/en/v0.19.0/baseplate/crypto.html#message-signing) to take advantage of versioned secrets from Vault. The old class-based API is still supported but is now marked deprecated.
* Rename `make_metrics_client` to `metrics_client_from_config` and `make_tracing_client` to `tracing_client_from_config` to be more consistent with other `_from_config` functions used throughout Baseplate. The old names are still supported but are now marked as deprecated.
Bug fixes
* Fix exception capture in server spans for the Thrift integration.
* Suppress noisy urllib3 logs from zipkin integration.
* Fix context property attachment in local spans, this caused observers like the metrics observer to be called multiple times on the same server span when local spans were used.
* Fix Python 3 import incompatibilities in `baseplate-healthcheck3`.
Upgrading
Adding Vault integration
In your application's entry point, instantiate a `SecretsStore` with [`secrets_store_from_config`](http://baseplate.readthedocs.io/en/v0.19.0/baseplate/secrets.html#baseplate.secrets.secrets_store_from_config) and attach it to the context object with `add_to_context`.
diff
def make_processor(app_config):
+ secrets = secrets_store_from_config(app_config)
+ baseplate.add_to_context("secrets", secrets)
Make sure that any time you use a secret you fetch it from the secret store rather than storing fetched secrets in the application. This ensures that as secrets expire and rotate your application stays up to date.
python
secret = context.secrets.get_versioned("secret/my-service/signing-key")
return make_signature(secret, "This is a signed message!", max_age)
Adding Sentry Integration
Sentry integration is just a matter of configuring and registering the new observer at application startup. For example (from [the activity service](https://github.com/reddit/reddit-service-activity)):
diff
-9,6 +9,7
from baseplate import (
Baseplate,
config,
+ error_reporter_from_config,
metrics_client_from_config,
tracing_client_from_config,
)
-122,6 +123,7 def make_processor(app_config): pragma: nocover
metrics_client = metrics_client_from_config(app_config)
tracing_client = tracing_client_from_config(app_config)
+ error_reporter = error_reporter_from_config(app_config, __name__)
redis_pool = redis.BlockingConnectionPool.from_url(
cfg.redis.url,
max_connections=cfg.redis.max_connections,
-132,6 +134,7 def make_processor(app_config): pragma: nocover
baseplate.configure_logging()
baseplate.configure_metrics(metrics_client)
baseplate.configure_tracing(tracing_client)
+ baseplate.configure_error_reporting(error_reporter)
baseplate.add_to_context("redis", RedisContextFactory(redis_pool))
counter = ActivityCounter(cfg.activity.window.total_seconds())
To configure the Sentry client in your application's config file, get a DSN from Sentry (for Reddit crew, contact IO) then see the docs for [`error_reporter_from_config`](http://baseplate.readthedocs.io/en/v0.19.0/baseplate/index.html#baseplate.error_reporter_from_config) for all the available options.
Make sure to add `python-raven` or `python3-raven` to your development environment.
Updating renamed functions
Update the import and call for `make_metrics_client` and `make_tracing_client` in your application's entry point with the new names `metrics_client_from_config` and `tracing_client_from_config`.
Updating usage of the MessageSigner
The previous `MessageSigner` API was a class that took a static secret and then provided signature generation and validation methods. The new API is bare functions that take the secret as a parameter. This allows you to refetch the secret from the `SecretsStore` immediately before use.
Before:
python
signer = MessageSigner(b"hunter2")
signature = signer.make_signature("message", max_age)
signer.validate_signature("message", signature)
After:
python
secret = context.secrets.get_versioned("secret/my-service/my-secret")
signature = signer.make_signature("message", max_age)
...
secret = context.secrets.get_versioned("secret/my-service/my-secret")
validate_signature(secret, "message", signature)
If you have not set up Vault yet, you can also use [`VersionedSecret.from_simple_secret`](http://baseplate.readthedocs.io/en/v0.19.0/baseplate/secrets.html#baseplate.secrets.VersionedSecret.from_simple_secret) to make a fake versioned secret from a static value for use with the new API.