BFE 1.8.8.6
Overview
This guide covers launching and verifying BFE 1.8.8.6 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace. You pick it as the software version when you subscribe and launch. The other VPN, proxy and remote access servers in the listing are described on the listing's guide hub.
BFE is an open-source layer-7 load balancer and reverse proxy. It is a CNCF sandbox project that started at Baidu. It routes HTTP, HTTPS and HTTP/2 traffic by host name, path, header, cookie or client address to clusters of backend servers. It balances load across them with health checks and is extended through modules (header rewriting, redirects, rate limiting, access logs and more).
On this image, BFE itself terminates HTTPS on port 443 with a certificate generated on your instance's first boot. Port 80 only redirects to HTTPS. Out of the box, BFE routes every request for the instance's own names to a small demo backend on 127.0.0.1:8081. You can see the whole path working before you point it at your own servers.
SecureAnyCloud compiles BFE 1.8.8.6 from the upstream release source with Go 1.27.3 and patched dependencies: golang.org/x/net 0.60.0, google.golang.org/grpc 1.83.2 and go-jose 4.1.4. These clear the Go security advisories that the upstream dependency set carries. govulncheck on the shipped binary reports no vulnerabilities.
mod_auth_jwt is not included (upstream depends on an unfixed JWT library). BFE's JWT authentication module uses github.com/golang-jwt/jwt v3, which has an advisory with no fix in that major version (GO-2025-3553). SecureAnyCloud therefore leaves the module out of the binary.
The build record, with the source, toolchain and dependency checksums, is in /usr/share/doc/bfe/BUILD-PROVENANCE.txt.
What you get:
- BFE 1.8.8.6 (
bfe.service) running as the unprivilegedbfeuser, allowed only to bind ports 80 and 443 - HTTPS on 443 served by BFE with a self-signed certificate and a TLS session-ticket key generated on your instance's first boot. Port 80 only redirects to HTTPS.
- BFE's monitor, metrics and reload API on
127.0.0.1:8421only - A demo backend (nginx, Ubuntu 24.04 package) on
127.0.0.1:8081, the default cluster until you add your own servers - Helpers:
bfe-selftest,bfe-reload(test and hot-reload the configuration) andbfe-tls-cert(certificate and served names) - No metadata access. Neither BFE nor nginx can reach the instance metadata service.
Default user: ubuntu | Architecture: x86_64 | Root volume: 8 GiB gp3
Security posture
- No default credential and no shared secret. BFE has no login. The upstream sample configuration ships published private keys, a published session-ticket key and two demo accounts; none of them is on this image. First boot generates the TLS key, certificate and session-ticket key for this instance only (mode 0640, owner
root, groupbfe). - Patched dependencies, and no unfixed JWT library. mod_auth_jwt is not included (upstream depends on an unfixed JWT library). A configuration that names it is refused with
no module for mod_auth_jwt, so it can never be switched on by mistake. The other compiled-in modules work as upstream documents them. - Only your names are served. BFE answers only the names first boot read from the instance metadata (public IPv4 address, public DNS name, private IPv4 address) and
localhost. A request for any other host name is refused and never reaches a backend. - Client addresses come from the connection, never from headers. BFE overwrites
X-Forwarded-For,X-Real-Ip,X-Forwarded-PortandX-Forwarded-Hostwith the real peer address and the configured host name before a request reaches your backend. Upstream BFE appends to whatever the client sent; this image replaces it. - Internal services stay local. The monitor and reload API (
8421) and the demo backend (8081) listen on127.0.0.1only. The monitor binds there through BFE'sMonitorAddrsetting. - TLS 1.2 with AEAD ciphers only (ECDHE with AES-GCM or ChaCha20-Poly1305) on port 443. BFE's TLS stack does not offer TLS 1.3.
- No SSRF to instance credentials.
bfe.serviceandnginx.servicecarry anIPAddressDenyrule for the metadata addresses (169.254.169.254, fd00:ec2::254, 168.63.129.16), so the proxy can never fetch instance credentials, whatever backend it is pointed at. BFE's start-up check also refuses a configuration that names the metadata service. Nothing broader is blocked, so your backends can be anywhere. The AMI is registered IMDSv2 only. - Least privilege. BFE runs as the
bfesystem user with one capability (binding ports below 1024), a read-only system and a private/tmp. - Fails closed. BFE starts only after first boot has written this instance's key material and a ready marker. Every start runs a check and BFE's own configuration test first. If first boot cannot finish, BFE stays stopped and first boot tries again on the next boot.
- Per-instance identity. SSH host keys and the machine ID are generated fresh on every instance.
Prerequisites
You need:
- An AWS account and permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
- The servers you want to balance, reachable from the instance (in the same VPC, or over the internet)
Recommended instance type: m5.large. BFE is light, so size the instance for your traffic.
Connecting to your instance
Version Operating system SSH user
BFE 1.8.8.6 on Ubuntu 24.04 Ubuntu 24.04 LTS ubuntu
Launch the instance
Option A: AWS Marketplace console
- Open the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace. Choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- When the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose BFE 1.8.8.6 on Ubuntu 24.04, then choose your Region.
- Pick an instance type (
m5.largerecommended; the software charge is $0.04 per vCPU per hour) and your key pair. - Review the security group. The listing's default group allows SSH (22), HTTP (80, redirect only) and HTTPS (443). Restrict SSH to your own IP address.
- Choose your VPC and a public subnet (or attach an Elastic IP later). Leave the metadata defaults, because the image already requires IMDSv2. Then choose Launch.
Option B: AWS CLI
Run this on your own workstation. It creates a security group that allows SSH from your management address and the web ports from everyone, then launches the instance.
Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the VPC, subnet, key pair and CIDR range with your own:
SG=$(aws ec2 create-security-group --group-name bfe \
--description "BFE layer-7 load balancer" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 80 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr 0.0.0.0/0
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--associate-public-ip-address \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=bfe}]'You do not need --metadata-options: the AMI already makes IMDSv2 required.
Connect and read the connection details
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>First boot finishes within a minute of launch. It issues the TLS certificate for the instance's addresses, generates the session-ticket key, and starts the demo backend and BFE.
The details are in /root/bfe-endpoints.txt, readable only by root. Nothing in it is a secret:
sudo cat /root/bfe-endpoints.txt# BFE 1.8.8.6 - generated on first boot by bfe-firstboot.service. Nothing here is a secret.
# The TLS key and session-ticket key in /etc/bfe/tls_conf were generated on this instance; none was baked into the image. BFE_HOST=3.93.246.80 BFE_URL=https://3.93.246.80/ BFE_NAMES=3.93.246.80 ec2-3-93-246-80.compute-1.amazonaws.com 172.31.82.170 localhost 127.0.0.1 TLS_CERT_SHA256=F1:A5:69:3E:51:3E:83:1F:BE:03:54:19:1A:F0:82:79:20:8B:38:37:3E:38:8E:57:FA:2B:70:B0:60:16:E0:A4 BFE_MONITOR=http://127.0.0.1:8421/monitor BFE_CONFIG=/etc/bfe/bfe.conf BFE_BACKENDS=/etc/bfe/cluster_conf/cluster_table.data
# Open TCP 443 (and 80, which only redirects) in the security group to the clients that should reach BFE.
# Your backends: edit /etc/bfe/cluster_conf/cluster_table.data, then
sudo bfe-reload
# New name or Elastic IP:
sudo bfe-tls-cert <name-or-ip> ...
# Self-test:
sudo bfe-selftest
# Guide: https://secureanycloud.com/stacks/bfe-1-8-8-6/Confirm BFE is running
Check that BFE and the demo backend are active and that first boot has completed. BFE listens on ports 80 and 443 on every interface. The monitor (8421) and the demo backend (8081) listen on 127.0.0.1 only:
systemctl is-active bfe.service nginx.service bfe-firstboot.service bfe -v 2>/dev/null
sudo ss -Htlnp '( sport = :80 or sport = :443 or sport = :8421 or sport = :8081 )' | awk '{print $4}' | sort -uactive active active bfe version: 1.8.8.6 *:443 *:80 127.0.0.1:8081 127.0.0.1:8421Run the self-test
The shipped self-test checks the whole path from the instance itself. It confirms that:
- an HTTPS request for the instance's name traverses BFE to the demo backend
- the certificate served is this instance's
- port 80 redirects to HTTPS
- a host name the instance does not serve is refused
- BFE's request counter moves
- the monitor and demo backend are refused on the private address
- the instance metadata service is unreachable from inside BFE and nginx
Run it:
sudo bfe-selftestok: bfe and nginx are active ok: monitor answers on 127.0.0.1:8421 ok: an HTTPS request for 3.93.246.80 traverses BFE to the loopback-only demo backend ok: the certificate served on 443 is this instance's (F1:A5:69:3E:51:3E:83:1F...) ok: port 80 redirects to https://3.93.246.80/; an unknown Host is refused on 80 and 443 ok: BFE's request counter moved (10 -> 14) ok: monitor 8421 and demo backend 8081 are refused on 172.31.82.170 ok: metadata: 169.254.169.254 is unreachable from inside bfe and nginx SELFTEST_OKSend traffic through BFE
A request for the instance's name over HTTPS reaches the demo backend, and BFE adds its own X-Proxied-By: bfe header. Plain HTTP is redirected to HTTPS. A request for a name the instance does not serve is refused with HTTP 500 and never reaches a backend:
H=$(sudo sed -n 's/^BFE_HOST=//p' /root/bfe-endpoints.txt)
curl -sk -D - -o /tmp/bfe-page.html --resolve "$H:443:127.0.0.1" "https://$H/" | grep -iE '^(HTTP|x-proxied-by)' grep -o '<title>[^<]*</title>' /tmp/bfe-page.html; rm -f /tmp/bfe-page.html
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' -H "Host: $H" http://127.0.0.1/docs
curl -sk --http1.1 -o /dev/null -w '%{http_code}\n' -H 'Host: www.example.org' https://127.0.0.1/HTTP/2 200 x-proxied-by: bfe <title>sac BFE demo backend</title> 301 https://3.93.246.80/docs 500Open https://<public-ip>/ in your own browser. After you accept the self-signed certificate, you see the demo backend page, served through BFE.
Point BFE at your own servers
BFE's routing has three parts:
server_data_conf/host_rule.datamaps host names to a product (sac_demoon this image).server_data_conf/route_rule.datasends a product's requests to a cluster.cluster_conf/cluster_table.datalists the servers in each cluster.
The shipped cluster has one server, the demo backend:
sudo cat /etc/bfe/cluster_conf/cluster_table.data{ "Version": "sac-1", "Config": { "cluster_demo": { "demo.local": [ { "Addr": "127.0.0.1", "Name": "sac_demo_backend", "Port": 8081, "Weight": 10 } ] } } }Replace the demo server with your own. Add as many entries as you have servers; Weight balances between them.
Change the health check in /etc/bfe/server_data_conf/cluster_conf.data (CheckConf: Uri, Host, StatusCode) to a URL that your servers answer. Then test and hot-reload the configuration. Nothing is reloaded if the test fails:
sudo nano /etc/bfe/cluster_conf/cluster_table.data
sudo nano /etc/bfe/server_data_conf/cluster_conf.data
sudo bfe-reloadBFE ignores SIGHUP. Instead, bfe-reload (also behind sudo systemctl reload bfe) runs bfe -t on the configuration and then calls BFE's reload API for routing, clusters, names, TLS and module rules.
Allow traffic from this instance's security group to your servers, and stop exposing them directly. The self-test's traversal check looks for the demo page. Once you replace the demo server, it reports that line as failed; everything else still applies.
To route different host names or paths to different clusters, add products to host_rule.data and conditions to route_rule.data (for example req_path_prefix_in("/api/", false)). See the BFE routing documentation.
Use your own domain name
bfe-tls-cert re-issues the self-signed certificate, updates the names BFE serves and hot-reloads BFE. With no argument, it reads the instance's current addresses from the instance metadata, which is useful after you attach an Elastic IP. With arguments, it uses the names you give, for example the DNS name that your record points at this instance:
sudo bfe-tls-cert <www.example.com>
sudo bfe-selftest | tail -1bfe-tls-cert always adds localhost and 127.0.0.1.
To use a certificate from a public CA instead, copy its full chain to /etc/bfe/tls_conf/certs/sac.crt and its key to /etc/bfe/tls_conf/certs/sac.key (mode 0640, owner root, group bfe). Make sure every DNS name in /etc/bfe/tls_conf/tls_rule_conf.data (SniConf) is in the certificate. Then run sudo bfe-reload.
Read BFE's metrics
The monitor API listens on 127.0.0.1:8421 only. It has no password, so keep it there. It also serves the reload endpoints. Counters are JSON:
curl -s 'http://127.0.0.1:8421/monitor/proxy_state?format=json' | python3 -c 'import json,sys; c=json.load(sys.stdin)["CounterData"]; [print(k, c[k]) for k in ("CLIENT_REQ_SERVED","HTTPS_CLIENT_REQ_SERVED","ERR_BK_FIND_PRODUCT")]'CLIENT_REQ_SERVED 17 HTTPS_CLIENT_REQ_SERVED 5 ERR_BK_FIND_PRODUCT 9ERR_BK_FIND_PRODUCT counts requests for host names BFE does not serve. The full list of monitor pages is at http://127.0.0.1:8421/monitor.
To collect metrics from a server elsewhere, use an SSH tunnel or a local agent rather than opening port 8421.
mod_auth_jwt is not included
mod_auth_jwt is not included (upstream depends on an unfixed JWT library). The binary contains neither the module nor the JWT library, and the build record says why:
grep -ac mod_auth_jwt /usr/local/bin/bfe || true
grep -A2 'mod_auth_jwt is not included' /usr/share/doc/bfe/BUILD-PROVENANCE.txt0 modules were raised in go.mod to clear Go security advisories, and mod_auth_jwt is not included (upstream depends on an unfixed JWT library: github.com/golang-jwt/jwt v3.2.2, GO-2025-3553 / CVE-2025-30204, which has no v3 fix). Licence: Apache-2.0 (LICENSE and NOTICE in this directory). Copyright The BFE Authors.If you add Modules = mod_auth_jwt to /etc/bfe/bfe.conf, BFE's configuration test fails with no module for mod_auth_jwt, and BFE does not start with it.
To authenticate clients, put the authentication in your backend application, or use BFE's mod_auth_request module to ask an authentication service that you run. SecureAnyCloud will include mod_auth_jwt again once BFE moves to a JWT library with the fix.
Metadata access is blocked
Both services carry the metadata deny rule. The self-test in Step 4 proved that the metadata service does not answer from inside either of them:
systemctl show -p IPAddressDeny bfe.service nginx.service | grep .IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32Even if a cluster were pointed at 169.254.169.254, BFE's start-up check would refuse the configuration, and the deny rule would block the connection anyway.
How the fail-closed start works
BFE starts only once first boot has written /var/lib/sac/data/bfe-ready.
Every start runs a check (as root) that refuses to start BFE without this instance's key material, with a cluster that names the metadata service, or with mod_auth_jwt. Then BFE's own configuration test runs. Only then does BFE start, as the bfe user:
systemctl cat bfe.service | grep -E '^(ConditionPathExists|ExecStartPre|User|AmbientCapabilities)='
sudo stat -c '%a %U:%G %n' /etc/bfe/tls_conf/certs/sac.key /etc/bfe/tls_conf/session_ticket_key.data /root/bfe-endpoints.txt
sudo grep -E '^(MonitorAddr|MinTlsVersion)' /etc/bfe/bfe.confConditionPathExists=/var/lib/sac/data/bfe-ready User=bfe ExecStartPre=+/usr/local/sbin/bfe-preflight /etc/bfe ExecStartPre=/usr/local/bin/bfe -t -c /etc/bfe -l /var/log/bfe AmbientCapabilities=CAP_NET_BIND_SERVICE 640 root:bfe /etc/bfe/tls_conf/certs/sac.key 640 root:bfe /etc/bfe/tls_conf/session_ticket_key.data 600 root:root /root/bfe-endpoints.txt MonitorAddr = 127.0.0.1 MinTlsVersion = VersionTLS12Security recommendations
- Keep SSH open only to your management addresses. Open 80 and 443 to the clients BFE serves.
- Put your backend servers where only this instance can reach them, so clients cannot bypass BFE.
- Use your own DNS name and a CA certificate (Step 7) for a public service.
- Keep the monitor API on
127.0.0.1. It has no authentication and can reload the configuration. - Leave unattended security upgrades on. BFE is a single binary in /usr/local/bin/bfe. SecureAnyCloud ships updated versions as new versions of this listing.
Support and licensing
BFE is open-source software by the BFE Authors, distributed under the Apache License 2.0. The license, the NOTICE file and SecureAnyCloud's build record (source, toolchain, the dependency changes and their checksums, plus a source offer) are in /usr/share/doc/bfe/.
SecureAnyCloud provides the packaging, the patched build, the secure defaults, the per-instance certificate and session-ticket key, the metadata block, the fail-closed start, the self-test and helpers, and 24/7 support.
SecureAnyCloud is not affiliated with or endorsed by the BFE project, the CNCF or Baidu. BFE is used here only to identify the software. nginx is a trademark of F5, Inc.
For help with deployment or configuration, email sales@secureanycloud.com or visit SecureAnyCloud support.
Deploy on AWS
Launch BFE 1.8.8.6 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com