Installing SSLB3

Three ways in: Helm on Kubernetes, plain manifests on Kubernetes, or a systemd unit on a Linux host. The first is the shortest and the last needs no Kubernetes at all.

Kubernetes, with Helm

The chart is published as an OCI artifact, so there is no repository to add:

shell

helm install sslb3 \
  oci://docker.hacking.hu/public/sslb3-ingress \
  --namespace sslb3 --create-namespace

That gives you the balancer, the controller as a sidecar, an IngressClass named sslb3, the L4Ingress CRD, RBAC, and a Service in front. It generates its own self-signed certificates for the loopback admin API and for the data plane fallback, so nothing has to exist first.

The values worth setting

ValueDefaultWhy

config.stats.enabled

false

The statistics page. Off because it is unauthenticated and describes the deployment.

config.prometheus.enabled

false

The metrics endpoint, off on the same terms.

service.type

LoadBalancer

ClusterIP where something else advertises the address, such as kube-router or MetalLB.

service.clusterIP

(unset)

Pin it where the ClusterIP is what gets advertised — recreating the Service otherwise releases the address.

defaultCertificate.secretName

(unset)

The certificate served when no Ingress matches. Unset means a self-signed one, so a mismatched request gets an untrusted answer rather than a clear error.

config.fastpath.mode

auto

Hand eligible layer 4 services to the kernel. Set userspace to serve everything in SSLB3 and deploy no helper sidecar.

config.fastpath.privilegedSysctlInit

false

Runs a privileged init container to set the sysctls IPVS needs. Off because PodSecurity baseline and restricted reject privileged containers.

config.threat.mode

block

Intrusion prevention. Every threshold is still zero, so nothing is watched until you set one — start at detect against real traffic.

Kubernetes, with kubectl

The same deployment written out, for reading or adapting. Apply it server-side:

shell

git clone https://gitlab.hacking.hu/szabolcs/sslb3.git
cd sslb3
kubectl apply --server-side -k crates/sslb3-ingress-controller/kube

The manifests are numbered in apply order: namespace, RBAC, CRD, configuration, then the Deployment and Service. The ConfigMap is where the balancer's configuration lives, and it is worth reading — every setting carries the reasoning next to it.

A Linux host, with systemd

No Kubernetes involved. The engine is configured by a file and is complete on its own.

The unit files ship with it, in crates/sslb3/systemd/, so there is nothing to write. They are not skeletons — they carry the hardening, the reload wiring and the stop timeout already worked out, and each directory has a README explaining the three or four settings that are not obvious.

shell

cargo build --release -p sslb3

install -m 0755 target/release/sslb3 /usr/local/bin/sslb3
install -d -m 0755 /etc/sslb3 /etc/sslb3/scripts
install -m 0644 etc/sslb-sample.conf /etc/sslb3/sslb.conf
useradd --system --no-create-home --shell /usr/sbin/nologin sslb3

install -m 0644 crates/sslb3/systemd/sslb3.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now sslb3

systemctl reload sslb3 sends SIGHUP, which rereads the configuration and the certificates without dropping a listener. A renewed certificate needs nothing more.

What the unit already does

It runs SSLB3 confined, and the confinement is set up around how SSLB3 actually starts rather than copied from a template:

Bounded capabilities

Only the four it needs on the way to dropping them: binding a privileged port, changing user and group, and reading a certificate that is not world-readable. Nothing else, so a bug before the drop has little to work with.

A read-only system

ProtectSystem=strict, ProtectHome, a private /tmp, and named runtime and log directories. It can write where its socket, pid and logs go, and nowhere else.

No kernel surface

Kernel tunables, modules and logs are all out of reach, along with cgroups and the clock. The one component that does touch a kernel setting is the fast path helper, which is a separate unit for exactly that reason.

Only the address families it uses

AF_INET, AF_INET6 and AF_UNIX. AF_NETLINK is deliberately absent — SSLB3 does not speak netlink, which is the whole reason the helper exists as its own process.

Reload without dropping a connection

ExecReload sends SIGHUP, so systemctl reload rereads the configuration and the certificates in place. A renewed certificate needs nothing else.

A stop timeout that outlasts the drain

TimeoutStopSec is longer than the configured shutdown timeout. If they were equal, systemd’s SIGKILL would land exactly on SSLB3’s drain deadline and cut off a request that was about to finish.

A file descriptor limit that fits

One descriptor per connection, twice over for the backend side, plus certificates and logs. The default of 1024 is reached by a few hundred concurrent connections.

The fast path, optionally

A second unit ships alongside, for the privileged helper that programs nftables or IPVS, with a commented environment file to fill in. Neither half alone does anything, and neither can break a service — see the fast path.

shell

install -m 0755 target/release/sslb3-netlink-helper /usr/local/bin/
install -m 0640 crates/sslb3-netlink-helper/systemd/sslb3-netlink-helper.env \
  /etc/sslb3/netlink-helper.env
editor /etc/sslb3/netlink-helper.env    # SSLB3_HELPER_BIND is required

install -m 0644 crates/sslb3-netlink-helper/systemd/sslb3-netlink-helper.service \
  /etc/systemd/system/
systemctl enable --now sslb3-netlink-helper

Check what the host can actually drive before wiring it to traffic. It answers none honestly when the module is missing or the capability was never granted, rather than claiming a facility and failing at the first rule:

shell

SSLB3_HELPER_BIND=203.0.113.10 sslb3-netlink-helper --probe

Its unit is confined differently from the balancer's, and deliberately so: it holds CAP_NET_ADMIN and nothing else, is allowed only AF_NETLINK and AF_UNIX — it never touches the network, it only describes it to the kernel — and leaves ProtectKernelTunables off, because setting two sysctls for IPVS is the one thing it legitimately needs to do. The balancer's unit protects them; this one is the reason the balancer can.

Container images

Pulled anonymously, no credential needed:

text

docker.hacking.hu/public/sslb3
docker.hacking.hu/public/sslb3-ingress-controller
docker.hacking.hu/public/sslb3-netlink-helper

All linux/amd64 and distroless. Pin a version tag rather than tracking latest for anything you care about.

Checking it works

In order, because each one rules out a different thing:

shell

# The pods are up and the controller connected to the balancer
kubectl -n sslb3 get pods
kubectl -n sslb3 logs deploy/sslb3-ingress -c controller | tail

# Your Ingress was accepted
kubectl get ingress -A

# An L4Ingress carries its own verdict
kubectl get l4ingress -A

# And what it is actually doing, if the statistics page is on
kubectl -n sslb3 port-forward deploy/sslb3-ingress 9102:9102