• Stars
    star
    185
  • Rank 208,271 (Top 5 %)
  • Language
    Go
  • License
    Apache License 2.0
  • Created over 1 year ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

HTTP proxy that injects 3rd party credentials into requests

Tokenizer

Tokenizer is an HTTP proxy that injects third party authentication credentials into requests. Clients encrypt third party secrets using the proxy's public key. When the client wants to send a request to the third party service, it does so via the proxy, sending along the encrypted secret in the Proxy-Tokenizer header. The proxy decrypts the secret and injects it into the client's request. To ensure that encrypted secrets can only be used by authorized clients, the encrypted data also includes instructions on authenticating the client.

Here's an example secret that the client encrypts using the proxy's public key:

secret = {
    inject_processor: {
        token: "my-stripe-api-token"
    },
    bearer_auth: {
        digest: Digest::SHA256.base64digest('trustno1')
    }
}

seal_key = ENV["TOKENIZER_PUBLIC_KEY"]
sealed_secret = RbNaCl::Boxes::Sealed.new(seal_key).box(secret.to_json)

The client configures their HTTP library to use the tokenizer service as it's HTTP proxy:

conn = Faraday.new(
    proxy: "http://tokenizer.flycast", 
    headers: {
        proxy_tokenizer: Base64.encode64(sealed_secret),
        proxy_authorization: "Bearer trustno1"
    }
)

conn.get("http://api.stripe.com")

The request will get rewritten to look like this:

GET / HTTP/1.1
Host: api.stripe.com
Authorization: Bearer my-stripe-api-token

Notice that the client's request is to http://api.stripe.com. In order for the proxy to be able to inject credentials into requests we need to speak plain HTTP to the proxy server, not HTTPS. The proxy transparently switches to HTTPS for connections to upstream services. This assumes communication between the client and tokenizer happens over a secure transport (a VPN).

Processors

The processor dictates how the encrypted secret gets turned into a credential and added to the request. The example above uses inject_processor, which simply injects the verbatim secret into a request header. By default, this injects the secret into the Authorization: Bearer header without further processing. The inject_processor can optionally specify a destination and/or printf-style format string to be applied to the injection of the credential:

secret = {
    inject_processor: {
        token: "my-stripe-api-token",
        dst:   "X-Stripe-Token",
        fmt:   "token=%s",
    },
    bearer_auth: {
        digest: Digest::SHA256.base64digest('trustno1')
    }
}

This will result in the header getting injected like this:

X-Stripe-Token: token=my-stripe-api-key

Aside from inject_processor, we also have inject_hmac_processor. This creates an HMAC signatures using the key stored in the encrypted secret and injects that into a request header. The hash algorithm can be specified in the secret under the key hash and defaults to SHA256. This processor signs the verbatim request body by default, but can sign custom messages specified in the msg parameter in the Proxy-Tokenizer header (see about parameters bellow). This processor also respects the dst and fmt options.

secret = {
    inject_hmac_processor: {
        key: "my signing key",
        hash: "sha256"
    },
    bearer_auth: {
        digest: Digest::SHA256.base64digest('trustno1')
    }
}

Request-time parameters

If the destination/formatting might vary between requests, inject_processor and inject_hmac_processor can specify an allowlist of dst/fmt parameters that the client can specify at request time. These parameters are supplied as JSON in the Proxy-Tokenizer header after the encrypted secret.

secret = {
    inject_processor: {
        token: "my-stripe-api-token"
        allowed_dst: ["X-Stripe-Token", "Authorization"],
        allowed_fmt: ["Bearer %s", "token=%s"],
    },
    bearer_auth: {
        digest: Digest::SHA256.base64digest('trustno1')
    }
}

seal_key = ENV["TOKENIZER_PUBLIC_KEY"]
sealed_secret = RbNaCl::Boxes::Sealed.new(seal_key).box(secret.to_json)

processor_params = {
    dst: "X-Stripe-Token", 
    fmt: "token=%s"
}

conn.headers[:proxy_tokenizer] = "#{Base64.encode64(sealed_secret)}; #{processor_params.to_json}"

conn.get("http://api.stripe.com")

Host allowlist

If a client is fully compromised, the attacker could send encrypted secrets via tokenizer to a service that simply echoes back the request. This way, the attacker could learn the plaintext value of the secret. To mitigate against this, secrets can specify which hosts they may be used against.

secret = {
    inject_processor: {
        token: "my-stripe-api-token"
    },
    bearer_auth: {
        digest: Digest::SHA256.base64digest('trustno1')
    },
    allowed_hosts: ["api.stripe.com"],
    # or
    # allowed_host_pattern: ".*\.stripe\.com$"
}

Production deployment — fly.io

Assuming you have flyctl installed, start by cloning this repository

git clone https://github.com/superfly/tokenizer
cd ./tokenizer

create a fly.io app:

fly app create
export FLY_APP="<name of app>"

generate a private (open) key:

OPEN_KEY=$(openssl rand -hex 32)
fly secrets set --stage OPEN_KEY=$OPEN_KEY

Deploy the app without making it available on the internet1:

fly deploy --no-public-ips

Tokenizer is now deployed and accessible to other apps in your org at <name of app>.flycast. The deploy logs will contain the public (seal) key, which can be used for encrypting secrets.

1Assigning a public IP address to the app is not recommended, since it will happily proxy traffic to private IP addresses. If you require a public deployment, consider running tokenizer in a separate, dedicated organization or using it in conjuction with smokescreen.

Production deployment — custom

Tokenizer is totally stateless, so it's simple to deploy anywhere.

Assuming you have Golang installed, you can build and install tokenizer in /usr/local/bin by running

GOBIN=/usr/local/bin go install github.com/superfly/tokenizer/cmd/tokenizer@latest

Generate a private (open) key:

export OPEN_KEY=$(openssl rand -hex 32)

Run the tokenizer server:

tokenizer

The output will contain the public (seal) key, which can be used for encrypting secrets.

Test deployment

See the READMEs in github.com/superfly/tokenizer/cmd/tokenizer and github.com/superfly/tokenizer/cmd/curl for instructions on running/testing tokenizer locally.

Configuration

Tokenizer is configured with the following environment variables:

  • OPEN_KEY - The hex encoded 32 byte private key is used for decrypting secrets.
  • LISTEN_ADDRESS - The address (ip:port) to listen on.
  • FILTERED_HEADERS - A comma separated list of request headers to strip from client requests.

More Repositories

1

litefs

FUSE-based file system for replicating SQLite databases across a cluster of machines
Go
4,017
star
2

flyctl

Command line tools for fly.io services
Go
1,352
star
3

fly

Deploy app servers close to your users. Package your app as a Docker image, and launch it in 17 cities with one simple CLI.
981
star
4

corrosion

Gossip-based service discovery (and more) for large distributed systems.
Rust
673
star
5

fly-log-shipper

Ship logs from fly to other providers
Shell
277
star
6

flyctl-actions

:octocat: GitHub Action that wraps the flyctl
TypeScript
266
star
7

init-snapshot

Fly
Rust
259
star
8

ltx

Go library for the LTX file format
Go
197
star
9

fly-pr-review-apps

Github Action for PR Review Apps on Fly.io
Shell
182
star
10

wormhole

Fly.io secure tunnel
Go
181
star
11

edge

A set of useful libraries for Edge Apps. Run locally, write tests, and integrate it into your deployment process. Move fast and maybe don't break things? Because, gosh darnit, you're an adult.
TypeScript
144
star
12

docs

HTML
101
star
13

fly_postgres_elixir

Library for working with local read-replica postgres databases and performing writes through RPC calls to other nodes in the primary Fly.io region.
Elixir
100
star
14

fly-ruby

Ruby gem for handling requests within a Fly.io multiregion database setup
Ruby
86
star
15

ssokenizer

Go
84
star
16

litefs-example

An example of deploying LiteFS on Fly.io.
Go
74
star
17

macaroon

Fly.io macaroon implementation
Go
70
star
18

fly_rpc_elixir

Makes it easier to build distributed Elixir applications on the Fly.io platform. Adds region awareness to an app and makes it easy to perform RPC calls in other Fly.io regions.
Elixir
52
star
19

litevfs

LiteFS VFS SQLite extension for serverless environments
Rust
46
star
20

rchab

Fly.io Remote Builder (Remote Controlled Hot Air Balloon)
Go
43
star
21

fly.rs

Fly Edge Application runtime.
Rust
40
star
22

elixir-hiring-project

This is a sample project we use to assess Elixir/Phoenix/LiveView skills, and also simulate an idea of the type of work a dev advocate could end up doing at Fly.io.
Elixir
35
star
23

dropspy

Go implementation of the venerable dropwatch utility
Go
34
star
24

fly-node

Node.js Express-compatible middleware for enhancing apps deployed on Fly.io
TypeScript
34
star
25

tired-proxy

An http proxy that's just too tired and eventually shuts down
Go
33
star
26

fly-autoscaler

A metrics-based autoscaler for Fly.io
Go
28
star
27

fly-go

Go client for the Fly.io API
Go
21
star
28

bfaas

Bash functions-as-a-service
TypeScript
21
star
29

onehostname

A Fly Edge Application for combining a bunch of different backends under one hostname
TypeScript
17
star
30

stimulus-live-elements

Live Element support for Rails via Stimulus
JavaScript
17
star
31

gravitas

Ruby
17
star
32

fly-checks

Basic health-check framework
Go
15
star
33

machine-proxy

PoC HTTP proxy for scale-to-zero apps via the Fly machines API
Go
15
star
34

burn

HTTP load testing tool
Go
13
star
35

fly-rails

Rails support for Fly-io
Ruby
13
star
36

image-processing

TypeScript
13
star
37

multi-cloud-haproxy

Awk
12
star
38

doh-proxy

Docker image: DNS over HTTPS proxy
Dockerfile
11
star
39

recco

Gain information about applications to inform deployments
Go
11
star
40

litefs-go

Go library for interacting with LiteFS.
Go
11
star
41

sqlite3-restore

Utility for performing an online restore of a SQLite database.
Go
9
star
42

flyjs

The Fly JavaScript runtime (archived)
TypeScript
9
star
43

dashboards

Collection of dashboards for visualizing Fly.io app data
9
star
44

dns-help

Instructions for pointing a domain at your Fly edge application
JavaScript
8
star
45

glitch-domains

JavaScript
8
star
46

anti-roadmap

This is the opposite of a roadmap. A place where we track things we're probably not going to do.
8
star
47

rant2outline

Convert those rants to meetup talk outlines with the power of AI!
TypeScript
8
star
48

fly-builders

CNB Buildpacks for Fly - supporting (so far) Deno
Shell
7
star
49

fly.cr

Crystal HTTP middleware to handle fly.io multi-region postgres replays.
Crystal
7
star
50

wordpress-image-optimizer

JavaScript
7
star
51

pixelfly

An HTTP image service
TypeScript
6
star
52

ghost-accelerator

JavaScript
6
star
53

ltx-rs

Rust library for the LTX file format
Rust
6
star
54

shout

Shouting is an effective way of communicating between hot air balloons
Go
5
star
55

load-balancer

Fly application load balancer.
TypeScript
5
star
56

anycable-rails

Run Anycable with Rails on Fly
Ruby
5
star
57

llm-describer

A simple image describer service using Ollama and PocketBase
Go
5
star
58

embedding-v8-talk

A talk about embedding v8
TypeScript
4
star
59

flygit

https://rawgit.com on Fly
JavaScript
4
star
60

fly-heroku-buildpack

Heroku buildpack that automatically connects apps to Fly.io
Shell
4
star
61

homebrew-tap

Ruby
4
star
62

litefs-node

A Node.js library for interacting with LiteFS.
C++
4
star
63

cee-dee-en

A basic CDN example using a Fly Edge App
JavaScript
3
star
64

glitch-custom-hostnames

Run any glitch app with a custom hostname
JavaScript
3
star
65

litefs-ruby

Ruby library for interacting with LiteFS.
Makefile
3
star
66

talks

Various talks we've given
TypeScript
3
star
67

tokenizer-elixir

Elixir
3
star
68

macaroon-elixir

Elixir library for attenuating Fly.io macaroons
Elixir
3
star
69

rust-templates

Rust
3
star
70

aws-lambda-express-example

A light-weight example of AWS Lambda, using Express.js and Claudia.js.
JavaScript
2
star
71

fileserver

Simple and dockerized fasthttp-based fileserver
Go
2
star
72

domain-reseller-guide

2
star
73

fly-openapi-sdk

Generated API clients for the Fly machines API
Python
2
star
74

flu

Start of a utility command line tool
Go
2
star
75

lfsc-go

Go client for LiteFS Cloud
Go
2
star
76

csharp-example

Deploy an example c# app to Fly
C#
2
star
77

fly-docker-example

Demonstrates horizontal scaling with Docker/Nginx and Docker/Fly.
JavaScript
2
star
78

cookiebot

Example macaroon discharge service
Go
2
star
79

redis-cache-example

Go
1
star
80

api-versioning

JavaScript
1
star
81

actioncable_redis-reconnect

A patch for ActionCable Redis that reconnects after it disconnects
Ruby
1
star
82

ruby-example

Deploy an example Ruby app to Fly
Ruby
1
star
83

fly-on-deno

TypeScript
1
star
84

pushpin-example

Pushpin on Fly
Dockerfile
1
star
85

ab-test-example-app

JavaScript
1
star
86

tipe-react-edge

Rendering Tipe API responses with React on the edge
JavaScript
1
star
87

responsive-images

Make images on any app responsive
JavaScript
1
star
88

go-example

Deploy an example Go app to Fly
Dockerfile
1
star
89

gpu-burn-app

A Fly app to stress GPU cards
Dockerfile
1
star
90

fsmap

Go module to list free block ranges on Linux filesystems by calling the `GETFSMAP` ioctl.
Go
1
star
91

fly-rust-axum-starter

Dockerfile
1
star