Created
December 14, 2025 06:34
-
-
Save utam0k/249350cab6233352b146835745461f4b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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