Packets that are not locally emitted by an user process don't have an UID, this includes for example:
- incoming traffic
- routed traffic
- kernel generated traffic (eg: ICMP errors, TCP RST)
- UDP packets used to tunnel WireGuard (emitted by kernel's WireGuard driver), even if here it concerns only the extra protocol packets rather than all of them (see below).
- probably a few missing examples
Here's an excerpt from the owner match module:
Forwarded packets do not have any socket associated with them. Packets
from kernel threads do have a socket, but usually no owner.
[...]
[!] --uid-owner userid[-userid]
Matches if the packet socket's file structure (if it has one) is owned by the given user. You may also specify a numerical UID, or an
UID range.
When there's no UID associated to (the socket associated to) a packet, the reserved invalid uid -1, aka 2^32-1=4294967295 (which is not a valid value to give as parameter) is returned to the owner match module instead of an UID. That's just to explain the meaning of the values. To find a packet not associated with any UID, one has thus to do the inverted query of matching all possible UIDs: ! --uid-owner 0-4294967294
.
Now for the specific case of WireGuard at least, it appears that the owner meta properties of the packet in the WireGuard interface (eg wg0) are propagated to the encapsulating packet on the actual interface (eg eth0). So most of the encapsulating UDP traffic will appear with the associated owner having generated it, matching the owner of the traffic inside the wg0 interface. This shouldn't come as granted (I was actually surprised that's the case, using kernel 5.7.x here), and it's possible other kind of tunnels don't behave the same here (they would have an owner inside the interface, but not on the encapsulating packets outside). I expect userland tunnels like OpenVPN to behave like this.
Then there are still all the cases described at the beginning without owner, among them:
- WireGuard's protocol traffic which periodically revalidates the communication. Usually sent right before or right after some data payload when there was some time without communication. Also sent when persistent keepalive is enabled.
- tunnelled kernel traffic without owner (such as a TCP RST), thus also without owner on the encapsulating UDP packet.
Those require the inverted match as explained above.
I don't know the actual goal behind the question, but will assume it's to grant access only to some UIDs. For UID 0 and 1000 this would become (could be further factorized):
iptables -I OUTPUT 1 -p udp --dport 51820 -m owner ! --uid-owner 0-4294967294 -j ACCEPT
iptables -I OUTPUT 2 -p udp --dport 51820 -m owner 0 -j ACCEPT
iptables -I OUTPUT 3 -p udp --dport 51820 -m owner 1000 -j ACCEPT
iptables -I OUTPUT 4 -p udp --dport 51820 -j DROP
One can verify the user/kernel property in logs with two rules that could look like this (and that surely require some additional limit match):
iptables -I OUTPUT 1 -p udp --dport 51820 -m owner ! --uid-owner 0-4294967294 -j LOG --log-prefix ' kernel originated packet: '
iptables -I OUTPUT 2 -p udp --dport 51820 -m owner --uid-owner 0-4294967294 -j LOG --log-prefix 'userland originated packet: ' --log-uid