A simple HTTP proxy that fogs over naughty URLs
Smokescreen is a HTTP CONNECT proxy. It proxies most traffic from Stripe to the external world (e.g., webhooks).
Smokescreen restricts which URLs it connects to:
Smokescreen also allows us to centralize egress from Stripe, allowing us to give financial partners stable egress IP addresses and abstracting away the details of which Stripe service is making the request.
In typical usage, clients contact Smokescreen over mTLS. Upon receiving a connection, Smokescreen authenticates the client's certificate against a configurable set of CAs and CRLs, extracts the client's identity, and checks the client's requested CONNECT destination against a configurable per-client ACL.
By default, Smokescreen will identify clients by the "common name" in the TLS certificate they present, if any. The client identification function can also be easily replaced; more on this in the usage section.
Smokescreen uses go modules to manage dependencies. The linked page contains documentation, but some useful commands are reproduced below:
go build go test go mod tidy will automatically fetch the latest version of any new dependencies. Running go mod vendor will vendor the dependency.go get [email protected] or go get dep@commit-hash will bring in specific versions of a dependency. The updated dependency should be vendored using go mod vendor.Smokescreen uses a custom fork of goproxy to allow us to support context passing and setting granular timeouts on proxy connections.
Generally, Smokescreen will only support the two most recent Go versions. See the test configuration for details.
Here are the options you can give Smokescreen:
…In order to override how Smokescreen identifies its clients, you must:
smokescreen.Config.RoleFromRequest with your own func(request *http.Request) (string, error)Here is a fictional example that would split a client certificate's OrganizationalUnit on commas and use the first particle as the service name.
…To control the routing of requests to specific IP addresses or IP blocks, use the deny-address, allow-address, deny-range, and allow-range options in the config.
Smokescreen supports rate and concurrency limiting to protect against overload:
| Option | Description |
|---|---|
max-concurrent-requests |
Limits simultaneous in-flight requests. Excess requests receive 503 Service Unavailable. |
max-request-rate |
Limits requests per second using a token bucket algorithm. Excess requests receive 429 Too Many Requests. |
max-request-burst |
Sets token bucket capacity. Must be greater than max-request-rate. Defaults to 2x the rate if omitted. |
max-concurrent-connect-tunnels |
Limits the number of active CONNECT tunnels (long-lived connections). Unlike max-concurrent-requests which only tracks request processing time, this limits actual tunnel connections and prevents resource exhaustion from unbounded CONNECT tunnels. Excess requests receive 429 Too Many Requests. |
Note:
max-concurrent-requestslimits the number of requests being processed, whilemax-concurrent-connect-tunnelslimits the number of active tunnel connections. For CONNECT requests, the request processing completes quickly but the tunnel connection remains open. Use both settings together for complete protection against resource exhaustion.
Example (CLI):
smokescreen --max-concurrent-requests=100 --max-request-rate=50 --max-concurrent-connect-tunnels=50Example (YAML):
connect_timeout: 10s # defaults to 10s; set to 0 to disable the timeout
max_concurrent_requests: 100
max_request_rate: 50
max_request_burst: 150 # optional, defaults to 2x rate
max_concurrent_connect_tunnels: 50 # limits active CONNECT tunnel connectionsA hostname ACL can be described in a YAML formatted file. The ACL, at its top-level, contains a list of services as well as a default behavior.
Three policies are supported:
| Policy | Behavior |
|---|---|
| Open | Allows all traffic for this service |
| Report | Allows all traffic for this service and warns if client accesses a remote host which is not in the list |
| Enforce | Only allows traffic to remote hosts provided in the list. Will warn and deny if remote host is not in the list |
:warning: The ACL is only applied to hostnames as they appear in the request! If you want to allow or deny traffic based on the destination IP address after DNS resolution, you should be using the config options instead (see the
IP Filteringsection above).
A host can be specified with or without a globbing prefix. The host (without the globbing prefix) must be in Punycode to prevent ambiguity.
| host | valid |
|---|---|
example.com |
yes |
*.example.com |
yes |
api.*.example.com |
no |
*example.com |
no |
ex*ample.com |
no |
éxämple.com |
no |
example.* |
hell no |
Here is a sample ACL.
Optionally, you may specify a global allow list and a global deny list for hostnames in your ACL config.
These lists override the policy, but do not override the allowed_domains list for each role.
For example, specifying example.com in your global_allow_list will allow traffic for that domain on that role, even if that role is set to enforce and does not specify example.com in its allowed domains.
Similarly, specifying malicious.com in your global_deny_list will deny traffic for that domain on a role, even if that role is set to report or open.
However, if the host specifies malicious.com in its allowed_domains, traffic to malicious.com will be allowed on that role, regardless of policy.
:warning: The global_deny_list will only block specific hostnames, not entire destinations. For example, if
malicious.comis in the global_deny_list but the IP address that it resolves to is not, roles with anopenpolicy will still be able to access the destination by using its IP address directly. For this reason, we recommend using allowlists instead of denylists whenever it is possible to do so, and blocking IP addresses via config options, not the ACL (see theIP Filteringsection above).
If a domain matches both the global_allow_list and the global_deny_list, the global_deny_list behavior takes priority.
Here is a sample ACL specifying these options.
See Development.md
No open issues yet, or sync has not completed.