{"record":{"id":"e6c8a0a6c35c1f10","repo":"slackhq/nebula","slug":"failed-to-create-dev-net-tun-w","errorCode":null,"errorMessage":"failed to create /dev/net/tun: %w","messagePattern":"failed to create /dev/net/tun: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"overlay/tun_linux.go","lineNumber":97,"sourceCode":"\t// on IFF_VNET_HDR after TUNSETIFF, so skip offload on inherited fds.\n\treturn newTunGeneric(c, l, deviceFd, false, 0, vpnNetworks, \"tun0\")\n}\n\n// openTunDev opens /dev/net/tun, creating the device node first if it's\n// missing (docker containers occasionally omit it).\nfunc openTunDev() (int, error) {\n\tfd, err := unix.Open(\"/dev/net/tun\", os.O_RDWR, 0)\n\tif err == nil {\n\t\treturn fd, nil\n\t}\n\tif !os.IsNotExist(err) {\n\t\treturn -1, err\n\t}\n\tif err = os.MkdirAll(\"/dev/net\", 0755); err != nil {\n\t\treturn -1, fmt.Errorf(\"/dev/net/tun doesn't exist, failed to mkdir -p /dev/net: %w\", err)\n\t}\n\tif err = unix.Mknod(\"/dev/net/tun\", unix.S_IFCHR|0600, int(unix.Mkdev(10, 200))); err != nil {\n\t\treturn -1, fmt.Errorf(\"failed to create /dev/net/tun: %w\", err)\n\t}\n\tfd, err = unix.Open(\"/dev/net/tun\", os.O_RDWR, 0)\n\tif err != nil {\n\t\treturn -1, fmt.Errorf(\"created /dev/net/tun, but still failed: %w\", err)\n\t}\n\treturn fd, nil\n}\n\n// tunSetIff runs TUNSETIFF with the given flags and returns the kernel-chosen device name on success.\nfunc tunSetIff(fd int, name string, flags uint16) (string, error) {\n\tvar req ifReq\n\treq.Flags = flags\n\tcopy(req.Name[:], name)\n\tif err := ioctl(uintptr(fd), uintptr(unix.TUNSETIFF), uintptr(unsafe.Pointer(&req))); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn strings.Trim(string(req.Name[:]), \"\\x00\"), nil\n}","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/overlay/tun_linux.go#L79-L115","documentation":"openTunDev (overlay/tun_linux.go:97) falls back to creating the /dev/net/tun character device node (mknod, major 10 minor 200) when it is missing. This error wraps a mknod failure, meaning the TUN device node could not be provisioned.","triggerScenarios":"unix.Mknod(\"/dev/net/tun\", S_IFCHR|0600, Mkdev(10,200)) fails: EPERM without CAP_MKNOD (the norm in unprivileged containers), read-only /dev, or EEXIST if a race created it concurrently.","commonSituations":"Unprivileged containers that hide or lack /dev/net/tun and block mknod; rootless podman/Kubernetes without device plugins; hardened sandbox images with read-only /dev.","solutions":["Mount the host device into the container: --device /dev/net/tun (or a Kubernetes device plugin / hostPath volume).","Run with the capabilities needed to create device nodes (CAP_MKNOD) plus NET_ADMIN for TUNSETIFF.","Pre-create the node in the image build (RUN mknod /dev/net/tun c 10 200) where /dev is writable at build time.","If EEXIST due to a race, retry the open; the node already exists."],"exampleFix":"// before\n$ docker run --cap-add NET_ADMIN nebula-image   # still no CAP_MKNOD, /dev/net/tun missing\n// after\n$ docker run --cap-add NET_ADMIN --device /dev/net/tun nebula-image","handlingStrategy":"validation","validationCode":"if os.Geteuid() != 0 {\n\treturn errors.New(\"creating /dev/net/tun requires root + CAP_MKNOD\")\n}\nif _, err := os.Stat(\"/dev/net/tun\"); err == nil {\n\t// node exists; no mknod needed\n}","typeGuard":null,"tryCatchPattern":"fd, err := openTunDev()\nif err != nil && strings.Contains(err.Error(), \"failed to create /dev/net/tun\") {\n\tif errors.Is(errors.Unwrap(err), unix.EEXIST) {\n\t\t// raced with another creator; safe to retry open\n\t}\n}","preventionTips":["Prefer mounting the host device (--device /dev/net/tun) over mknod at runtime.","Grant CAP_MKNOD only if runtime node creation is truly required.","Avoid read-only /dev in images that rely on runtime mknod.","Handle EEXIST races by retrying the open instead of failing."],"tags":["linux","tun","mknod","container","permissions"],"backgroundTag":"missing-dev-net-tun","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}