/privacy-gateway-server-go

An Oblivious HTTP gateway

Primary LanguageGoBSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

Privacy Gateway Application Server

This project contains a gateway implementation for an Oblivious HTTP (OHTTP) gateway in Go.

Overview

This gateway implements a simple version of the Oblivious Gateway Resource as described in the specification. In particular, it accepts encapsulated Binary HTTP requests and then uses the corresponding HTTP requests to fetch a Target resource. The response from this Target is encapsulated back to the original client of the encapsulated request.

By default, the gateway exposes the following API endpoints:

  • "/gateway": An endpoint that will accept OHTTP requests, fetch the corresponding target resource, and return an OHTTP response.
  • "/gateway-echo": An endpoint that will echo the contents of the encapsulated OHTTP request back in an OHTTP response.
  • "/ohttp-configs": An endpoint that will provide an encoded KeyConfig.
  • "/health": An endpoint for inspecting the health of the gateway (returns 200 in normal conditions).

The gateway only supports the HPKE ciphersuite based on DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, and AES-128-GCM.

The gateway does not currently support key rotation. This issue tracks adding this feature.

Deployment

This section describes deployment instructions for the gateway.

Configuration Variables

The behavior of the gateway is configurable via a number of environment variables. These are explained below.

  • SEED_SECRET_KEY: This environment variable is a hex-encoded byte array representing a secret seed used to derive the gateway private and public key pair. It MUST be 32 randomly generated bytes produced from a cryptographically secure random number generator, such as /dev/urandom. See this guidance for additional information.
  • ALLOWED_TARGET_ORIGINS: This environment variable contains a comma-separated list of target origin names that the gateway is allowed to access. When configured, the gateway will only attempt to resolve requests to target origins in this list. Any other request will yield a HTTP 403 Forbidden return code.
  • CERT: This environment variable is the name of a file containing the certificate (chain) used to serve TLS connections.
  • KEY: This environment variable is the name of a file containing the private key used to serve TLS connections.
  • LOG_FORMAT: This environment variable controls the format in which events are logged. Supported values are:
  • LOG_LEVEL: This environment variable controls how noisy logs are. The supported values correspond to the slog.Level values.
  • TARGET_REWRITES: This environment variable contains a JSON document instructing the gateway to rewrite the target URL found in an encapsulated request to some specified scheme and host.

Target URL rewrites

The TARGET_REWRITES configuration option is useful to set up forwarding between the gateway and target when both are on a private network or if they share a loopback interface. For example, suppose that the target is exposed to the internet at https://example.org, but also reachable by the gateway at http://localhost:8080 (note http and not https). It's more efficient to redirect traffic over localhost than back out over the internet, so you could set TARGET_REWRITES to:

{
	"example.org": { "Scheme": "http", "Host": "localhost:8080" }
}

Then the encapsulated HTTP requests

POST /some-cool-api HTTP/1.1
Host: example.org

some content

or

POST https://example.org/some-cool-api HTTP/1.1

some content

...would both be rewritten to:

POST http://localhost:8080/some-cool-api HTTP/1.1

some content

Custom Application Payloads {#custom-config}

The gateway can be configured to service Binary HTTP (BHTTP) messages or custom application payloads. To use custom applciation payloads, you must specify the type of application request and response encodings using the CUSTOM_REQUEST_TYPE and CUSTOM_RESPONSE_TYPE environment variables. For example, if you were using protobuf as the application data encoding, you might set CUSTOM_REQUEST_TYPE="message/protohttp request" and CUSTOM_RESPONSE_TYPE="message/protohttp response". See the OHTTP library and OHTTP standard for additional information about choosing custom content types. This example protobuf file contains an example protobuf encoding of HTTP messages as an alternate to BHTTP.

When specifying a custom application format, it is also required to implement a new handler for the format. This can be done by adding a new ContentType handler that implements the logic for producing an application response for your application request. As an example, if the custom content type corresponded to DNS messages, the handler might resolve the DNS query and produce an encoded DNS response. Alternatively, if using the example protobuf-based HTTP encoding, the ContentType handler might be implemented as follows:

func protobufHandler(binaryRequest []byte) ([]byte, error) {
	request := &Request{}
	if err := proto.Unmarshal(binaryRequest, request); err != nil {
		return nil, err
	}

        // Convert the protohttp Request to a http.Request equivalent value
	targetRequest, err := protoHTTPToRequest(request)
	if err != nil {
		return nil, err
	}

	client := &http.Client{}
	targetResponse, err := client.Do(targetRequest)
	if err != nil {
		return nil, err
	}

	response, err := responseToProtoHTTP(targetResponse)
	if err != nil {
		return nil, err
	}

	return proto.Marshal(response)
}

That's it!

Local development

To deploy the server locally, first acquire a TLS certificate using mkcert as follows:

$ mkcert -key-file key.pem -cert-file cert.pem 127.0.0.1 localhost

Then build and run the server as follows:

$ make all
$ CERT=cert.pem KEY=key.pem PORT=4567 ./gateway

Preconfigured deployments

Deploy deploy to Scalingo

Manual deployment

This server can also be manually deployed on any bare metal machine, or in cloud providers such as GCP. Instructions for both follow.

Bare metal

Deployment on bare metal servers, such as Equinix, can be done following the instructions below. These steps assume that git and go are both installed on the metal.

  1. Configure a certificate on the metal using certbot. Once complete, the output should be something like the following, assuming the server domain name is "example.com":
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem

You must configure certbot to renew this certificate periodically. The simplest way to do this is via a cron job:

$ 00 00 1 * 1 certbot renew
  1. Configure two environment variables to reference these files:
$ export CERT=/etc/letsencrypt/live/example.com/fullchain.pem
$ export KEY=/etc/letsencrypt/live/example.com/privkey.pem
  1. Clone and build the server:
$ git clone git@github.com:cloudflare/app-relay-gateway-go.git
$ cd app-relay-gateway-go
$ go build ./...
  1. Run the server:
$ PORT=443 ./gateway &

This will run the server until completion. You must configure the server to restart should it terminate prematurely.

GCP

To deploy, run:

$ gcloud app deploy

To check on its status, run:

$ gcloud app browse

To stream logs when deployed, run

$ gcloud app logs tail -s default