The situation is nothing to write home about. C2 malware, supply-chain risk, ransomware, cryptomining, unsolicited telemetry, plaintext protocols across the Internet, escalating data egress charges – you name it – this one unplugged gap in the Cloud, the outbound connections originating from your deployments, keeps on giving (or taking.)
With no human-readable visibility on any egress flows, not much you can do with all those IP addresses in the flow logs. Talk about flying blind. It's time to install a filtering proxy, and Squid is the word on the grapevine.
Let's take a deep-dive 🔎
A minimal allowlisting Squid config
# /etc/squid/squid.conf
cache deny all
# ensure only trusted client subnets are listed
acl localnet src 10.0.0.0/8
acl localnet src 172.16.0.0/12
acl localnet src 192.168.0.0/16
acl allowlist dstdomain "allowlist.txt"
http_access allow localnet allowlist
http_access deny all
http_port 3128
# /etc/squid/allowlist.txt
foo.auth0.com
mtls.okta.com
# from Thu, 30 Mar 2023 06:00:00 +0000 cutover to the new subdomain at auth0.com
bar.auth0.com
Now the only things we need to worry about are:
❏ discover all the domain names that need to be allowed
❏ develop a mechanism to change config files without downtime, perhaps by pulling down from a bucket on loop and sending the squid process a HUP signal, or redeploying stateless containers and monitoring their load balancer
❏ create an audit log trail of changes made to these config files
❏ ensure logs are parsed and can be searched efficiently
❏ create a subnet for each application, so a least-privilege ACL can be made for each IP range
❏ suppress the healthcheck logs
❏ set minimum inbound & outbound TLS version level to 1.2
❏ create TCP port-based ACLs for letting SSH through, allowed to specific static IP addresses
❏ reconfigure all applications with http_proxy, https_proxy config
❏ add no_proxy config so http requests are only proxied when they need to
...and the proxy server config grows tentacles 🦑
# /etc/squid/squid.conf
...
# here be dragons 🐲
🐉
🦕
...scripts to "orchestrate" emerge 🎼
#!/bin/bash
# don't forget to install bash
set -e # at least stop if a command fails, without a helpful error message
...
...applications become less portable, less testable
val proxyHost = "squid-somewhere.some-zone.local"
val proxyPort = 3128
val httpsProxyTransport = ClientTransport.httpsProxy(InetSocketAddress.createUnresolved(proxyHost, proxyPort))
val settings = ConnectionPoolSettings(system).withTransport(httpsProxyTransport)
Http().singleRequest(HttpRequest(uri = "https://foo.auth0.com"), settings = settings)
Maybe a proxy-less solution?
A discriminating, Cloud-friendly firewall might just be what you need instead 💊
So, how does DiscrimiNAT compare to Squid then?
Squid | DiscrimiNAT | |
---|---|---|
Without Application Changes | 🔴 no a smooth operation can only be achieved if the applications have HTTP_PROXY, HTTPS_PROXY and NO_PROXY configured appropriately; this is so that http:// and https:// traffic is force-routed through the proxy at the application-layer and the inter-VPC, intra-VPC and instance metadata traffic on the link-local interface does not route through it | 🟢 yes zero config change required on the applications no matter which application-layer protocol is involved; all routing is carried out as natively defined on the Cloud platform's route tables and firewall rules, and therefore, works with serverless too |
FQDN Discovery | 🟠 potentially per-application policies are difficult to accomplish (discussed below), therefore, any relaxation of enforcement (so logs can be collected) puts the entire VPC in open mode. Moreover, the entire range of ports and protocols accessed by an application will surely be missed by Squid's typical port-based config | 🟢 yes the see-thru monitoring mode is able to capture and generate filterable logs for specific applications, therefore, keeping policies enforced for the rest of the VPC. Logs also indicate whether current rules would cover the traffic seen or not and can be filtered on this criteria |
Least Privilege egress | 🟠 potentially will require apps to proxy-authenticate so they can be identified and corresponding ACLs applied, or hardcoding of distinct source IP ranges/subnets mapped to specific policies in the config file. These approaches do not align with the Cloud providers' micro-segmentation features | 🟢 yes integrates with the Cloud providers' native application-level constructs such as Firewall Rules in GCP and Security Groups in AWS, deriving egress policies from these resources' built-in fields |
Spoofing Prevention | 🟢 yes proxies terminate incoming connections, read the metadata and policy-permitting, launch new outbound connections, therefore, working around the need to carry out more checks | 🟢 yes inspects raw packets for protocol-level anomalies and also conducts asynchronous, out-of-band DNS checks to verify IP addresses against domain names |
Protocol Downgrade Protection | 🟠 potentially such a capability must be explicitly configured in the config file for both requests and responses of a TLS handshake | 🟢 yes enforces minimum protocol level versions (such as 1.2 for TLS), as per contemporary standards, for both sides of a handshake |
SSH (or SFTP) Protocol Support | 🔴 no proxying at a TCP port level does not check for protocol anomalies, requires workarounds on the client-side, and only IP address-based policies may be applied; it is an HTTP proxy after all | 🟢 yes protocol parsing is implemented sufficiently and natively for deep packet inspection, and policies can be applied on destination FQDNs |
TLS (or HTTPS) Protocol Support | 🟢 yes proxies have their own implementation for protocols such as HTTPS written in low-level languages such as C and C++, linked to common libraries such as OpenSSL because they need to start new connections | 🟢 yes deep packet inspection engines do not reimplement the protocols per se but only a subset of the specifications so packets on the wire can be parsed without interference; DiscrimiNAT is written in Rust, a low-level but memory-safe language |
TLS Decryption | 🟠 potentially if the proxy's CA certificates are installed on clients to trust, it can be configured to decrypt TLS connections and inspect their payload; the onward connection to the intended destination would be initiated by the proxy, and only the proxy will see the true server SSL certificate; the client will see an SSL certificate from the proxy, and the boundary of unencrypted data will move to the proxy node | 🔴 no at this time, DiscrimiNAT does not implement decryption of the full payload or installation of CA keys; the boundary of unencrypted data does not move to this gateway at the Internet/public edge of your network |
Without Connection Termination | 🔴 no proxies by definition terminate the inbound connection and launch a similar one outbound; this adds huge latency with TCP and TLS handshakes needing to be carried out twice for each connection; cipher preferences from the client can get diluted too | 🟢 yes packets are inspected as they flow on the wire with deep packet inspection; since no connections are terminated or new ones initiated, all original handshake preferences are preserved; no impact on latency either |
Cloud-Native Rules Management | 🔴 no rules in the form of ACLs are described in config files, and any updates to them would involve delivering changes to such files and sending a HUP signal to Squid's processes or restarting them; this could involve a lot of work, especially with auditability and exception handling thrown in | 🟢 yes config is pulled from Cloud providers' native constructs such as Firewall Rules in GCP and Security Groups in AWS, therefore enabling the use of their web consoles and any existing infrastructure as code tools such as Terraform; and with auditability since these are intrinsic to the Cloud's APIs |
Cloud-Native Logging | 🔴 no an HTTP proxy's logs are very application-layer oriented and usually very noisy; shipping these into the Cloud platform's logging service in a parseable format requires agent-based, delicate grok config | 🟢 yes whether it's StackDriver or CloudWatch, proper, structured flow and audit logs are separately indexed in mere seconds with rich security-oriented metadata |
Transparent Operation | 🟠 potentially with complex routing, a range of ports defined upfront, and with limitations around TLS interception, some transparent operation can be achieved | 🟢 yes since this replaces the NAT, packets are naturally routed via it at an IP level, and the use of deep packet inspection enables it to work by observation rather than termination |
Safe To Operate | 🔴 no with hundreds of config options and opportunities for policy-bypass with the use of port-based ACLs tied together with IP addresses in hand-crafted config files, it is easy to end up with a security stance that isn't safe; and may inadvertently have holes in it | 🟢 yes has been designed upfront to not have features that can downgrade the security stance expected of egress filtering, and is built for and tested thoroughly on the supported Cloud platforms. No specialised knowledge is required to configure and operate it; can be handed over for self-service operation |
Lecture 18 and Lecture 19 from "Computer and Network Security" at Purdue University are an excellent read for those interested in an in-depth course on proxies vs packet-filtering firewalls.
Enter DX: Developer Experience ⛅
The DiscrimiNAT Firewall has been engineered from the ground up with DX in mind.
👉 No application config required for when and when not to use a proxy 👇
$ env | grep _PROXY
$
👉 Firewall config in Cloud console, precisely where it should be 👇
👉 Keep using Terraform or any other Infrastructure as Code tool 👇
fqdns_saas_auth = [
"foo.auth0.com",
"mtls.okta.com"
]
discriminat_saas_auth = format("discriminat:tls:%s", join(",", local.fqdns_saas_auth))
👉 Support the SSH protocol as well for those SFTP and git clone jobs 👇
git clone git@github.com:EnterpriseQualityCoding/FizzBuzzEnterpriseEdition.git
👉 Structured logs with well thought out fields straight into Logs Explorer 👇
👉 Meet regulatory and compliance encryption standards out of the box 👇
{
...
"proto": "tls",
"proto_v": "1.2",
...
}
Next steps
🚀 Get a technical, full walk-through demo
🚀 Launch free trial from the GCP Marketplace
🚀 Get in touch with our DevSecOps who will:
- not only guide you through the best architecture for your use case
- but also troubleshoot any issues you may encounter
- and answer any geeky questions on protocols and whatnot