Skip to content

Instantly share code, notes, and snippets.

@utam0k
Created December 14, 2025 06:34
Show Gist options
  • Select an option

  • Save utam0k/249350cab6233352b146835745461f4b to your computer and use it in GitHub Desktop.

Select an option

Save utam0k/249350cab6233352b146835745461f4b to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"flag"
"fmt"
"math/bits"
"net"
"os"
"strconv"
"strings"
"syscall"
"unsafe"
)
var noSetEff = flag.Bool("no-set-eff", false, "do not set CAP_NET_BIND_SERVICE in effective set")
const (
port = 81
capNetBindService = 10 // CAP_NET_BIND_SERVICE
linuxCapVersion3 = 0x20080522 // _LINUX_CAPABILITY_VERSION_3
linuxCapU32s = 2 // _LINUX_CAPABILITY_U32S_3
)
type capUserHeader struct {
version uint32
pid int32
}
type capUserData struct {
effective uint32
permitted uint32
inheritable uint32
}
func capget(hdr *capUserHeader, data *[linuxCapU32s]capUserData) error {
_, _, errno := syscall.RawSyscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0)
if errno != 0 {
return errno
}
return nil
}
func capset(hdr *capUserHeader, data *[linuxCapU32s]capUserData) error {
_, _, errno := syscall.RawSyscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0)
if errno != 0 {
return errno
}
return nil
}
func setEffectiveCap(cap int) error {
hdr := capUserHeader{version: linuxCapVersion3, pid: 0}
var data [linuxCapU32s]capUserData
if err := capget(&hdr, &data); err != nil {
return fmt.Errorf("capget: %w", err)
}
index := cap / 32
bit := uint32(1) << uint32(cap%32)
data[index].effective |= bit
if err := capset(&hdr, &data); err != nil {
return fmt.Errorf("capset: %w", err)
}
return nil
}
type capsView struct {
noNewPrivs string
hex map[string]uint64
}
func readStatusCaps() (*capsView, error) {
f, err := os.Open("/proc/self/status")
if err != nil {
return nil, err
}
defer f.Close()
keys := map[string]bool{
"CapInh": true,
"CapPrm": true,
"CapEff": true,
"CapBnd": true,
"CapAmb": true,
"NoNewPrivs": true,
}
cv := &capsView{hex: make(map[string]uint64)}
sc := bufio.NewScanner(f)
for sc.Scan() {
line := sc.Text()
parts := strings.SplitN(line, ":", 2)
if len(parts) != 2 {
continue
}
k := strings.TrimSpace(parts[0])
if !keys[k] {
continue
}
v := strings.TrimSpace(parts[1])
if k == "NoNewPrivs" {
cv.noNewPrivs = v
continue
}
u, err := strconv.ParseUint(strings.Fields(v)[0], 16, 64)
if err != nil {
return nil, fmt.Errorf("failed to parse %s=%q as hex: %w", k, v, err)
}
cv.hex[k] = u
}
return cv, sc.Err()
}
func bitIsSet(mask uint64, bit int) bool {
return (mask & (1 << uint(bit))) != 0
}
func popCountUpTo(mask uint64, lastCap int) int {
if lastCap >= 63 {
return bits.OnesCount64(mask)
}
usable := mask & ((1 << uint(lastCap+1)) - 1)
return bits.OnesCount64(usable)
}
func yn(b bool) string {
if b {
return "yes"
}
return "no"
}
func tryBind(port int) {
addr := fmt.Sprintf("127.0.0.1:%d", port)
ln, err := net.Listen("tcp4", addr)
if err != nil {
if strings.Contains(err.Error(), "permission denied") {
fmt.Printf("bind(%d): FAIL permission denied\n", port)
} else {
fmt.Printf("bind(%d): FAIL %v\n", port, err)
}
return
}
_ = ln.Close()
fmt.Printf("bind(%d): OK\n", port)
}
func main() {
flag.Parse()
if !*noSetEff {
if err := setEffectiveCap(capNetBindService); err != nil {
fmt.Fprintf(os.Stderr, "failed to set effective CAP_NET_BIND_SERVICE: %v\n", err)
os.Exit(1)
}
}
cv, err := readStatusCaps()
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
if cv.noNewPrivs == "" {
cv.noNewPrivs = "?"
}
inh := cv.hex["CapInh"]
prm := cv.hex["CapPrm"]
eff := cv.hex["CapEff"]
bnd := cv.hex["CapBnd"]
amb := cv.hex["CapAmb"]
fmt.Printf("uid=%d euid=%d NoNewPrivs=%s\n", os.Getuid(), os.Geteuid(), cv.noNewPrivs)
fmt.Printf("CAP_NET_BIND_SERVICE: Eff=%s Prm=%s Bnd=%s Amb=%s Inh=%s\n",
yn(bitIsSet(eff, capNetBindService)),
yn(bitIsSet(prm, capNetBindService)),
yn(bitIsSet(bnd, capNetBindService)),
yn(bitIsSet(amb, capNetBindService)),
yn(bitIsSet(inh, capNetBindService)),
)
tryBind(port)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment