This program spawns a child process and only allows it to bind to a specific IPv4/IPv6 port.
Linux kernel >= 5.6 (uses pidfd_getfd
)
Here's an example using netcat. If we tell port-restricter to only allow port 5000, and tell netcat to listen on port 5001, then netcat will get an EPERM error when it calls bind():
./port-restricter -p 5000 -- nc -l 0.0.0.0 5001
# nc: Operation not permitted
./port-restricter -p 5000 -- nc -l 0.0.0.0 5000
# Executes normally
Make sure to install the libseccomp development files (libseccomp-dev
on
Debian/Ubuntu, libseccomp-devel
on Fedora).
Then run make
.
This parent process uses the seccomp_unotify(2)
API (available since Linux 5.0) to get notified whenever the child process
calls bind
, then reads the child process' memory to determine which port was
requested. If the requested port is equal to the target port, then the parent
calls uses pidfd_getfd
to get a duplicate of the child's socket fd, calls
bind
with this new fd and the desired address + port, and returns 0 to the
child if everything succeeds.
sequenceDiagram
Parent->>Child: fork
Note over Child: load seccomp filter with<br>SECCOMP_RET_USER_NOTIF
Child->>Parent: send notify_fd over a pipe
Note over Parent: duplicate notify_fd via pidfd_getfd
Parent-->>Child: continue (write dummy over pipe)
Note over Child: execv<br>socket<br>bind
Child->>Parent: notification (SECCOMP_IOCTL_NOTIF_RECV)
Note over Parent: read /proc/child_pid/mem<br>check port<br>duplicate sockfd via pidfd_getfd<br>bind
Parent-->>Child: continue (SECCOMP_IOCTL_NOTIF_SEND)