A forward proxy for machines, with access control lists
https://hub.docker.com/r/bsycorp/inkfish
This is a non-caching forward (aka egress/outbound) proxy, used to implement URL white-listing for applications.
Key features:
- An outbound proxy designed for machines
- Can use cloud metadata to determine the identity of an instance
- Can use Proxy-Authorization header to identify "non-instance" (e.g. codebuild, serverless) workload
- Per-instance, per-user URL white-lists
- TLS MITM by default, white-lists all requests at the URL level
- Optional MITM bypass, by host
You can start the proxy listening on port 8080 with a built-in demo config:
# Start proxy
docker run -p 8080:8080 bsycorp/inkfish:latest /app/inkfish -metadata none -config /config/demo
# Test as anonymous user
export http_proxy=http://localhost:8080
export https_proxy=http://localhost:8080
export no_proxy=127.0.0.1
curl -k https://ifconfig.io/ # This should work for anonymous
curl -k https://google.com/ # This will not work for anonymous user
# Test as an authenticated user
export http_proxy=http://foo:bar@localhost:8080
export https_proxy=http://foo:bar@localhost:8080
curl -k https://google.com/ # This should work
You can find the demo config over here: /testdata/demo_config/. When you are ready to try out your own white-lists, you can mount them into the proxy container from your host:
docker run -v `pwd`/my_config:/config/mine -p 8080:8080 \
bsycorp/inkfish:latest /app/inkfish -metadata none -config /config/mine
The next step would be to start the proxy in a cloud environment where you can use instance metadata instead of "hard coded" proxy credentials.
Three distribution mechanisms are offered:
- Standard docker:
bsycorp/inkfish:x.y.z
. This is about 30MB and based on minideb. The entry point is a shell. - Slim docker:
bsycorp/inkfish:x.y.z-slim
. A 10MB container with only the static Linux binary. The entry point is the inkfish binary as is customary for containers with no shell. - Linux static binary: You can download this from the (releases page)[https://github.com/bsycorp/inkfish/releases].
If you use the slim
image, you will also need to mount SSL certificates (configuration of what upstream CAs
are trusted) into the container in addition to your proxy configuration files. On a Linux host, this is usually
done by volume mounting SSL certs from the host into the container by adding -v /etc/ssl/certs:/etc/ssl/certs
to the docker run command.
$ docker run bsycorp/inkfish:latest-slim -h
Usage of /app/inkfish:
-addr string
proxy listen address (default ":8080")
-cacert string
path to CA cert file
-cakey string
path to CA key file
-client-idle-timeout int
client idle timeout (default 300)
-client-read-timeout int
client read timeout
-client-write-timeout int
client write timeout
-config string
path to configuration files (default ".")
-drain-time int
shutdown drain deadline (seconds) (default 30)
-insecure-test-mode
test mode (does not block)
-metadata string
default metadata provider (aws,none) (default "aws")
-metadata-update-every int
metadata update interval (default 10)
-metrics string
metrics provider (none,datadog,prometheus) (default "none")
The -config
argument supplies a path to a directory full of "access control lists" and password file
entries.
Passwd files in the config directory must have a .passwd
extension. They may contain one or more
lines of: <username>:<sha256-of-password>
and will be used to verify proxy auth.
It is expected that passwords will generated by infracode / orchestration and have high entropy so a heavyweight password hashing function is not required.
ACL files in the config directory must have a .conf
extension. These control what requests will
be allowed through the proxy. The general format looks like:
from <user> [user2 user3...]
from ...
url [METHOD,METHOD2] <url-regex> [modifiers]
url ...
s3 <bucket-name> [modifiers]
bypass <host-port-regex>
bypass ...
Blank lines and comments (lines starting with #
) are ignored. The from
lines gate entry into the ACL.
The may be specified as:
user:foo
- Identifies a client who will supply a proxy-authorization header with a username offoo
.tag:foo
- Identifies a client whose cloud metadata (e.g. instance ProxyUser tag in AWS) isfoo
.ANONYMOUS
- Identifies a user or system which does not supply a proxy-authorization header and does not have any identifying metadata tags.AUTHENTICATED
- Identifies a client with a tag or valid proxy-authorization credentials.ANYONE
- Any client. This includes clients with invalid proxy-authorization credentials.
The acl
directive is used to permit requests according to a regular expression matching a URL.. You
may optionally specify one or more methods in the ACL, causing only requests made with one of the listed
methods to match the ACL. Typical "whole-host" acls look like:
acl ^http(s)?://foo\.com/
WARNING: it is generally a mistake to forget the trailing /
, as this would cause the regular expression
to match things like https://foo.com.au/evilthing
as well as the intended domain https://foo.com/
.
Similarly, it is usually a mistake to forget to escape dots with backslashes as this can also cause
unintended matches.
A more complex acl example might look like:
acl HEAD,GET,POST ^http(s)://api\.foo\.com/v2/
The bypass
directive is used to disable TLS MITM for specific hosts. You should supply a regular
expression which can be matched directly against the client's CONNECT request. For example:
bypass ^my-super-bucket\.ap-southeast-2\.amazonaws\.com:443$
There is also a shorthand for a url
regex that includes all AWS S3 URL notations
(bucket in path and bucket in host) across all regions
s3 my-super-bucket
Modifiers alter the processing of a particular ACL. Currently supported modifiers are:
quiet
- Suppresses logging of successful requests for the URL pattern or S3 bucket. CONNECT will still be logged, but not individual requests. This is useful for thing like SQS or log upload endpoints where many requests are expected under ordinary circumstances.
Rather than distributing proxy credentials, the preferred method of access control in inkfish is via cloud instance metadata.
For AWS, specify the ProxyUser
tag on an instance. So for example if you apply tag of ProxyUser=foo,
then in your ACL you would write:
from tag:foo
url ^http(s)?://.*$
To grant instances with that tag unrestricted outbound HTTP(s) access.
Configure health checks for the service to hit the listening port on /healthz
.
Set drain-time
to your shutdown connection drain deadline. The default is
30 seconds. If you have a load balancer forwarding requests to inkfish, your
load balancer drain time should be higher than this value.