139 lines
4.9 KiB
Markdown
139 lines
4.9 KiB
Markdown
|
---
|
||
|
title: "Hello doas"
|
||
|
tags: ["Linux"]
|
||
|
date: 2021-01-30T15:15:55-05:00
|
||
|
draft: false
|
||
|
---
|
||
|
Today, I switched my workstation from `sudo` to `doas`. I'm running Void Linux,
|
||
|
and the process was fairly easy.
|
||
|
|
||
|
First, I needed to figure out how to remove `sudo` (yes, I realize I could have
|
||
|
installed `doas` first, then removed `sudo`, but I decided to do it the hard way.)
|
||
|
As it turns out, the [advanced usage section of the XBPS manual](https://docs.voidlinux.org/xbps/advanced-usage.html#ignoring-packages) details how to use the `ignorepkg` entry in xbps.d with nothing
|
||
|
other than this exact use case! I created the file `/etc/xbps.d/20-ignorepkg-sudo.conf` with contents
|
||
|
|
||
|
```
|
||
|
ignorepkg=sudo
|
||
|
```
|
||
|
|
||
|
and then ran `sudo xbps-remove sudo` (an ironic command).
|
||
|
|
||
|
After that, because I was stupid and removed `sudo` before I had set up `doas`,
|
||
|
I had to use plain-old `su` to change to the root user and run `xi opendoas`. I also
|
||
|
configured `doas` in `/etc/doas.conf` with the following:
|
||
|
|
||
|
```
|
||
|
# see doas.conf(5) for configuration details
|
||
|
permit nopass keepenv :admin
|
||
|
```
|
||
|
|
||
|
I ran `groupadd admin`, `usermod -aG admin joel`, and then logged out so that my
|
||
|
user account would see the new group perms.
|
||
|
|
||
|
And just like that, I can now run `doas xbps-install ...` and all of my other commands,
|
||
|
just substituting `doas` for `sudo`.
|
||
|
|
||
|
The one thing I immediately missed was `sudoedit`. Before I accidentally tried
|
||
|
to use `sudo` for the first time, I had already accidentally tried to run `sudoedit`
|
||
|
*at least* 5 times. I had to fix this. I saw a discussion on Reddit where [one user
|
||
|
suggested](https://www.reddit.com/r/linux/comments/l6y7nv/is_doas_a_good_alternative_to_sudo/gl4hs42?utm_source=share&utm_medium=web2x&context=3) writing a script to replace the `sudoedit` functionality.
|
||
|
I quickly starting hacking together something like that. I started with:
|
||
|
|
||
|
```
|
||
|
#!/bin/sh
|
||
|
mkdir -p /tmp/doasedit
|
||
|
doas cp $1 /tmp/doasedit/tmp_file
|
||
|
$EDITOR /tmp/doasedit/tmp_file
|
||
|
```
|
||
|
|
||
|
And quickly ran into my first road-block. The script is going to have to change
|
||
|
the permissions of that file before the user can edit it. But if the script changes
|
||
|
the permissions, how can I restore it to the original location with the right
|
||
|
permissions? `cp /tmp/doasedit/tmp_file $1` won't work. I thought about just using
|
||
|
cat to overwrite the file contents in-place (`cat /tmp/doasedit/tmp_file > $1`).
|
||
|
That *could* create some issues if a program has the file open. Instead, a better option
|
||
|
is to create two copies of the file--one for editing, and one for preserving file
|
||
|
attributes:
|
||
|
|
||
|
```
|
||
|
#!/bin/sh
|
||
|
mkdir -p /tmp/doasedit
|
||
|
doas cp $1 /tmp/doasedit/edit
|
||
|
doas chown -R $USER:$USER /tmp/doasedit/edit
|
||
|
doas cp $1 /tmp/doasedit/file
|
||
|
$EDITOR /tmp/doasedit/edit
|
||
|
cat /tmp/doasedit/edit | doas tee /tmp/doasedit/file 1>/dev/null
|
||
|
doas mv -f /tmp/doasedit/file $1
|
||
|
rm -rf /tmp/doasedit
|
||
|
```
|
||
|
|
||
|
Of course, the issue with this is that it only works with absolute paths.
|
||
|
I want to make it work for relative paths as well. I'm going to take advantage
|
||
|
of `realpath`, which is part of the `coreutils` package from Void. As a bonus, this
|
||
|
will also take care of the edge case where the given file is a symlink (IIRC,
|
||
|
`sudoedit` didn't follow symlinks, so I may be diverging here):
|
||
|
|
||
|
```
|
||
|
#!/bin/sh
|
||
|
mkdir -p /tmp/doasedit
|
||
|
srcfile="$(realpath $1)"
|
||
|
|
||
|
doas cp $srcfile /tmp/doasedit/edit
|
||
|
doas chown -R $USER:$USER /tmp/doasedit/edit
|
||
|
doas cp $srcfile /tmp/doasedit/file
|
||
|
|
||
|
$EDITOR /tmp/doasedit/edit
|
||
|
|
||
|
cat /tmp/doasedit/edit | doas tee /tmp/doasedit/file 1>/dev/null
|
||
|
doas mv -f /tmp/doasedit/file $srcfile
|
||
|
|
||
|
rm -rf /tmp/doasedit
|
||
|
```
|
||
|
|
||
|
At this point, it works...okay-ish. It can only be used in one instance currently
|
||
|
since I hard-coded `/tmp/doasedit/file` and `/tmp/doasedit/edit`, but that's easily fixed:
|
||
|
|
||
|
```
|
||
|
#!/bin/sh
|
||
|
|
||
|
destfile_pfx="$(cat /dev/urandom | tr -cd 'a-f0-9' | head -c 32)"
|
||
|
|
||
|
while [ -d "/tmp/doasedit/$destfile_pfx" ]; do
|
||
|
destfile_pfx="$(cat /dev/urandom | tr -cd 'a-f0-9' | head -c 32)"
|
||
|
done
|
||
|
|
||
|
mkdir -p /tmp/doasedit/$destfile_pfx
|
||
|
srcfile="$(realpath $1)"
|
||
|
|
||
|
doas cp $srcfile /tmp/doasedit/$destfile_pfx/edit
|
||
|
doas chown -R $USER:$USER /tmp/doasedit/$destfile_pfx/edit
|
||
|
doas cp $srcfile /tmp/doasedit/$destfile_pfx/file
|
||
|
|
||
|
$EDITOR /tmp/doasedit/$destfile_pfx/edit
|
||
|
|
||
|
cat /tmp/doasedit/$destfile_pfx/edit | doas tee /tmp/doasedit/$destfile_pfx/file 1>/dev/null
|
||
|
doas mv -f /tmp/doasedit/$destfile_pfx/file $srcfile
|
||
|
|
||
|
rm -rf /tmp/doasedit/$destfile_pfx
|
||
|
```
|
||
|
|
||
|
At this point, the only thing missing is the check to see if the file was actually
|
||
|
edited:
|
||
|
|
||
|
```
|
||
|
...
|
||
|
cat /tmp/doasedit/$destfile_pfx/edit | doas tee /tmp/doasedit/$destfile_pfx/file 1>/dev/null
|
||
|
|
||
|
if cmp -s "/tmp/doasedit/$destfile_pfx/file" "$srcfile"; then
|
||
|
echo "Skipping write; no changes."
|
||
|
else
|
||
|
doas mv -f /tmp/doasedit/$destfile_pfx/file $srcfile
|
||
|
fi
|
||
|
...
|
||
|
```
|
||
|
|
||
|
I put this in a [repo on GitHub](https://github.com/AluminumTank/doasedit) if
|
||
|
anyone is interested. I know that a major
|
||
|
weakness of this script is the number of times it calls `doas`, which could
|
||
|
break flows where password is required every time `doas` is run.
|