Liftbridge 26.01.1
Overview
Liftbridge is an open source (Apache-2.0) server for durable message streams, written in Go. A stream is an append-only log attached to a NATS subject. Clients publish messages to it, and every message gets an offset (0, 1, 2 and so on). Clients subscribe from any point in the log: the earliest message, a given offset, the latest, or only new messages.
Messages stay on disk until the stream's retention removes them, so a consumer that was offline catches up from where it left off. Clients talk to Liftbridge's gRPC API with a Liftbridge client library (go-liftbridge, and community clients for Python, Java and Node.js) or with the lift-cli tool on the image.
This guide covers launching and using the Liftbridge 26.01.1 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud streaming and messaging listing on AWS Marketplace. You pick it as the software version when you subscribe.
What you get:
- gRPC with mutual TLS only, on 9292. Clients reach Liftbridge on 9292 over TLS 1.3 or 1.2, and every client must present a client certificate issued by this instance's own certificate authority. The server refuses a client that has no certificate, a certificate from another CA or a self-signed certificate. It also refuses clear text (h2c) and TLS 1.0 or 1.1 (Step 9). Nothing else answers on the network (Step 10).
- A certificate authority made on your instance. At first boot, the instance creates its own CA, a server certificate for its EC2 names and addresses, and one client certificate,
liftbridge-client-1. Nothing comes from the image, so no two instances share a key.liftbridge-client-certissues more client certificates and can replace the CA to revoke them all (Step 8). - A private embedded NATS server. Liftbridge runs its own NATS server, which it uses internally. Here it listens on the loopback address
127.0.0.1:4222only and requires a login generated at first boot. Only theliftbridgeservice user and root may connect to it. It has no monitoring, cluster, gateway, leaf-node, WebSocket or MQTT port (Step 10). - A server that refuses to run unsafely. Before every start, a configuration gate refuses an unsafe change: clear-text gRPC, no client certificates, telemetry on, NATS on a network address or without its login, a NATS monitoring or cluster port, a key that does not match its certificate, or a configuration readable by others. After every start, a guard checks the running service and stops it if anything is wrong. If first boot cannot create the CA, the certificates or the NATS login, Liftbridge never starts (Step 12).
- No telemetry, no instance metadata, no outbound network. Liftbridge's telemetry, which is on by default upstream, is off. The service runs with the EC2 instance metadata address blocked. A firewall table lets the
liftbridgeuser answer its clients and reach its own NATS server on127.0.0.1:4222, and nothing else, in IPv4 and IPv6 (Step 13). - A SecureAnyCloud build from the v26.01.1 tag on a current Go. The upstream 26.01.1 release binary was built with Go 1.25.6 and modules that have known vulnerabilities. SecureAnyCloud builds liftbridge from the unmodified v26.01.1 tag source with Go 1.27.3, raising four modules to fixed versions. The image records how you can rebuild it byte for byte (Step 15). liftbridge --version reports 26.01.1. Ubuntu 24.04 LTS is fully patched at build time.
Two things to know before you start. First, Liftbridge has no per-stream access rules. Any client with a valid certificate may create, publish to, read and delete every stream. Give each application its own certificate so you know who connects, and use a separate instance for parties that must not see each other's streams.
Second, Liftbridge has no certificate revocation list. To cut off one lost client key, you replace the CA, which stops every client certificate at once (Step 8).
What is on the image: Liftbridge 26.01.1 with its embedded NATS server (/usr/local/bin/liftbridge; configuration /etc/liftbridge/liftbridge.yaml and /etc/liftbridge/nats.conf; certificates in /etc/liftbridge/tls/; data in /var/lib/liftbridge), the lift-cli client (/usr/local/bin/lift-cli), and the SecureAnyCloud helpers liftbridge-selfcheck, liftbridge-client-cert, liftbridge-set-hostname, liftbridge-install-cert, liftbridge-config-gate, liftbridge-guard and liftbridge-probe.
Default user: ubuntu. Architecture: x86_64. Recommended instance type: m5.large.
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
- A security group that allows TCP port 22 from your admin network (SSH) and TCP port 9292 from the networks your Liftbridge clients connect from (Liftbridge gRPC with mutual TLS).
Nothing else needs to be open. NATS (4222), NATS monitoring (8222) and NATS clustering (6222) are not served to the network, so opening them achieves nothing.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Liftbridge 26.01.1 on Ubuntu 24.04 as the software version, select your Region, and choose Launch through EC2.
Choose m5.large, or larger for high message rates, along with your key pair and a security group as described above. Streams are stored on the root volume, so give it room for the data you keep (Step 11).
To launch with the AWS CLI instead, replace the values in angle brackets:
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=50,VolumeType=gp3}' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=liftbridge}]'The image requires IMDSv2 (session tokens). First boot takes under a minute after the instance is running. A boot service reads the instance's EC2 names, and the first-boot service creates the CA, the certificates and the NATS login. Then Liftbridge starts and the guard checks it.
Connect to your instance
Connect over SSH with your key pair. For Liftbridge 26.01.1 on Ubuntu 24.04, the SSH user is ubuntu.
ssh -i <your-key.pem> ubuntu@<public-ip>The login banner shows where the endpoint and the certificates are:
Liftbridge 26.01.1 on Ubuntu 24.04 (sac) Endpoint, NATS login, fingerprints:
sudo cat /root/liftbridge-credentials.txt Clients: gRPC with mutual TLS on <this host>:9292; CA + client certificate in /root/liftbridge-client/ Server: systemctl status liftbridge Verify:
sudo liftbridge-selfcheck Certs:
sudo liftbridge-client-cert list User guide: https://secureanycloud.com/stacks/liftbridge-26-01-1/Check the services and the listening set
systemctl is-active liftbridge-metadata-names liftbridge-firstboot liftbridge-egress-guard liftbridge
sudo ss -tlnp | grep -E ':(22|4222|6222|8222|9292) '
sudo ss -ulnp | grep -c liftbridge || trueactive active active active LISTEN 0 4096 127.0.0.1:4222 0.0.0.0:* users:(("liftbridge",pid=17412,fd=3)) LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=822,fd=3),("systemd",pid=1,fd=177)) LISTEN 0 4096 [::]:22 [::]:* users:(("sshd",pid=822,fd=4),("systemd",pid=1,fd=181)) LISTEN 0 4096 *:9292 *:* users:(("liftbridge",pid=17412,fd=16)) 0Each service has one job. liftbridge-metadata-names reads this instance's EC2 names for the server certificate. liftbridge-firstboot prepares the CA, the certificates and the configuration. liftbridge-egress-guard loads the firewall table. liftbridge is the server.
Liftbridge listens on 9292 on every address (IPv4 and IPv6) for clients. Its embedded NATS server listens on 127.0.0.1:4222 only. There is no UDP socket.
Your endpoint and the credentials files
First boot writes everything a client needs to two places, both readable by root only:
/root/liftbridge-credentials.txt(0600): the endpoint, the certificate fingerprints, the paths of the client files and the NATS login./root/liftbridge-client/(0700):ca.crt(this instance's CA certificate),liftbridge-client-1.crtandliftbridge-client-1.key(the first client certificate and its key), and any client certificate you add later.
Show the endpoint and the fingerprints. This leaves out the NATS password, which no client needs:
sudo grep -E '^LIFTBRIDGE_(HOST|ADDR|PORT|PRIVATE_IP|TLS_CA_FILE|TLS_CA_SHA256|CLIENT_CERT_FILE|CLIENT_KEY_FILE|SERVER_CERT_SHA256)=' /root/liftbridge-credentials.txtLIFTBRIDGE_HOST=ec2-3-90-50-227.compute-1.amazonaws.com LIFTBRIDGE_ADDR=ec2-3-90-50-227.compute-1.amazonaws.com:9292 LIFTBRIDGE_PORT=9292 LIFTBRIDGE_PRIVATE_IP=172.31.95.40 LIFTBRIDGE_TLS_CA_FILE=/root/liftbridge-client/ca.crt LIFTBRIDGE_TLS_CA_SHA256=<the CA certificate's SHA-256 fingerprint> LIFTBRIDGE_CLIENT_CERT_FILE=/root/liftbridge-client/liftbridge-client-1.crt LIFTBRIDGE_CLIENT_KEY_FILE=/root/liftbridge-client/liftbridge-client-1.key LIFTBRIDGE_SERVER_CERT_SHA256=<the server certificate's SHA-256 fingerprint>LIFTBRIDGE_ADDR is the address Liftbridge advertises. Liftbridge clients connect to the address you give them, ask the server for its metadata, then connect to the advertised address. It must therefore be a name your clients can reach.
It is this instance's EC2 public DNS name. Without one, it is the public IPv4, private DNS name or private IPv4. Inside the VPC, the public DNS name resolves to the private address. To advertise your own DNS name, see Step 14.
The server certificate covers all of these names and addresses plus 127.0.0.1 and localhost, and this instance's CA issues it. Clients pin ca.crt for this server only. Give it to the client as its CA file (-ca in lift-cli, RootCAs in Go). Never add it to a system or browser trust store.
Test on the instance
liftbridge-selfcheck runs the guard and a full round trip over mutual TLS with liftbridge-client-1. It creates a stream, publishes 3 messages, reads them back from offset 0 and deletes the stream.
sudo liftbridge-selfcheckliftbridge-guard: OK (check): listeners tcp 0.0.0.0:9292 and 127.0.0.1:4222 only; 9292 refuses a client without a certificate and clear-text h2c; NATS refuses a client without the login; uid liftbridge egress replies and 127.0.0.1:4222 only liftbridge-selfcheck: round trip over mutual TLS to ec2-3-90-50-227.compute-1.amazonaws.com:9292: stream created, 3 messages published and read back from offset 0, stream deleted SELFCHECK OKYou can run the same test by hand with lift-cli. Run it as root, because the client key is root-only:
C=/root/liftbridge-client;
ADDR=$(sudo sed -n 's/^LIFTBRIDGE_ADDR=//p' /root/liftbridge-credentials.txt) LC=(sudo lift-cli); TLS=(-addr "$ADDR" -ca "$C/ca.crt" -cert "$C/liftbridge-client-1.crt" -key "$C/liftbridge-client-1.key") "${LC[@]}" create "${TLS[@]}" -stream orders -subject orders.events for i in 1 2 3; do "${LC[@]}" publish "${TLS[@]}" -stream orders -message "order $i placed"; done "${LC[@]}" subscribe "${TLS[@]}" -stream orders -count 3 -start earliest "${LC[@]}" delete "${TLS[@]}" -stream ordersOK created stream "orders" (subject "orders.events") OK published to "orders" offset=0 OK published to "orders" offset=1 OK published to "orders" offset=2 offset=0 value=order 1 placed offset=1 value=order 2 placed offset=2 value=order 3 placed OK deleted stream "orders"Copy the client certificate, key and CA to a client
A client needs three files: ca.crt, a client certificate and its key. On the instance, put copies in a private archive that you can fetch with scp:
sudo tar -C /root/liftbridge-client -czf /home/ubuntu/liftbridge-client-1.tgz ca.crt liftbridge-client-1.crt liftbridge-client-1.key
sudo chown ubuntu:ubuntu /home/ubuntu/liftbridge-client-1.tgz && chmod 0600 /home/ubuntu/liftbridge-client-1.tgz ls -l /home/ubuntu/liftbridge-client-1.tgzOn the client machine, fetch the archive and unpack it into a private directory. If the client is a Linux x86_64 machine, copy lift-cli too. It is a static binary with no dependencies:
mkdir -m 0700 -p ~/liftbridge && cd ~/liftbridge scp -i <your-key.pem> ubuntu@<public-ip>:liftbridge-client-1.tgz . && tar -xzf liftbridge-client-1.tgz && rm liftbridge-client-1.tgz chmod 0600 liftbridge-client-1.key scp -i <your-key.pem> ubuntu@<public-ip>:/usr/local/bin/lift-cli . && chmod 0755 lift-cliThen remove the archive from the instance. The originals stay in /root/liftbridge-client/:
shred -u /home/ubuntu/liftbridge-client-1.tgz && echo removedTreat the .key file like a password. Anyone who has it and can reach port 9292 can read and write every stream.
Connect a client from another machine
Allow 9292 from the client's network in the security group. Then, in ~/liftbridge on the client, run the following with LIFTBRIDGE_ADDR from Step 4:
TLS="-addr <liftbridge-addr> -ca ca.crt -cert liftbridge-client-1.crt -key liftbridge-client-1.key" ./lift-cli metadata $TLS ./lift-cli create $TLS -stream orders -subject orders.events for i in 1 2 3 4 5; do ./lift-cli publish $TLS -stream orders -message "order $i placed"; done ./lift-cli subscribe $TLS -stream orders -count 5 -start earliestOK connected — 1 broker(s) known OK created stream "orders" (subject "orders.events") OK published to "orders" offset=0 ... offset=0 value=order 1 placed offset=1 value=order 2 placed offset=2 value=order 3 placed offset=3 value=order 4 placed offset=4 value=order 5 placedThe messages are durable. If you restart Liftbridge (sudo systemctl restart liftbridge) or reboot the instance, subscribe -start earliest reads the same offsets again.
In your own Go code, go-liftbridge v2 takes the same three files. The following complete program creates a stream with a 24-hour retention, publishes a message and reads the stream from offset 0:
mkdir liftbridge-demo && cd liftbridge-demo && go mod init example.com/liftbridge-demo go get github.com/liftbridge-io/go-liftbridge/v2@v2.4.0 cat > main.go <<'EOF' package main import ( "context" "crypto/tls" "crypto/x509" "fmt" "os" "time" lift "github.com/liftbridge-io/go-liftbridge/v2" ) func main() { addr, dir := os.Args[1], os.Args[2] // <liftbridge-addr> and the directory with the three files cert, err := tls.LoadX509KeyPair(dir+"/liftbridge-client-1.crt", dir+"/liftbridge-client-1.key") if err != nil { panic(err) } caPEM, err := os.ReadFile(dir + "/ca.crt") if err != nil { panic(err) } pool := x509.NewCertPool() pool.AppendCertsFromPEM(caPEM) // trust this instance's CA only, for this connection only client, err := lift.Connect([]string{addr}, lift.TLSConfig(&tls.Config{ Certificates: []tls.Certificate{cert}, RootCAs: pool, MinVersion: tls.VersionTLS12})) if err != nil { panic(err) } defer client.Close() ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() err = client.CreateStream(ctx, "events", "events", lift.RetentionMaxAge(24*time.Hour)) if err != nil && err != lift.ErrStreamExists { panic(err) } if _, err := client.Publish(ctx, "events", []byte("hello from go-liftbridge")); err != nil { panic(err) } done := make(chan struct{}) err = client.Subscribe(ctx, "events", func(msg *lift.Message, err error) { if err == nil { fmt.Printf("offset=%d value=%s\n", msg.Offset(), msg.Value()) close(done) } }, lift.StartAtEarliestReceived()) if err != nil { panic(err) } <-done } EOF go run . <liftbridge-addr> ~/liftbridgeOther client libraries take the same three files as their CA, certificate and key options.
Issue more client certificates, and revoke them
Give each application its own client certificate:
sudo liftbridge-client-cert add app-orders
sudo liftbridge-client-cert list
sudo ls /root/liftbridge-client/liftbridge-client-cert: issued app-orders: /root/liftbridge-client/app-orders.crt and /root/liftbridge-client/app-orders.key (copy them and /root/liftbridge-client/ca.crt to the client) NAME SERIAL EXPIRES liftbridge-client-1 8AF3DD0549C7ACF0C4879F193C798E58 Feb 10 16:28:31 2029 GMT app-orders B103BBEF8EC8056570E4822FD4023303 Feb 10 16:40:04 2029 GMT app-orders.crt app-orders.key ca.crt liftbridge-client-1.crt liftbridge-client-1.keyA name is 1 to 63 lowercase letters, digits or -. Each certificate is an EC P-256 key with a certificate valid for 825 days. It may only be used by a client, so it cannot act as a server or sign other certificates. Copy <name>.crt, <name>.key and ca.crt to that client as in Step 6.
Why the CA key stays on the instance. liftbridge-client-cert needs the CA key to issue certificates. The boot service needs it to re-issue the server certificate when the instance's names change, because an EC2 stop and start gives the instance a new public name.
The key is kept in /etc/sac/liftbridge/ (root only, 0700, the key file 0600). It never leaves the instance and is never readable by the liftbridge user. Anyone who could read it is already root on this server.
Revoking. Liftbridge does not check a revocation list, so a certificate stays valid until it expires. To cut off a lost or leaked client key, replace the CA:
sudo liftbridge-client-cert new-caThis command makes a new CA, a new server certificate (same server key) and a new liftbridge-client-1, and restarts Liftbridge. Every earlier client certificate stops working at once. Copy the new ca.crt and a new client certificate to every client you keep, and add the others again with add.
Mutual TLS is enforced
With a client certificate, the TLS handshake completes on TLS 1.3. Without one, the server ends the handshake. TLS 1.0 and 1.1 are refused:
C=/root/liftbridge-client;
H=$(sudo sed -n 's/^LIFTBRIDGE_HOST=//p' /root/liftbridge-credentials.txt)
sudo openssl s_client -connect "$H:9292" -servername "$H" -CAfile $C/ca.crt -cert $C/liftbridge-client-1.crt -key $C/liftbridge-client-1.key -brief </dev/null 2>&1 | grep -E 'Protocol version|Verification' || true
sudo openssl s_client -connect "$H:9292" -servername "$H" -CAfile $C/ca.crt -brief </dev/null 2>&1 | grep -o 'alert certificate required' | head -1 || true
sudo openssl s_client -connect "$H:9292" -servername "$H" -CAfile $C/ca.crt -cert $C/liftbridge-client-1.crt -key $C/liftbridge-client-1.key -tls1_1 -cipher 'DEFAULT@SECLEVEL=0' -brief </dev/null 2>&1 | grep -o 'alert protocol version' | head -1 || true liftbridge-probe h2c --host "$H" --port 9292Protocol version: TLSv1.3 Verification: OK alert certificate required alert protocol version NOT-H2C eofNOT-H2C means a clear-text gRPC (HTTP/2) client got no answer. A client certificate issued by another instance's CA, or a self-signed one, is refused in the same way as no certificate.
Why NATS 4222 is loopback-only and authenticated
Liftbridge stores its streams through its embedded NATS server. Every message published to a stream's subject passes through NATS, and Liftbridge itself is a NATS client.
Upstream's default lets NATS listen without a login, so any process that can reach 4222 could read and write the messages behind every stream and bypass Liftbridge's mutual TLS. On this image:
- NATS listens on
127.0.0.1:4222only, so nothing outside the instance can reach it, whatever the security group says. - It requires the login
liftbridgewith a password generated at first boot (LIFTBRIDGE_NATS_*in the credentials file, and in the root-only Liftbridge configuration). - A firewall rule refuses connections to 4222 from every local user except the
liftbridgeservice user and root, so another account on the instance cannot even try the password. - It has no monitoring (8222), cluster (6222), gateway, leaf-node, WebSocket or MQTT port, and no JetStream.
IP=$(sudo sed -n 's/^LIFTBRIDGE_PRIVATE_IP=//p' /root/liftbridge-credentials.txt) for p in 4222 6222 8222 9292; do nc -z -w 3 "$IP" "$p" 2>/dev/null && echo "$IP:$p open" || echo "$IP:$p closed"; done nc -z -w 3 127.0.0.1 4222 && echo "ubuntu -> 127.0.0.1:4222 open" || echo "ubuntu -> 127.0.0.1:4222 refused"
sudo liftbridge-probe nats --host 127.0.0.1 --port 4222172.31.95.40:4222 closed 172.31.95.40:6222 closed 172.31.95.40:8222 closed 172.31.95.40:9292 open ubuntu -> 127.0.0.1:4222 refused NATS-REFUSED Authorization Violation auth_required=TrueThe last line shows root connecting without the login. NATS refuses it.
The data directory and retention
Streams are stored in /var/lib/liftbridge (owned by the liftbridge user, mode 0700), on the root volume. It is the only directory the service may write.
Liftbridge's defaults apply to every stream, unless the client sets its own values when it creates the stream. Each setting is shown with its default on this image and the per-stream option in go-liftbridge:
Retention by age: 7 days (lift.RetentionMaxAge(d))
Retention by size: none, no limit (lift.RetentionMaxBytes(n))
Retention by message count: none, no limit (lift.RetentionMaxMessages(n))
Segment size: 256 MiB (lift.SegmentMaxBytes(n))
Retention removes whole closed segments, so a stream can briefly hold a little more than its limit. The configuration gate accepts only the shipped server configuration, so these server-wide defaults are fixed. Set the retention you need on each stream when you create it (Step 7 shows RetentionMaxAge). A stream deleted with lift-cli delete (or DeleteStream) frees its space at once.
Watch the space with df -h /var/lib/liftbridge. To make room, grow the EBS root volume or delete or shorten streams. To grow the volume, modify the volume in EC2, then run sudo growpart /dev/nvme0n1 1 && sudo resize2fs /dev/root. For a backup, take an EBS snapshot of the root volume.
The server refuses to run unsafely
- Before every start,
liftbridge-config-gate(run by systemd as root before Liftbridge) compares the configuration with the shipped template. It refuses clear-text gRPC, a configuration without client certificates, telemetry on, NATS on a network address or without its login, a NATS monitoring or cluster port, a server key that does not match its certificate, a server certificate from another CA, no client CA, a configuration readable by others, and a data directory others can open. A refused start leaves nothing listening. - After every start,
liftbridge-guardchecks the listening set (9292 and127.0.0.1:4222only). It checks that 9292 refuses a client without a certificate and clear text, that NATS refuses a client without the login, and that the firewall table is loaded. If anything is wrong, it stops Liftbridge. - At first boot, if the CA, the certificates or the NATS login cannot be created, the first-boot marker is not written and Liftbridge does not start.
- Liftbridge requires
liftbridge-egress-guard.service. If the firewall table cannot be loaded, Liftbridge does not start.
sudo liftbridge-config-gate systemctl show -p Requires --value liftbridge | tr ' ' '\n' | grep egressliftbridge-config-gate: OK (liftbridge.service) liftbridge-egress-guard.serviceLiftbridge runs as the unprivileged liftbridge user in a systemd sandbox with no capabilities. The sandbox has a read-only system except /var/lib/liftbridge, a system-call filter, no writable and executable memory, and core dumps off.
The instance metadata block and outbound traffic
The EC2 instance metadata service (169.254.169.254) hands out the instance's IAM role credentials to any process that asks, so a network service must not be able to reach it. liftbridge and liftbridge-firstboot run with the metadata addresses blocked by systemd (IPAddressDeny).
Only liftbridge-metadata-names, which reads the instance's names for the server certificate, may reach the metadata service, and it may reach nothing else:
for u in liftbridge liftbridge-firstboot liftbridge-metadata-names; do printf '%-26s deny=%s allow=%s\n' "$u" "$(systemctl show -p IPAddressDeny --value "$u")" "$(systemctl show -p IPAddressAllow --value "$u")"; doneliftbridge deny=::ffff:169.254.169.254/128 ::ffff:168.63.129.16/128 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 allow= liftbridge-firstboot deny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 allow= liftbridge-metadata-names deny=0.0.0.0/0 ::/0 allow=fd00:ec2::254/128 169.254.169.254/32On top of that, the nftables table inet sac_liftbridge_egress denies all outbound traffic, IPv4 and IPv6, for the liftbridge user. That user may answer its clients and connect to its own NATS server on 127.0.0.1:4222, and nothing else. It cannot reach the metadata service, the internet, your VPC, DNS or even its own port 9292. Other users are unaffected:
IP=$(sudo sed -n 's/^LIFTBRIDGE_PRIVATE_IP=//p' /root/liftbridge-credentials.txt) for t in http://169.254.169.254/latest/meta-data/ https://1.1.1.1/ "http://$IP:22/"; do
sudo -u liftbridge curl -s -m 5 -o /dev/null -w "liftbridge -> $t: %{http_code} %{errormsg}\n" "$t" || true; done curl -s -m 5 -o /dev/null -w "ubuntu -> https://1.1.1.1/: %{http_code}\n" https://1.1.1.1/liftbridge -> http://169.254.169.254/latest/meta-data/: 000 Failed to connect to 169.254.169.254 port 80 after 0 ms: Couldn't connect to server liftbridge -> https://1.1.1.1/: 000 Failed to connect to 1.1.1.1 port 443 after 0 ms: Couldn't connect to server liftbridge -> http://172.31.95.40:22/: 000 Failed to connect to 172.31.95.40 port 22 after 0 ms: Couldn't connect to server ubuntu -> https://1.1.1.1/: 301liftbridge-egress-guard.service loads the table before Liftbridge starts. Liftbridge needs no outbound connection, because a single node has no peers and telemetry is off.
Use your own DNS name or a certificate from your CA
Point a DNS name at the instance. An Elastic IP keeps the address across stop and start. Then make Liftbridge advertise the name:
sudo liftbridge-set-hostname <dns-name>This command re-issues the server certificate (same key, from this instance's CA) to cover the name. It updates LIFTBRIDGE_HOST and LIFTBRIDGE_ADDR in /root/liftbridge-credentials.txt and restarts Liftbridge. Clients keep their ca.crt and certificates.
To serve a server certificate from your own CA or a public one instead, copy these files to the instance: the chain (server certificate first, then any intermediates), its unencrypted PEM key, and the root CA certificate your clients will trust. Then run:
sudo liftbridge-install-cert <certificate-chain.pem> <private-key.pem> <server-ca.pem> <dns-name>Port 9292 then serves your certificate. <server-ca.pem> is copied to /root/liftbridge-client/server-ca.crt and named in LIFTBRIDGE_TLS_CA_FILE. Clients verify the server with it instead of ca.crt.
Client certificates are unchanged. Liftbridge still requires one issued by this instance's CA. The boot-time renewal never replaces an installed certificate. To go back to a new key and certificate from this instance's CA, run sudo liftbridge-set-hostname <name>.
How this build was made, and how to verify it
The upstream Liftbridge 26.01.1 release binary was built with Go 1.25.6, which is out of support. It also uses module versions that have known vulnerabilities reachable from Liftbridge's code. SecureAnyCloud therefore builds Liftbridge from the upstream source instead:
- Source: the v26.01.1 tag, commit
50ac358b41eba51e695105436589958f8c4794d8, fromhttps://github.com/liftbridge-io/liftbridge/archive/refs/tags/v26.01.1.tar.gz(SHA-256c6cf76fb396c445333f41c3780f1f288040ed9431065c9215d815ffa651d47e0). No source file is changed. - Go: go1.27.3 (
go1.27.3.linux-amd64.tar.gz, SHA-25665efbc80fdc1fa2cd2631cad5b481777a68e8725f34e89f50fc6ac714e379b40), because Go 1.25 no longer gets security fixes. - Module raises:
github.com/nats-io/nats-server/v2v2.12.15,google.golang.org/grpcv1.85.0,golang.org/x/netv0.62.0 andgolang.org/x/cryptov0.58.0, thengo mod tidy, which changes onlygo.modandgo.sum(SHA-2561c53c5296f4fe63f108a674485c23ea2ec36e678c99f76d12c2a7be813809d64andbc958bfa29d6f3c9b40aaa20283161d120c286a5fe75a1d5a299be3b9b283cce). Both files, the originals and the diffs are in/usr/share/doc/sac-liftbridge/build/. - Build command, run in the unpacked source with the raised
go.modandgo.sum:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v1 GOTOOLCHAIN=local GOFLAGS=-modcacherw \
go build -mod=readonly -trimpath -buildvcs=false \
-ldflags='-buildid= -X github.com/liftbridge-io/liftbridge/server.Version=26.01.1' -o liftbridge .- Result: /usr/local/bin/liftbridge, SHA-256 c1003adf40c2976454b7290629bd4ffca19cd9142d6f7658b810efa2e189eddb. The build is reproducible, so the same source, Go and module files give the same bytes. lift-cli is a small SecureAnyCloud client on go-liftbridge v2.4.0 with the same raises, SHA-256 418e6f9891d1dbf0d0c3ea045f254bc7c9b306aeded5a8350f1829754b964256.
- Checks at build time:
govulncheckfinds no reachable vulnerability in either program, in source and in binary mode, and Liftbridge's own short tests pass on the raised modules. No Go toolchain is left on the image.
Check it on your instance:
liftbridge --version sha256sum /usr/local/bin/liftbridge /usr/local/bin/lift-cli grep -E '^(build|source|toolchain|raises) ' /usr/share/doc/sac-liftbridge/PROVENANCE | cut -c1-160liftbridge version 26.01.1 c1003adf40c2976454b7290629bd4ffca19cd9142d6f7658b810efa2e189eddb /usr/local/bin/liftbridge 418e6f9891d1dbf0d0c3ea045f254bc7c9b306aeded5a8350f1829754b964256 /usr/local/bin/lift-cli build sac build from source (coordinator ruling d-4065), not the upstream release binary source https://github.com/liftbridge-io/liftbridge/archive/refs/tags/v26.01.1.tar.gz sha256 c6cf76fb396c445333f41c3780f1f288040ed9431065c9215d815ffa651d47e0 (tag v26.01.1 toolchain go1.27.3 https://go.dev/dl/go1.27.3.linux-amd64.tar.gz sha256 65efbc80fdc1fa2cd2631cad5b481777a68e8725f34e89f50fc6ac714e379b40 (CGO_ENABLED=0 -trimpath -buildvc raises github.com/nats-io/nats-server/v2@v2.12.15 google.golang.org/grpc@v1.85.0 golang.org/x/net@v0.62.0 golang.org/x/crypto@v0.58.0 + go mod tidy (no source patch); go.mThe license texts of every module linked into the two programs are in /usr/share/doc/sac-liftbridge/THIRD_PARTY_NOTICES.
Logs, files and limits
- Server log:
journalctl -u liftbridgeshows start-up and shutdown and the gate's and the guard's results, and never a key or password. For first boot, usejournalctl -u liftbridge-firstboot. - Configuration:
/etc/liftbridge/liftbridge.yamland/etc/liftbridge/nats.conf(root:liftbridge 0640, rendered at first boot and checked by the gate on every start). Server key and certificates:/etc/liftbridge/tls/. CA:/etc/sac/liftbridge/(root only). - A message is at most about 1 MiB (the embedded NATS server's payload limit), and NATS accepts at most 1,024 connections.
- This image runs a single Liftbridge node, so each stream has one replica. Protect the data with EBS snapshots. Clustering is not configured.
Support
SecureAnyCloud provides support for the image, covering launch problems, the first-boot setup, the certificates, the build and the SecureAnyCloud helpers. Contact sales@secureanycloud.com.
Liftbridge itself is documented at liftbridge.io and github.com/liftbridge-io/liftbridge.
Deploy on AWS
Launch Liftbridge 26.01.1 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