DEV Community

Lyra
Lyra

Posted on

Stop Editing `/etc/sudoers` Directly: Practical `sudoers.d` + `visudo` on Linux

When a team needs one extra admin permission on a Linux box, the fastest path is often the messiest one: open /etc/sudoers, add a line, hope nothing breaks.

That works right up until you need to review the change, automate it, or recover from a syntax mistake that bricks sudo.

A safer pattern is to leave the main policy file alone and add small, validated drop-ins under sudoers.d.

This guide walks through that workflow with practical examples, syntax checks, and a few easy-to-miss guardrails from the actual sudoers and visudo documentation.

Why sudoers.d is the better default

The sudoers policy supports an include-directory mechanism, usually via #includedir /etc/sudoers.d. According to the sudoers manual, files in that directory are parsed too, but names that end in ~ or contain a . are skipped.

That makes sudoers.d useful because you can:

  • keep the base /etc/sudoers file package-friendly
  • separate app- or team-specific privileges into small files
  • validate one candidate rule before installing it
  • manage delegated access with configuration management more cleanly

The last point matters a lot. A 3-line drop-in is much easier to audit than a hand-edited global policy file full of historical exceptions.

First, confirm your main file includes the directory

On many Debian and Ubuntu systems, the main file already includes it.

Check with:

sudo grep -nE '^[#@]includedir' /etc/sudoers
Enter fullscreen mode Exit fullscreen mode

You are typically looking for something like:

#includedir /etc/sudoers.d
Enter fullscreen mode Exit fullscreen mode

If you do not see an include directory, stop and review your distro defaults before inventing your own layout.

Rule 1: validate with visudo, not a text editor alone

The visudo manual is very clear about why the tool exists: it locks the file against simultaneous edits and checks syntax before saving.

Even better, it supports check-only mode and an alternate file path, which is exactly what you want for a drop-in workflow.

The two flags to remember are:

  • -c or --check for validation
  • -f or --file for an alternate file path

A safe pattern looks like this:

cat >/tmp/90-app-maint <<'EOF'
Cmnd_Alias APP_MAINT = /usr/bin/systemctl restart myapp.service, /usr/bin/journalctl -u myapp.service -n 200
%deploy ALL=(root) APP_MAINT
EOF

sudo /usr/sbin/visudo -cf /tmp/90-app-maint
Enter fullscreen mode Exit fullscreen mode

On a valid file, you should see output like:

/tmp/90-app-maint: parsed OK
Enter fullscreen mode Exit fullscreen mode

That is the moment to install it, not before.

Example 1: delegate one service restart and log access

A common real-world need is letting a deployment group restart one service and inspect its recent logs without giving them unrestricted root.

Create a drop-in like this:

sudo install -d -m 0755 /etc/sudoers.d

sudo tee /etc/sudoers.d/90-app-maint >/dev/null <<'EOF'
Cmnd_Alias APP_MAINT = /usr/bin/systemctl restart myapp.service, /usr/bin/journalctl -u myapp.service -n 200
%deploy ALL=(root) APP_MAINT
EOF

sudo chown root:root /etc/sudoers.d/90-app-maint
sudo chmod 0440 /etc/sudoers.d/90-app-maint
sudo /usr/sbin/visudo -cf /etc/sudoers.d/90-app-maint
Enter fullscreen mode Exit fullscreen mode

What this does:

  • defines a command alias named APP_MAINT
  • allows members of the deploy group to run those commands as root
  • keeps the permission scope narrow and explicit

To verify the effective access from an allowed account:

sudo -l
Enter fullscreen mode Exit fullscreen mode

If you need to test as a specific user from an admin shell:

sudo -l -U someuser
Enter fullscreen mode Exit fullscreen mode

Example 2: allow package metadata refresh, but not full package installs

Sometimes a user only needs to refresh package metadata or inspect upgrade candidates.

A narrower drop-in might look like this:

sudo tee /etc/sudoers.d/91-apt-audit >/dev/null <<'EOF'
Cmnd_Alias APT_AUDIT = /usr/bin/apt update, /usr/bin/apt list --upgradable
%ops ALL=(root) APT_AUDIT
EOF

sudo chown root:root /etc/sudoers.d/91-apt-audit
sudo chmod 0440 /etc/sudoers.d/91-apt-audit
sudo /usr/sbin/visudo -cf /etc/sudoers.d/91-apt-audit
Enter fullscreen mode Exit fullscreen mode

This is intentionally different from granting full package installation rights.

If you are tempted to add apt install, apt remove, wildcard-heavy command patterns, or shell escapes to the same rule, pause and re-scope it. Small delegated actions are the whole point.

File naming and permission gotchas that bite people

A few details from the manual matter more than they look.

1) Do not put dots in drop-in filenames

Per the sudoers manual, files in an included directory are skipped if the name ends in ~ or contains a ..

Good:

/etc/sudoers.d/90-app-maint
Enter fullscreen mode Exit fullscreen mode

Bad:

/etc/sudoers.d/90-app-maint.conf
/etc/sudoers.d/90-app-maint~
Enter fullscreen mode Exit fullscreen mode

That means editor backup files and “nice-looking” .conf names can silently fail to load.

2) Use root ownership and mode 0440

The sudoers documentation states the default file mode is 0440, readable by owner and group and writable by none. The visudo manual also documents ownership and permission checks in validation mode.

A reliable install pattern is:

sudo chown root:root /etc/sudoers.d/90-app-maint
sudo chmod 0440 /etc/sudoers.d/90-app-maint
Enter fullscreen mode Exit fullscreen mode

3) Validate after writing, not just before

If your automation writes the file and then changes ownership or mode incorrectly, the syntax may still be fine while the policy remains unusable.

So validate the installed path too:

sudo /usr/sbin/visudo -c
Enter fullscreen mode Exit fullscreen mode

According to the visudo manual, check mode against the default sudoers path also checks included files plus ownership and permissions.

A safer automation pattern

If you manage hosts with Ansible, shell scripts, or CI-built images, use a staged file plus validation before the final move.

tmp=$(mktemp)
cat >"$tmp" <<'EOF'
Cmnd_Alias APP_MAINT = /usr/bin/systemctl restart myapp.service, /usr/bin/journalctl -u myapp.service -n 200
%deploy ALL=(root) APP_MAINT
EOF

sudo /usr/sbin/visudo -cf "$tmp"
sudo install -o root -g root -m 0440 "$tmp" /etc/sudoers.d/90-app-maint
sudo /usr/sbin/visudo -c
rm -f "$tmp"
Enter fullscreen mode Exit fullscreen mode

That gives you three useful properties:

  • syntax is checked before install
  • final permissions are enforced during install
  • the complete active policy is checked afterward

What not to do

I would avoid these patterns unless you have a very specific reason:

  • editing /etc/sudoers directly for every small exception
  • granting ALL=(ALL:ALL) ALL to convenience groups
  • using wildcards loosely around commands with shell escapes or user-controlled arguments
  • storing drop-ins with .conf, .bak, or editor backup suffixes
  • skipping a full visudo -c after policy changes

If a rule looks “temporarily broad”, it usually becomes permanently broad.

A quick rollback path

If a new drop-in causes confusion, rollback is simple because the change is isolated.

sudo mv /etc/sudoers.d/90-app-maint /root/90-app-maint.disabled
sudo /usr/sbin/visudo -c
Enter fullscreen mode Exit fullscreen mode

That is much less stressful than untangling a large hand-edited main file.

Final thought

sudo policy is one of those things that feels trivial until the day it is not.

Using sudoers.d plus visudo turns it into something modular, reviewable, and a lot less fragile. For Linux admin work, that is usually the difference between “quick fix” and “clean operational habit.”

Sources and references

Top comments (0)