A browser using an established Linux VPN does not open /dev/net/tun. It opens a socket, and the kernel decides which interface that socket's packets leave by. The process that created the tunnel is doing a different job from the process whose traffic it carries. Keeping those two roles separate answers most permission questions before they are asked.

Follow one packet from socket to tunnel

An ordinary application calls connect or send, and the kernel looks up a route for the destination. If the selected route points at the tunnel interface, the packet is queued to the device the VPN daemon reads. The daemon encrypts it and sends it out through the physical interface. Each arrow in that description is a place where a permission question can be answered.

Nothing in that path requires the application to know about the device. The daemon holds the file descriptor and performs the interface operations, while the application only sees a normal network API.

Draw the two halves once, and permission questions become easier to place. The upper half is the application world, and the lower half belongs to the daemon.

Decide who needs the device and who does not

Creating or attaching to a TUN interface requires device access plus the capability to configure it, which is why a supported VPN service starts with network administration authority or uses a privileged helper. That requirement belongs to the daemon. It says nothing about the browser, the database client or the file-transfer tool. A service that needs the capability needs it for its own setup step, not for its clients.

Once the route and policy are in place, an ordinary process uses the resulting path through its usual socket calls. It does not need the device node, and it does not need root.

Changing device permissions is not a way to grant an application destination access. It only moves the device boundary somewhere else.

Check the namespace the application actually runs in

A container, or a second network namespace, gives its processes a separate interface set and route table. A tunnel on the host does not appear there automatically, and no amount of device permission changes that. The application will simply route through whatever that namespace provides. That is why a container image cannot inherit a host tunnel by accident.

Check with ip netns list and ip -n NAME route, or by inspecting the namespace link for the process. The answer is usually visible in one command.

Two users on the same host can also be given different paths by policy. That is a network decision rather than evidence about device ownership.

Separate capabilities from network policy

Some tools need more than a socket. Capturing packets needs a raw socket, and changing routes needs administration authority. Those are separate operations with separate grants, and granting one should not be read as a blanket requirement for using the VPN.

A program that implements its own tunnel is a third case. It genuinely opens TUN because it is taking on the daemon's role, which is why it needs the daemon's privileges.

Name the operation before naming the privilege. The two are usually easier to separate than they look.

Verify the split with two accounts

Connect the tunnel as the service, then run the application as an ordinary user and confirm it reaches the private resource. Check ip route get for the destination from that user's shell to see which interface was selected. If the route is right and the connection still fails, the problem is policy rather than privilege.

The design that results is least privilege with an explicit boundary: one component owns setup and tunnel resources, workloads keep their normal accounts, and network policy decides what may pass. Running everything as root collapses those roles without explaining anything.

Sɛbɛn minnu bɛ tali kɛ