Sudo
GitHub Blog Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

When it comes to sudo logging, pretty is not always better

Version 1.9.16 of sudo will introduce a new logging option: json_compact. This does not affect logging to syslog, only logging to files. Previously, sudo created human-readable JSON log files. With this new setting enabled, logs are no longer pretty but can be easily read by logging software.

Before you begin

As I am writing this blog, version 1.9.16 is not yet released, not even a beta. For now, if you want to test this feature, you will have to compile sudo yourself from source. Once 1.9.16 is released, it will be available here on the sudo website as ready to install package for major Linux and UNIX variants. And eventually it will officially become available in various operating systems.

Compiling sudo

If you have development tools installed on your system, compiling sudo takes just a few minutes. Best of all, it is possible to build ready to install packages, if you use one of the supported operating systems. In my tests I used openSUSE Leap 15.5, which is roughly SLES 15 SP5 compatible.

Download the sudo source code from github:

git clone https://github.com/sudo-project/sudo.git

Change to the scripts directory in the sudo source code, and you are ready to build a sudo package with just a single command:

./mkpkg

If you have all necessary dependencies installed, then in just a few minutes you will have ready to install sudo packages in the directory. In my case these are two RPM packages:

sudo-1.9.15-6.x86_64.rpm
sudo-logsrvd-1.9.15-6.x86_64.rpm

The first one contains the actual sudo command and related utilities. The second file installs the sudo log server daemon, the service for central session recording collection. Check --help if you need extra options, like Python support. However, those are not needed for testing the new logging options.

The pretty JSON logs

If you want to see something really pretty, change the logformat to JSON and the logging destination to a file. Add the following two lines to the sudoers file using visudo:

Defaults log_format=json
Defaults logfile=/var/log/czpsudo

Obviously, the name of the log file could be anything. I tend to use my initials in names just to make sure that the name I use does not interfere with any other names on the system.

Now try to run any command using sudo and check the content of the log file. It is a beautiful, human readable, JSON-formatted file. Here is a sample event. A simple ls command, but the log spans many lines:

{
    "accept": {
        "uuid": "985717a7d8-a11d-4897-3f4b-914b76d20c",
        "server_time": {
            "seconds": 1712587090,
            "nanoseconds": 396830151,
            "iso8601": "20240408143810Z",
            "localtime": "Apr  8 16:38:10"
        },
        "submit_time": {
            "seconds": 1712587090,
            "nanoseconds": 396543098,
            "iso8601": "20240408143810Z",
            "localtime": "Apr  8 16:38:10"
        },
        "submituser": "czanik",
        "command": "/usr/bin/ls",
        "runuser": "root",
        "runcwd": "/home/czanik",
        "source": "/etc/sudoers:66:23",
        "ttyname": "/dev/pts/0",
        "submithost": "leap154b",
        "submitcwd": "/home/czanik",
        "runuid": 0,
        "columns": 238,
        "lines": 60,
        "runargv": [
            "ls"
        ],
        "runenv": [
            "LANG=en_US.UTF-8",
            "COLORTERM=1",
            "TERM=xterm-256color",
            "MAIL=/var/mail/root",
            "PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin:/usr/local/sbin",
            "LOGNAME=root",
            "USER=root",
            "HOME=/root",
            "SHELL=/bin/bash",
            "SUDO_COMMAND=/usr/bin/ls",
            "SUDO_USER=czanik",
            "SUDO_UID=1000",
            "SUDO_GID=100",
            "SUDO_HOME=/home/czanik"
        ],
        "submitenv": [
            "LS_COLORS=no=00:fi=00:di=01;34:ln=00;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=41;33;01:ex=00;32:*.cmd=00;32:*.exe=01;32:*.com=01;32:*.bat=01;32:*.btm=01;32:*.dll=01;32:*.tar=00;31:*.tbz=00;31:*.tgz=00;31:*.rpm=00;31:*.deb=00;31:*.arj=00;31:*.taz=00;31:*.lzh=00;31:*.lzma=00;31:*.zip=00;31:*.zoo=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31:*.tb2=00;31:*.tz2=00;31:*.tbz2=00;31:*.xz=00;31:*.avi=01;35:*.bmp=01;35:*.dl=01;35:*.fli=01;35:*.gif=01;35:*.gl=01;35:*.jpg=01;35:*.jpeg=01;35:*.mkv=01;35:*.mng=01;35:*.mov=01;35:*.mp4=01;35:*.mpg=01;35:*.pcx=01;35:*.pbm=01;35:*.pgm=01;35:*.png=01;35:*.ppm=01;35:*.svg=01;35:*.tga=01;35:*.tif=01;35:*.webm=01;35:*.webp=01;35:*.wmv=01;35:*.xbm=01;35:*.xcf=01;35:*.xpm=01;35:*.aiff=00;32:*.ape=00;32:*.au=00;32:*.flac=00;32:*.m4a=00;32:*.mid=00;32:*.mp3=00;32:*.mpc=00;32:*.ogg=00;32:*.voc=00;32:*.wav=00;32:*.wma=00;32:*.wv=00;32:",
            "HOSTTYPE=x86_64",
            "LESSCLOSE=lessclose.sh %s %s",
            "XKEYSYMDB=/usr/X11R6/lib/X11/XKeysymDB",
            "LANG=en_US.UTF-8",
            "MALLOC_PERTURB_=69",
            "WINDOWMANAGER=/usr/bin/startxfce4",
            "LESS=-M -I -R",
            "HOSTNAME=leap154b",
            "CONFIG_SITE=/usr/share/site/x86_64-unknown-linux-gnu",
            "CSHEDIT=emacs",
            "GPG_TTY=/dev/pts/0",
            "AUDIODRIVER=pulseaudio",
            "LESS_ADVANCED_PREPROCESSOR=no",
            "COLORTERM=1",
            "MACHTYPE=x86_64-suse-linux",
            "QEMU_AUDIO_DRV=pa",
            "MINICOM=-c on",
            "OSTYPE=linux",
            "USER=czanik",
            "PAGER=less",
            "MORE=-sl",
            "PWD=/home/czanik",
            "HOME=/home/czanik",
            "HOST=leap154b",
            "XNLSPATH=/usr/share/X11/nls",
            "XDG_DATA_DIRS=/usr/share",
            "PROFILEREAD=true",
            "FROM_HEADER=",
            "MAIL=/var/spool/mail/czanik",
            "LESSKEY=/etc/lesskey.bin",
            "TERM=xterm-256color",
            "SHELL=/bin/bash",
            "LS_OPTIONS=-N --color=tty -T 0",
            "PYTHONSTARTUP=/etc/pythonstart",
            "SHLVL=1",
            "G_FILENAME_ENCODING=@locale,UTF-8,ISO-8859-15,CP1252",
            "MANPATH=/usr/local/man:/usr/share/man",
            "LOGNAME=czanik",
            "XDG_CONFIG_DIRS=/etc/xdg",
            "PATH=/home/czanik/bin:/usr/local/bin:/usr/bin:/bin",
            "G_BROKEN_FILENAMES=1",
            "HISTSIZE=1000",
            "CPU=x86_64",
            "LESSOPEN=lessopen.sh %s",
            "BASH_FUNC_mc%%=() {  . /usr/share/mc/mc-wrapper.sh\n}",
            "_=/usr/local/bin/sudo"
        ]
    }
}

This is almost two pages in my text editor. It is easy for a person to read–we can find any important information just by a quick glance at the log.

So, why do we need yet another logging format? Nowadays, logs are rarely read directly by humans unless you have a single host to check logs on. Instead, the logs are collected centrally and analyzed by various applications. Humans usually check the logs only when these apps spot something suspicious, or when they learn of an anomaly some other way. By default, logging applications expect events to be a single line, and dealing with multi-line log messages requires extra effort, if it is possible at all.

The compact JSON logs

The answer to this problem is the introduction of single line JSON messages by sudo. This is known as compact or minified JSON. The configuration is similar, but instead of json, it is called json_compact:

Defaults log_format=json_compact
Defaults logfile=/var/log/czpsudo2

The content of the log message is the same, however instead of being human readable, all fields are on a single line with no extra white space. Not so convenient for humans, but easy to read by most of the existing log management applications:

{"accept":{"uuid":"8cdfaea5c1-df69-44b6-180a-7fb1378d94","server_time":{"seconds":1712645078,"nanoseconds":588541414,"iso8601":"20240409064438Z","localtime":"Apr  9 08:44:38"},"submit_time":{"seconds":1712645078,"nanoseconds":587941154,"iso8601":"20240409064438Z","localtime":"Apr  9 08:44:38"},"submituser":"czanik","command":"/usr/bin/ls","runuser":"root","runcwd":"/home/czanik","source":"/etc/sudoers:66:23","ttyname":"/dev/pts/0","submithost":"leap154b","submitcwd":"/home/czanik","runuid":0,"columns":118,"lines":60,"runargv":["ls","/root/"],"runenv":["LANG=en_US.UTF-8","COLORTERM=1","TERM=xterm-256color","MAIL=/var/mail/root","PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin:/usr/local/sbin","LOGNAME=root","USER=root","HOME=/root","SHELL=/bin/bash","SUDO_COMMAND=/usr/bin/ls /root/","SUDO_USER=czanik","SUDO_UID=1000","SUDO_GID=100","SUDO_HOME=/home/czanik"],"submitenv":["LS_COLORS=no=00:fi=00:di=01;34:ln=00;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=41;33;01:ex=00;32:*.cmd=00;32:*.exe=01;32:*.com=01;32:*.bat=01;32:*.btm=01;32:*.dll=01;32:*.tar=00;31:*.tbz=00;31:*.tgz=00;31:*.rpm=00;31:*.deb=00;31:*.arj=00;31:*.taz=00;31:*.lzh=00;31:*.lzma=00;31:*.zip=00;31:*.zoo=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31:*.tb2=00;31:*.tz2=00;31:*.tbz2=00;31:*.xz=00;31:*.avi=01;35:*.bmp=01;35:*.dl=01;35:*.fli=01;35:*.gif=01;35:*.gl=01;35:*.jpg=01;35:*.jpeg=01;35:*.mkv=01;35:*.mng=01;35:*.mov=01;35:*.mp4=01;35:*.mpg=01;35:*.pcx=01;35:*.pbm=01;35:*.pgm=01;35:*.png=01;35:*.ppm=01;35:*.svg=01;35:*.tga=01;35:*.tif=01;35:*.webm=01;35:*.webp=01;35:*.wmv=01;35:*.xbm=01;35:*.xcf=01;35:*.xpm=01;35:*.aiff=00;32:*.ape=00;32:*.au=00;32:*.flac=00;32:*.m4a=00;32:*.mid=00;32:*.mp3=00;32:*.mpc=00;32:*.ogg=00;32:*.voc=00;32:*.wav=00;32:*.wma=00;32:*.wv=00;32:","HOSTTYPE=x86_64","LESSCLOSE=lessclose.sh %s %s","XKEYSYMDB=/usr/X11R6/lib/X11/XKeysymDB","LANG=en_US.UTF-8","MALLOC_PERTURB_=69","WINDOWMANAGER=/usr/bin/startxfce4","LESS=-M -I -R","HOSTNAME=leap154b","CONFIG_SITE=/usr/share/site/x86_64-unknown-linux-gnu","CSHEDIT=emacs","GPG_TTY=/dev/pts/0","AUDIODRIVER=pulseaudio","LESS_ADVANCED_PREPROCESSOR=no","COLORTERM=1","MACHTYPE=x86_64-suse-linux","QEMU_AUDIO_DRV=pa","MINICOM=-c on","OSTYPE=linux","USER=czanik","PAGER=less","MORE=-sl","PWD=/home/czanik","HOME=/home/czanik","HOST=leap154b","XNLSPATH=/usr/share/X11/nls","XDG_DATA_DIRS=/usr/share","PROFILEREAD=true","FROM_HEADER=","MAIL=/var/spool/mail/czanik","LESSKEY=/etc/lesskey.bin","TERM=xterm-256color","SHELL=/bin/bash","LS_OPTIONS=-N --color=tty -T 0","PYTHONSTARTUP=/etc/pythonstart","SHLVL=1","G_FILENAME_ENCODING=@locale,UTF-8,ISO-8859-15,CP1252","MANPATH=/usr/local/man:/usr/share/man","LOGNAME=czanik","XDG_CONFIG_DIRS=/etc/xdg","PATH=/home/czanik/bin:/usr/local/bin:/usr/bin:/bin","G_BROKEN_FILENAMES=1","HISTSIZE=1000","CPU=x86_64","LESSOPEN=lessopen.sh %s","BASH_FUNC_mc%%=() {  . /usr/share/mc/mc-wrapper.sh\n}","_=/usr/local/bin/sudo"]}}

There are applications, such as jq, that can render these logs nicely on the terminal. But if you have more than a single machine, then you collect logs centrally anyway, and most likely push logs to Elasticsearch or similar, where you can view JSON data easily.

What is next?

With the introduction of the json_compact option, the old format is also available as json_pretty. Right now pretty output is still the default when you set log_format to json. However, in a future version json_compact will become the default logging format if you use json.

If you use syslog-ng, head over to the syslog-ng blog at https://www.syslog-ng.com/community/b/blog where I plan to publish a blog about working with a sudo log written with json_compact formatting.

If you would like to be notified about new posts and sudo news, sign up for the sudo blog announcement mailing list.