Skip to content

v0.19.0

Compare
Choose a tag to compare
@spladug spladug released this 25 May 21:43
· 766 commits to develop since this release

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.

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 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 and attach it to the context object with add_to_context.

 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.

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):

@@ -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 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:

signer = MessageSigner(b"hunter2")
signature = signer.make_signature("message", max_age)
signer.validate_signature("message", signature)

After:

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 to make a fake versioned secret from a static value for use with the new API.