It's quite a mouthful title, so let's explain the problem first. Recently I was working on a Go project related to VPN in Linux, which among other things needs to install a tun
interface, so it needs to be sudo-ed (run as root). But since I was logged in as a regular user, every attempt to debug the code in VS Code crashed at the line which creates the tun interface. Nothing surprising there - root
user privileges are needed for creating an interface. OK, but how to start debugging as root? Actually, there are probably several solutions, some being trivial:
-
Login to Linux as
root
user. Many resources online explain how to allow that. Here's one such article for Ubuntu 20.04. But I didn't want to do so. There's a reason why logging as root is disabled in the first place. -
Launch VS Code as
root
(something likesudo code .
). Honestly, I haven't even tried that, but it kinda makes sense that it works - if a process runs asroot
, all processes launched by it also run asroot
, at least I believe so. But again, I would rather avoid launching third-party apps asroot
unless I have to. Also, chances are that it would cause headaches with environment configuration - VS Code, since running asroot
, won't be able to see your environment variables. Again, I haven't even tried, so it may happen that I'm wrong - pls correct me.
Solution
After some research, I've found a solution I was satisfied with. It's explained in this Github issue. It is based on executing dlv (a Go debugger)
as root
. Here's what you need to do:
1. Allow sudo
Without Password
To avoid password prompts when using sudo
, you have (at least) two options:
-
Pipe the password to
sudo
, by executing something like (note-S
flag):$ echo my_passw0rd | sudo -S dlv
root
password in plain text in your code, which isn't a good idea. -
Allow
sudo
without password for particular user/command, by editingsudoers
file. So you need to executesudo visudo
, and append the following line at the end of the file:yourusername ALL=(root)NOPASSWD:/home/yourusername/go/bin/dlv
yourusername
with your actual username, of course. Also keep in mind that this line should be added at the end of thesudoers
fil, becausesudo
evaluates the file in order, and takes the last match.
We'll go with the second approach because it's more secure. The added line basically says "when yourusername
executes sudo /home/yourusername/go/bin/dlv
, don't ask for a password."
2. Create a dlv
Wrapper
The next thing we want to do is to intercept all dlm
calls, and to prepend them with sudo
if needed to accomplish that we need to create dlv-sudo.sh
file in .vscode
directory of the project, with the following content:
#!/bin/sh
if ! which dlv ; then
PATH="${GOPATH}/bin:$PATH"
fi
if [ "$DEBUG_AS_ROOT" = "true" ]; then
DLV=$(which dlv)
exec sudo "$DLV" --only-same-user=false "$@"
else
exec dlv "$@"
fi
Don't forget to make the file executable by executing something like: chmod +x .vscode/dlv-sudo.sh
Let's quickly explain the key parts of this script:
-
In line 5 we are checking if we should elevate at all, by examining
DEBUG_AS_ROOT
environment variable. -
If the elevation is requested, we need to replace
dlv
command with the full path (/home/yourusername/go/bin/dlv
), because we've defined the full path in thesudoers
file. This is exactly what we do in line 6. -
In line 7 we're executing the elevated (sudo-ed)
dlv
. -
If the elevation isn't requested, we're executing
dlv
in a regular way, in line 9.
3. Make VS Code Use the Wrapper
Once we have the wrapper created, we need to tell VS Code not to use dlv
command anymore, but to replace it with our script (with dlv-sudo.sh
). To do so, we need to create settings.json
file in .vscode
directory, with the following content:
{
"go.alternateTools": {
"dlv": "${workspaceFolder}/.vscode/dlv-sudo.sh"
}
}
4. Launch Configuration
OK, we've created the feature, so let's see how we can use it. We are now able to define at launch configuration level if we need debugging as root
or not. Here's an example of launch configurations (.vscode/launch.json
file), which contains two configurations - one requires root
debugging, while another doesn't require:
{
"version": "0.2.0",
"configurations": [
{
"name": "Run as root",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}",
"env": {
"DEBUG_AS_ROOT": "true",
},
"args": []
},
{
"name": "Run as myself",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}",
"env": {},
"args": []
}
]
}
As you can see, the only difference between the two configurations is that the first one, Run as root
, defines "DEBUG_AS_ROOT": "true"
environment variable, while the second one doesn't. Now we can simply select the desired configuration in VS Code and debug!
Troubleshooting
Even if you do everything as explained above, it may happen that it doesn't work for some reason. For example, in my case it failed with the following error in VS Code DEBUG CONSOLE:
exec: "go": executable file not found in $PATH
Process exiting with code: 1
It took me more than an hour to resolve because I wasn't reading carefully. The error is actually quite descriptive - it says that go
command cannot be found. But how is it possible if you can execute it? For example, if you try to get the go
version, you'll get something like:
$ go version
go version go1.14.3 linux/amd64
Obviously go
works, but still VS Code displays the error saying that go
can't be found. What's the reason for such strange behavior? The reason in my case was because GOROOT
environment variable was defined in my ~/.profile
file, as well as code which adds it to PATH
. It means that go
command was added to my PATH
, but not in root
user's PATH
. To confirm that this is the reason, I've executed the following:
$ sudo go version
sudo: go: command not found
Sure enough. So the solution is obvious: ensure that root
user's PATH
also includes go
.
Finally, in case of some other problems, there's one potential solution that comes from the Windows world: try with logout/login or restart.
Add new comment
Anonymous comments require solving captcha, and waiting for administrator's approval.