Request for classic confinement: sni-router (io_uring L4 proxy)

name: sni-router

description: | sni-router is a high-performance SNI-based L4 (TCP/UDP/QUIC) routing proxy daemon for Linux. It routes client connections to backends by inspecting the TLS SNI (and the QUIC Initial ClientHello) without necessarily terminating the TLS handshake; an optional TLS-terminate mode is also supported. It is built on an io_uring-native, thread-per-core async runtime (monoio) with per-core CPU pinning and kernel zero-copy splice() on the data path. Maximum throughput is the project’s primary design goal.

snapcraft: sni-router/snap/snapcraft.yaml at main · Ashteeer/sni-router · GitHub

upstream: GitHub - Ashteeer/sni-router: High-performance SNI-based L4 router: TCP/UDP/QUIC passthrough without TLS termination · GitHub

upstream-relation: I am the sole author and maintainer of the upstream project (open source, Apache-2.0).

supported-category: | None of the entries in the “Supported” known-categories list apply — sni-router is a networking service daemon, not a compiler/IDE/language/ orchestration agent. Instead this request falls under the “Criteria” section: “access to resources not yet supported by snapd and where the requirement is clearly understood to be supportable by snapd. This may result in temporarily granting classic until snapd supports the use case in strict mode.” The resource is the io_uring syscall family (see reasoning). I am explicitly asking for temporary classic pending strict-mode io_uring support, and I am happy to move to strict as soon as that exists.

reasoning: | This is NOT “difficulty making strict confinement work” (a configuration problem). It is the absence of any strict interface exposing a kernel capability the daemon requires: the io_uring syscall family.

sni-router’s entire data path is io_uring-native via the monoio runtime, which calls io_uring_setup / io_uring_enter / io_uring_register. snapd’s default seccomp template does not permit these syscalls, and no existing interface (network, network-bind, process-control, system-observe, etc.) adds them. I verified this on the target host (snapd 2.76, Ubuntu 24.04): the generated strict seccomp profile of an installed strict snap contains epoll_create1 but none of io_uring_setup / io_uring_enter / io_uring_register. Under strict confinement the runtime can only fall back to an epoll driver, which negates the sole purpose of the project (an io_uring-native, zero-copy L4 proxy meant to outperform epoll-based alternatives under load).

The requirement is clearly supportable by snapd in strict mode — it is a bounded set of syscalls that could be added to the seccomp template (and/or exposed through a dedicated interface). I am glad to help drive that snapd feature request; until it lands, classic is the only way to run the snap at its intended performance. The daemon also uses sched_setaffinity for per-core CPU pinning, which similarly has no strict interface today.

File access is deliberately minimal and is NOT the reason for this request:

  • Its own config file, relocatable via $SNI_ROUTER_CONFIG / $SNAP_DATA.
  • In TLS-terminate mode, the TLS certificate/private-key files at paths the administrator configures (commonly under /etc/letsencrypt or /etc/sni-router). I believe this is fully addressable under strict via the system-files interface (and/or layouts) and I am happy to declare it. It needs no access to $HOME, dotfiles, or arbitrary host files; it does not run other snaps and does not use sudo/pkexec; and it ships all dependencies from source (no host-only libraries).

Full disclosure on self-update, since it is security-relevant and visible in the source: the standalone (tarball / install.sh) build has an optional self-update (sni-router -u, or an authenticated POST /update on the management API) that atomically replaces its own binary with the latest release. It downloads ONLY from the hardcoded official GitHub repository (Ashteeer/sni-router) release assets — never an arbitrary URL. This belongs to the non-snap deployment and is NOT a reason for classic. Inside a snap it is inert by design (the binary lives in the read-only squashfs, so the in-place replace cannot occur); snap installs update via snap refresh from the Store. I can compile this path out, or make it a no-op that points users to snap refresh, for the snap build if preferred.

I understand that strict confinement is generally preferred over classic, and that if snapd gains io_uring support in strict mode I should migrate to strict (declaring system-files for the certificate paths), optionally keeping a separate classicmode track for older snapd as described in the process notes.

I’ve tried the existing interfaces to make the snap work under strict confinement: network + network-bind restore socket access, and system-files would cover the certificate paths — but neither they nor any other interface unblock the io_uring syscalls (or sched_setaffinity) under the default seccomp template on snapd 2.76, so the io_uring data path cannot function under strict.

This request has been added to the queue for review by the @reviewers team.

Hello @Ashteeer!

While the technical reasons for the request for classic are sound and clear here, there is no interface support for io_uring, unfortunately I do not believe this snap meets our criteria for classic at this time. From what I can find, this is currently a very new project, and given the sensitivity of classic confinement, it is generally reserved for mature, well-known applications. This is a -1 for granting classic confinement to the sni-router snap at this time.

As you mention, the limiting factor leading you away from strict confinement here is the performance difference. I would like to encourage you to submit an auto-connection request for the necessary strict interfaces and publish the snap using the epoll fallback, as it would be a welcome addition to the snap store, even with the reduced performance.

@jslarraz, what are your thoughts about requesting a dedicated interface to support io_uring usage?

Hey folks!

AFAIK there is no good way to add support for io_uring in snaps as of now. This is because apparmor cannot properly mediate it currently, unless things have changed. Thus, I don’t think a new interface allowing io_uring will be a thing in the very short term.

1 Like