/http-message-signing-proxy

An HTTP proxy intended for use with the Form3 API that signs incoming requests using HTTP Message Signatures RFC

Primary LanguageGoApache License 2.0Apache-2.0

HTTPS signing proxy

Introduction

The request signing proxy would sit in front of the client and intercept outbound requests, sign them with client's private key and transfer signed requests to the server. It uses the request signing library here.

design.png

More about request signing here.

Run the proxy

The proxy requires --config flag, which point to the config file. See configuration section below.

./signing-proxy --config <config_file_path>

The proxy can alternatively be run in a Docker image. An example is provided in Dockerfile.

docker build -t signing-proxy .
docker run -p <host_port>:<server_config_port> -v <config_file_path>:/config.yaml --config /config.yaml

In example directory, there are several files which help running the proxy locally:

  • config_example.yaml: A complete config file that should be in --config flag.
  • cert.crt: A self-signed certificate for an HTTPS proxy (TLS mode only).
  • private.key: The private key associated with the certificate above (TLS mode only).
  • rsa_private_key.pem: A private key that is used to sign incoming request.
  • rsa_public_key.pub: The public key associated with the private key above. It is not used by the proxy but will be useful if you need to verify the signature.
  • docker-compose.yaml: A convenient docker compose file that runs the proxy with all the config above. The proxy serves HTTPS requests at port 8080.

Configuration

Configuration is typically done with a yaml file. Refer to config_example.yaml for all configurable options.

Configuration override

One can override any string field (list field override is not supported) with --set flag or environment variable.

Override config using --set flag

To override specific fields, --set key=value flag can be set multiple times. For example, proxy.signer.keyId and log.level in the yaml file above can be overridden by:

./signing-proxy --config <config_file_path> \
  --set proxy.signer.keyid=5099392e-3040-40f9-ac70-ce66a9ee0ed6 \
  --set log.level=debug

Override config using env var

A a.b.c field can be automatically overridden by setting a A_B_C env var (all capitalised and dots replaced by underscore). For example, proxy.signer.keyId and proxy.signer.bodyDigestAlgo in the yaml file above can be overridden by:

export PROXY_SIGNER_KEYID=5099392e-3040-40f9-ac70-ce66a9ee0ed6
export PROXY_SIGNER_BODYDIGESTALGO=SHA-512

Proxy mechanism

Any request coming in the proxy will be signed and forwarded to the upstream target, meaning the host will be replaced by the target host and a signature header will be added, the rest of the request is kept as-is. If the request fails the signing validation (due to missing headers for example), the request will not be proxied and the server will return a 400 - Bad Request response to the client.

The upstream target can be another proxy. In that case, HTTP_PROXY, HTTPS_PROXY and NO_PROXY environment variables can be explicitly set.

Additionally, there are two endpoints explicitly exposed by the proxy:

  • GET /-/health for health check.
  • GET /-/prometheus for metrics.

Incoming requests like above will not be signed nor forwarded to the upstream target.

Metrics

The proxy publishes certain metrics under GET /-/prometheus endpoint:

Metric name Type Description
internal_error_total Counter Total number of the proxy's internal errors. Upstream errors do not count.
request_count_total Counter Total number of requests coming to the proxy.
signed_request_total Counter Total number of incoming requests that have been signed and proxied.
signing_duration_seconds Histogram Request signing duration time in seconds.
request_duration_seconds Histogram Total request duration time in seconds, including signing and upstream processing.

Testing and Linting

To ensure the code has high quality, readability and maintainability, we use golangci-lint for linting and execute both happy and sad test paths in our tests.

The default make command will run both linting and tests

Linting can be run individually using:

make lint

Tests can be run individually using:

make test

Contributions

If you'd like to help improve http-message-signing-proxy, please fork this repo and raise a PR!