Open TheDcoder opened 2 years ago
The issue I hit before was how to then run the program in the netns as the target user and with the target user environment, etc.
I think it'd require making more system calls directly, rather than using Rust's Command API.
But this would be good to add - see issue #48 too.
I'm not suggesting a paradigm shift, just an alternative to sudo. I guess one could write a sudo shim program which will automatically translate calls to sudo
to their own method of escalation, but it would be nice to have some kind of official support so that users don't have to muck around with manipulating their PATH
so that vopono can find the sudo shim.
I'd be content even with being able to run vopono as root directly, is this possible?
It used to work, but I think the switch to the sudo
crate might have broken it, I'll take a look when I have time.
In the long-term it'd be best to avoid shelling out entirely, separate privilege escalation if necessary and make syscalls directly - like issue #49
@jamesmcm I did some testing and I running vopono as root, it seems to be stuck starting OpenVPN (even though I can see the process in htop):
root@rpi /t/testing# vopono exec --provider privateinternetaccess --server switzerland 'sleep inf'
2022-09-12T21:41:00.999Z WARN vopono > Could not parse PULSE_SERVER from pactl info output: Err(Could not parse pactl output!:
)
2022-09-12T21:41:00.999Z WARN vopono::util > Running vopono as root user directly!
2022-09-12T21:41:01.017Z INFO vopono::util > Chosen config: /root/.config/vopono/pia/openvpn/switzerland-ch.ovpn
2022-09-12T21:41:01.031Z INFO vopono::netns > Created new network namespace: vopono_pia_switzerland
2022-09-12T21:41:01.229Z INFO vopono::netns > IP address of namespace as seen from host: 10.200.2.2
2022-09-12T21:41:01.229Z INFO vopono::netns > IP address of host as seen from namespace: 10.200.2.1
2022-09-12T21:41:01.244Z INFO vopono::openvpn > Launching OpenVPN...
It's been around like this for a day.
I agree with what you regarding shelling, direct syscalls would be much better.
How does privilege escalation with sudo work? I did some investigation by using a sudo shim script to print out what's being ran and the env variables being set, but I did not see any differences in the variables nor the command which was ran (it was the same as the one I ran).
So how does it know that it's a sudo invocation and not a direct root invocation? How does it identify the original user? Do you just check $USER
because you are calling sudo with the -E
option to preserve user vars? :thinking:
If you run it with -v
for verbose you should see exactly what it runs.
Privilege escalation is currently handled by:
pub fn elevate_privileges(askpass: bool) -> anyhow::Result<()> {
use signal_hook::{consts::SIGINT, flag};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
// Check if already running as root
if nix::unistd::getuid().as_raw() != 0 {
info!("Calling sudo for elevated privileges, current user will be used as default user");
let args: Vec<String> = std::env::args().collect();
let terminated = Arc::new(AtomicBool::new(false));
flag::register(SIGINT, Arc::clone(&terminated))?;
let sudo_flags = if askpass { "-AE" } else { "-E" };
debug!("Args: {:?}", &args);
// status blocks until the process has ended
let _status = Command::new("sudo")
.arg(sudo_flags)
.args(args.clone())
.status()
.context(format!("Executing sudo {} {:?}", sudo_flags, &args))?;
// Deprecated - do we need to handle flag here?
// cleanup::cleanup_signal(SIGINT)?;
if terminated.load(Ordering::SeqCst) {
// we received a sigint,
// so we want to pass it on by terminating with a sigint
nix::sys::signal::kill(nix::unistd::getpid(), nix::sys::signal::Signal::SIGINT)
.expect("failed to send SIGINT");
}
std::process::exit(0);
} else if std::env::var("SUDO_USER").is_err() {
warn!("Running vopono as root user directly!");
}
Ok(())
}
You can also check the OpenVPN log which should be at /root/.config/vopono/logs/
in this case (when it is running).
I think the main remaining problematic part is https://github.com/jamesmcm/vopono/blob/master/vopono_core/src/network/netns.rs#L132-L136 - using sudo to run as the given user.
You can also check the OpenVPN log which should be at /root/.config/vopono/logs/ in this case (when it is running).
I can't see the logs
directory (vopono is still running):
root@rpi ~# ls ~/.config/vopono
config.toml pia/
I think the main remaining problematic part is (...) using sudo to run as the given user.
Not sure I understand, I haven't used Rust before so I can't really read the code well given its verbose syntax. I will try the --verbose
option to see what vopono is doing.
I found the logs at /etc/netns/vopono_pia_switzerland/openvpn.log
and I think I found the issue:
(...more of the stuff below...)
1663106301.439663 1 SIGUSR1[soft,init_instance] received, process restarting
1663106621.460569 4000021 RESOLVE: Cannot resolve host address: swiss.privacy.network:1198 (Temporary failure in name resolution)
1663106641.481329 4000021 RESOLVE: Cannot resolve host address: swiss.privacy.network:1198 (Temporary failure in name resolution)
1663106641.481373 40 Could not determine IPv4/IPv6 protocol
1663106641.481467 1 SIGUSR1[soft,init_instance] received, process restarting
1663106961.499285 4000021 RESOLVE: Cannot resolve host address: swiss.privacy.network:1198 (Temporary failure in name resolution)
1663106981.519281 4000021 RESOLVE: Cannot resolve host address: swiss.privacy.network:1198 (Temporary failure in name resolution)
1663106981.519328 40 Could not determine IPv4/IPv6 protocol
1663106981.519423 1 SIGUSR1[soft,init_instance] received, process restarting
And the contents of the two other .conf
files in that directory:
root@rpi ~# cat /etc/netns/vopono_pia_switzerland/{nsswitch,resolv}.conf
# Name Service Switch configuration file.
# See nsswitch.conf(5) for details.
passwd: files systemd
group: files [SUCCESS=merge] systemd
shadow: files systemd
gshadow: files systemd
publickey: files
hosts: files mymachines myhostname dns
networks: files
protocols: files
services: files
ethers: files
rpc: files
netgroup: files
nameserver 209.222.18.222
nameserver 209.222.18.218
Obviously OpenVPN is having trouble with resolving the address of the VPN server, but I'm not sure how to fix the issue. Perhaps I should sync and try the config files with the IP addresses directly instead of domain names.
There is some progress I guess, OpenVPN is running with errors when I use the config files with the IP address directly:
2022-09-13T22:32:38.160Z DEBUG vopono::openvpn > 1663108358.160084 3000021 TLS Error: TLS handshake failed
2022-09-13T22:32:38.161Z DEBUG vopono::openvpn > 1663108358.160261 1 SIGUSR1[soft,tls-error] received, process restarting
2022-09-13T22:32:43.161Z DEBUG vopono::openvpn > 1663108363.161624 1 TCP/UDP: Preserving recently used remote address: [AF_INET]212.102.37.19:1198
2022-09-13T22:32:43.161Z DEBUG vopono::openvpn > 1663108363.161689 1 UDP link local: (not bound)
2022-09-13T22:32:43.161Z DEBUG vopono::openvpn > 1663108363.161705 1 UDP link remote: [AF_INET]212.102.37.19:1198
2022-09-13T22:33:43.876Z DEBUG vopono::openvpn > 1663108423.876649 3000021 TLS Error: TLS key negotiation failed to occur within 60 seconds (check
your network connectivity)
2022-09-13T22:33:43.876Z DEBUG vopono::openvpn > 1663108423.876692 3000021 TLS Error: TLS handshake failed
2022-09-13T22:33:43.876Z DEBUG vopono::openvpn > 1663108423.876869 1 SIGUSR1[soft,tls-error] received, process restarting
2022-09-13T22:33:53.877Z DEBUG vopono::openvpn > 1663108433.877155 1 TCP/UDP: Preserving recently used remote address: [AF_INET]212.102.37.19:1198
2022-09-13T22:33:53.877Z DEBUG vopono::openvpn > 1663108433.877227 1 UDP link local: (not bound)
2022-09-13T22:33:53.877Z DEBUG vopono::openvpn > 1663108433.877284 1 UDP link remote: [AF_INET]212.102.37.19:1198
2022-09-13T22:34:54.081Z DEBUG vopono::openvpn > 1663108494.081120 3000021 TLS Error: TLS key negotiation failed to occur within 60 seconds (check
your network connectivity)
2022-09-13T22:34:54.081Z DEBUG vopono::openvpn > 1663108494.081154 3000021 TLS Error: TLS handshake failed
It's interesting that it's a DNS issue, since that is set in /etc/netns/{netns.name}/hosts
Are you using any service to manage DNS automatically?
I'm using Arch Linux ARM's default network stack so I guess I'm using systemd-resolved
for the DNS.
Given that lots of installations have firejail
installed, using that for creating network namespaces if available (requires no privileges) would be an option as well.
Another option might be bwrap
, which is even more common due to flatpak, which also supports new creating network namespaces, but I'm not sure if those are customizable enough.
Edit: Even with bwrap
, I think this should be very possible as you should be able to just bindmount something to /etc/resolv.conf for instance.
For anyone looking for a way to run a program through a vpn connection without using sudo, I found that directly setting up a linux namespace (like here) and then using firejail --netns=... command
works really well. Thanks @alexmo1997 for the pointer.
It would be nice to be able to do it via vopono at some point but as I understand it this would be a larger undertaking
FWIW I intend to add creating just the configured network namespace in the next release. You still need sudo to create the network namespace though, no?
Yes indeed, sudo is needed to setup the namespace. Adding a namespace setup command to vopono as you said, that allows to later run a program in it without sudo would be a good solution in my opinion.
I'm trying to create a user service with vopono and it's been kind of a pain, so the ability to run vopono exec
without sudo
would be a blessing!
@Lcchy Do you have any pointers for setting up something like this for ProtonVPN?
@musjj You can find the scripts I've ended up writing here
The rest of the repo just adds some utility to automatically fetch the wg config files from Mullvad and other things, but is not really necessary.
You would need to manually or automatically fetch a wireguard config file from your vpn provider
@Lcchy Thanks, but I realized that vopono actually supports this now (kind of), should've read the guide more carefully: https://github.com/jamesmcm/vopono/blob/master/USERGUIDE.md#creating-only-network-namespace.
I tried to turn it into a service, but it doesn't correctly start for some reason:
Mar 13 03:13:08 $HOST sudo[2110670]: pam_unix(sudo:session): session opened for user root(uid=0) by $USER(uid=1000)
Mar 13 03:13:09 $HOST sudo[2110670]: pam_unix(sudo:session): session closed for user root
It just starts and stops without any errors.
Here's my service (don't mind the nix paths):
[Unit]
Description=Run applications through VPN tunnels with temporary network namespaces
Wants=network-online.target
After=network-online.target
[Service]
Environment=PATH=/run/wrappers/bin:/nix/store/j0i0p3mzlf7p1j69ximz567cpvhlnk2b-openvpn-2.6.9/bin:/nix/store/imf924bs6lwrvkpdairl6sw48a2aljra-vopono-0.10.9/bin
Environment=SSH_AUTH_SOCK=%t/gnupg/S.gpg-agent.ssh
Environment=SSH_ASKPASS=1
ExecStart=/nix/store/imf924bs6lwrvkpdairl6sw48a2aljra-vopono-0.10.9/bin/vopono -v exec --provider protonvpn --server japan --create-netns-only none
Type=simple
[Install]
WantedBy=multi-user.target
It looks like the sudo
was authenticated successfully, but then it just does nothing and shuts down.
Hi! sorry for answering so late. In my opinion it seems like vopono tries to authenticate but doesn't manage to attach to any input so it closes without creating the namespace. But I am not so sure, it could be something else.
EDIT: as written in the userguide, vopono would need to be setup on root to not ask for a password I think: https://github.com/jamesmcm/vopono/blob/master/USERGUIDE.md#systemd-service
Hi,
Is there any way to use vopono without using sudo? I don't use it on my system (I prefer using doas) and it seems to be a hard requirement as vopono automatically calls
sudo
.So is there any way to use this without sudo? Even as root directly.
Ideally we want to implement an alternative mechanism for privilege escalation which is generic and not dependent on a single utility like sudo. For example this can be easily done by a script which handles escalation.