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-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.