New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for unprivileged namespaces on linux #104
Conversation
Hi, thanks a lot. This is a great feature and the PR is well-documented. It's rare to see PRs of such quality and I really appreciate the effort. I probably won't be able to finish the review today, but I will likely get this merged next weekend. Regarding your notes:
This is totally true. There is at least one easily feasible approach to solve this:
I will address this when merging your PR. Thanks again. PS: Depending on your use case, you may also be interested in pallium which solves this by chaining network namespaces with the first one making use of (https://github.com/rootless-containers/slirp4netns) or slirpnetstack. (It does not yet use tun2proxy though.) |
Hi. Thanks for the suggestions, will definitely give them a try. There are some issues with cross compilation, I will address them in a weekend.
Good to hear. You may also consider moving |
I am considering this, I have already seen the |
90be936
to
a1a708a
Compare
Fixed, checks are all green. I just tested Edit: Large |
589da6a
to
d6bd7a5
Compare
Hi @one-d-wide , I have changed the code to make UDP work again. Please check your part to make it works correctly. |
Hi. The issue with socks5 associated udp connection was that primary tcp connection must be maintained for the whole period of the udp session, and when it closes socks5 server assumes udp connection is no longer in use. I reverted your last commit since it is mostly obsolete. Should I remove it from the history entirely? And could you elaborate about the change to the cli parameters there making /// Specify a command to run with root-like capabilities in the new namespace.
/// This could be useful to start additional daemons, e.g. `openvpn` instance.
- #[arg(requires = "unshare")]
+ #[arg(long, value_name = "command", requires = "unshare")]
pub admin_command: Vec<OsString>, For example that change would require turning the following line: $ tun2proxy --unshare --setup --proxy none -- openvpn --config config.ovpn To this: $ tun2proxy --unshare --setup --proxy none --admin-command=openvpn --admin-command=--config --admin-command=config.ovpn |
Since first call to get_udp_associate() will always is None, so there be minor changes. |
OK, change back as your wish.
|
Since the merge will be |
It isn't. In case proxy type is |
Oops. |
You change back it. Please. |
I'm not familiar with namespaces, so please modify it according to your ideas. |
Why would you do so? This would destroy any logical changes segregation provided by commits, I contributed to some larger project and they were pretty insistent on keeping commit history clean.
Ok, give me a moment. |
Hi @blechschmidt , I have no more opinion here. Please review and test and then merge it. |
7d3c48a
to
a5e9195
Compare
Thank you again for implementing this feature. I have merged this implementation now using a merge commit, making it clear that all commits have been merged into master in one go. Apart from a few cosmetic changes, I have mainly made the following adaptions:
There is certainly a lot of room for optimization. One just has to look at the push/pull implementation ( |
This has been addressed in 09994d4 and v4.0.0 of the |
Motivation
Hi. I was trying to came up with some solution to make a network environment isolated from a global system setup with all traffic reaching outside routing via the a socks proxy or vpn instance in unprivileged (rootless) manner.
Usually this would require using something like veth pairs (virtual network interfaces) to bridge namespaces. But required modifications affect the global network namespace therefore require root-like capabilities. That aren't normally available to the user processes.
This why I came up with a solution utilizing TUN interface. And since it similar to what this project does, I decided to integrate that functionality here.
Here are use-cases I wanted to add support for:
Implemented features
--unshare
.--proxy none
.Usage
Create a new network namespace where all traffic reaching outside is routed through the socks5 proxy.
$ tun2proxy --unshare --startup --proxy "socks5://..." ...
Create a new network namespace without additional redirection of traffic with
openvpn
running in it (with root-like capabilities provided to allow network configuration).$ tun2proxy --unshare --startup --proxy "none" -- openvpn [...] ...
Start
/bin/sh
in the namespace created by thetun2proxy
started the latest. Persistent command is suggested intun2proxy
output.$ nsenter --target $(pgrep -n tun2proxy) --preserve-credentials --user --net --mount /bin/sh
Architecture
There are three core ideas in play:
unix(7)
sockets may not only exchange data but also copies of valid file descriptors.This mechanism is useful to transfer sockets bonded to the global network to another network namespace.
CLOEXEC
flag (it is usually set by the std implementation) are inherited in descending processes.Using those ideas execution flow can be described as follows:
tun2proxy
process with argument--unshare
generates a pair of interconnected unix sockets unsettingCLOEXEC
flag on one of those and converting it to raw file descriptor value.unshare(1)
process that creates a new namespace.unshare
process is instructed to starttun2proxy
binary again but this time with--socket-transfer-fd <fd>
argument where<fd>
is the raw file descriptor discussed earlier.tun2proxy
process inside the new namespace initializes TUN interface and routing table as normal.tun2proxy
process that is still in the global network namespace and sockets created by which are bonded to the global namespace even after the transfer to the descendant process occurs.[admin command]
cli argument) is started after the network is ready, preserving root-like capabilities, that process could be for example a vpn daemon.tun2proxy
operates as usual.Code changes
bin/main.rs
- added a startup routine triggered if--unshare
is specified.args.rs
- added new cli arguments:--proxy none
,--unshare
,--socket-transfer-fd <fd>
,[admin\_command...]
,udp_timeout
.desktop_api.rs
- added code to spawn anadmin_command
and fix to allow running other proxy in a new namespace.lib.rs
- added functionality to fetch network sockets in case--socket-tranfer-fd
was specified. Also a small fix to bunch series ofMutex::lock
into one call where applicable.no_proxy.rs
(created) - implementation of no-proxy mode, basically a no-op since it signals that connection is always established.proxy_handle.rs
(and implementations of proxy protocols) - addget_server_addr
function toProxyHandler
trait, previously server address was supplied directly from cli arguments.socket_transfer.rs
(created) - functionality to safely transfer raw sockets between processes.Notes
tproxy-config
is lacking the support not to set ipv6 routing. This confuses some user application if proxy doesn't support ipv6 connectivity, e.g. causes timeout in resolution and subsequent fail of dns requests (this also could be attributed toipstack
not reporting unreachable destinations).tproxy-config
is always setting more specific (compared to0.0.0.0/0
) routing addresses even if no default route is presented in the network namespace. This causes mutual exclusion in routing tables for0.0.0.0/1
and128.0.0.0/1
address spaces with other proxy application (notablyopenvpn
).