ACLs for log‑only access
When you need a user to read system logs but you don’t want to give them sudo or pull them into a privileged group, POSIX ACLs are the cleanest way to do it.
They let you hand out read rights on a per‑file or per‑directory basis, and you can set defaults so that every new file inherits the same restrictions.
A backup account that pulls logs to an off‑site archive but never touches the live system is the classic case. ACLs keep the principle of least privilege intact while still letting the job run unattended.
Preparing the backup user
# Create the account, no shell, home directory for logs
sudo useradd -r -s /usr/sbin/nologin -d /var/backups/backup backup
sudo mkdir -p /var/backups/backup
sudo chown backup:backup /var/backups/backup
sudo chmod 700 /var/backups/backup
Why nologin?
A non‑interactive account cuts down the attack surface.
Why a dedicated home?
It keeps the backup files separate from the rest of the system.
If you prefer a group, create a backup group and add the user to it. The ACL example below uses a group, but the same commands work with a single user by replacing g:backup with u:backup.
Setting the ACL on /var/log
# Give the backup group read and execute on the directory
sudo setfacl -m g:backup:rx /var/log
# Give read permission on existing log files
sudo find /var/log -type f -exec setfacl -m g:backup:r {} \;
rx on the directory lets the user walk into /var/log and list its contents.
r on files lets the user open and read them. No write or execute bits are granted.
Making new files inherit the ACL
Log files are created by services that run as root. To ensure every new log file gets the same ACL, set a default ACL on the directory:
sudo setfacl -d -m g:backup:rx /var/log
The -d flag creates a default ACL that applies to all new files and subdirectories.
If you want the backup group to read rotated logs that are created by logrotate, the default ACL will apply to those files as well.
Verifying the ACL
sudo getfacl /var/log | grep backup
Typical output:
# file: /var/log
# owner: root
# group: root
user::rwx
group::r-x
mask::r-x
other::r-x
group:backup:rx
For a file:
# file: /var/log/syslog
# owner: root
# group: syslog
user::rw-
group::r--
mask::rw-
other::r--
group:backup:r--
The mask line shows the effective permissions for the group; it must include the requested bits. In this case, r-- is the mask, so the backup group can only read.
Running a backup script
A simple, idempotent script that copies logs to the backup directory:
#!/usr/bin/env bash
set -euo pipefail
SRC="/var/log"
DST="/var/backups/backup/logs-$(date +%Y%m%d)"
mkdir -p "$DST"
# rsync preserves timestamps and permissions, but we’ll strip write bits
rsync -a --delete --chmod=Du=rwx,Fu=rw- "$SRC/" "$DST/"
# Optional: compress the archive
tar -czf "$DST.tar.gz" -C "$DST" .
rm -rf "$DST"
Run this script as the backup user:
sudo -u backup /usr/local/bin/backup-logs.sh
Because the ACL on /var/log only grants read, the script can’t modify any log file. The rsync command uses --chmod to set the destination permissions to read‑write for the backup user, but it never touches the source.
Schedule it with cron or a systemd timer. A systemd timer is preferable on modern distributions:
# /etc/systemd/system/backup-logs.service
[Unit]
Description=Backup system logs
[Service]
Type=oneshot
User=backup
ExecStart=/usr/local/bin/backup-logs.sh
# /etc/systemd/system/backup-logs.timer
[Unit]
Description=Run backup-logs.service daily
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
Enable and start the timer:
sudo systemctl enable --now backup-logs.timer
Now the logs are copied every day at 2 am, read‑only from /var/log, and stored in an isolated backup directory.
See also
- Detecting and Stopping Rogue DNS Leaks on a VPN with resolvectl and systemd‑resolved
- Rootless Podman on a Raspberry Pi – Solving the “Cannot Bind Mount /home/user/data” Permission Issue
- Finding the Most Frequent Syslog Lines in /var/log/syslog with awk, sort, and head
- Preventing Disk Space Woes on a Home NAS by Configuring Logrotate for Docker Container Logs
- Keep a 1 GB VPS from dying: a single cron job that purges /var/log/syslog after 30 days