Auditd ACLs

Enhance Zimbra Security with AuditD and ACLs

Auditd (Linux Audit Daemon) can be used to capture detailed information about file accesses, system calls, and user actions. Auditd provides administrators the ability to track changes and identify suspicious activities and potentially get an early warning on system compromise by hackers.

Adding Auditd to your system will give you more detailed logs, but it also increases the overall log volume, therefore it is recommended to enable this along with centralized logging with Elastic Stack. For details see the link in the further reading section.

Linux File System ACLs (Access Control Lists) provide a more flexible way of managing file permissions compared to the traditional chmod and chown commands. When creating new files and folders, Access Control Lists (ACLs) offer several benefits over traditional chmod and chown:

  • Default Permissions for New Files and Directories: ACLs allow you to set default permissions for a directory, which means that any new file or subdirectory created within will automatically inherit those permissions. This is particularly useful when you want consistent access settings without manually modifying each new item.
  • Fine-Grained Access Control: ACLs let you define permissions for specific users or groups beyond the file’s owner, group, and others. When new files or folders are created, the inherited ACL settings can grant access to multiple users directly, avoiding the limitations of the traditional chmod mechanism that can only specify one owner and one group.

Setting up ACLs on Zimbra (beta)

Zimbra engineering is currently investigating the use of ACLs and these will hopefully be added to an upcoming patch soon. That said you can already try out ACLs to enhance security on Zimbra.

Create a new empty file /usr/local/sbin/zmacl_beta with the following contents:

#!/bin/bash

# Function to check if the script is run as root
check_if_root() {
  if [ "$EUID" -ne 0 ]; then
    echo "Error: This script must be run as root."
    exit 1
  fi
}

# Function to check if setfacl is installed
check_setfacl_installed() {
  if ! command -v setfacl &> /dev/null; then
    echo "Error: 'setfacl' command not found."

    if [ -f /etc/debian_version ]; then
      echo "To install 'setfacl' on Ubuntu/Debian, run: sudo apt install acl"
    elif [ -f /etc/redhat-release ]; then
      echo "To install 'setfacl' on RedHat/CentOS/Fedora, run: sudo yum install acl"
    else
      echo "Please install 'acl' package using your distribution's package manager."
    fi
    exit 1
  fi
}

# Check if acl argument is passed
if [ "$#" -ne 1 ]; then
  echo "Usage: $0 [enable|disable]"
  exit 1
fi

ACL_ACTION="$1"

# Directories to apply ACLs to
DIRS=(
  "/opt/zimbra/jetty/webapps/zimbraAdmin/public/"
  "/opt/zimbra/jetty/webapps/zimbra/public/"
)

# Function to enable ACLs (deny write access)
enable_acls() {
  for DIR in "${DIRS[@]}"; do
    echo "Setting ownership and base permissions"
    chown zimbra:zimbra -R "$DIR"
    chmod ugo-w -R "$DIR"
    echo "Applying write deny ACLs to $DIR"

    # Deny write access for everyone except root and zimbra
    setfacl -R -m u::r-x "$DIR"     # Owner has read/execute
    setfacl -R -m u:zimbra:r-x "$DIR"  # zimbra user has read/execute
    setfacl -R -m g::r-x "$DIR"     # Group has read/execute
    setfacl -R -m o::r-x "$DIR"     # Others have read/execute

    # Ensure these ACLs are the default for new files and directories
    setfacl -dR -m u::r-x "$DIR"
    setfacl -dR -m u:zimbra:r-x "$DIR"
    setfacl -dR -m g::r-x "$DIR"
    setfacl -dR -m o::r-x "$DIR"

    echo "ACLs applied successfully to $DIR"
  done
}

# Function to disable ACLs (remove all setfacl entries)
disable_acls() {
  for DIR in "${DIRS[@]}"; do
    echo "Setting ownership and base permissions"
    chown zimbra:zimbra -R "$DIR"
    chmod go-w -R "$DIR"
    chmod u+w -R "$DIR"

    echo "Removing ACLs from $DIR"

    # Remove ACLs recursively
    setfacl -R -b "$DIR"
    setfacl -dR -b "$DIR"   # Remove default ACLs

    echo "ACLs removed successfully from $DIR"
  done
}

# Main script logic
check_if_root           # Check if the script is run as root
check_setfacl_installed # Check if setfacl is installed

case "$ACL_ACTION" in
  enable)
    enable_acls
    ;;
  disable)
    disable_acls
    ;;
  *)
    echo "Invalid argument. Use 'enable' or 'disable'."
    exit 1
    ;;
esac

Next allow the script to be executed, as the user root execute:

chmod +x /usr/local/sbin/zmacl_beta

Then you can enable ACLs on by running the script as root:

/usr/local/sbin/zmacl_beta enable

The ACLs will prevent writes by the user zimbra to the public folders in Jetty. This provides a significant security enhancement, however it will also prevent functionality such as the Client Uploader to work from the Admin Console UI, but it is already recommended to remove this Zimlet as follows:

As the user root run:

rm -Rf /opt/zimbra/lib/ext/com_zimbra_clientuploader

As the user zimbra run:

zmzimletctl undeploy com_zimbra_clientuploader
zmmailboxdctl restart

Enhance Zimbra server logging with AuditD

Now that we have enabled ACLs we can use Auditd to monitor failed write attempts to the Jetty public folders.

Create a script /root/install-auditd with the following contents:

#!/bin/bash

WHO=`whoami`
if [ $WHO != "root" ]
then
echo
echo "Execute this script as root (\"su\")"
echo
exit 1
fi

# Function to check if the kernel is tainted
check_kernel_taint() {
  echo "Checking if the kernel is tainted..."
  taint=$(cat /proc/sys/kernel/tainted)

  if [ "$taint" != "0" ]; then
    echo "WARNING: The kernel is tainted. This can indicate hardware issues or non-standard kernel modules."
    echo "Do you wish to continue? (yes/no)"
    read response
    if [[ "$response" != "yes" ]]; then
      echo "Installation aborted."
      exit 1
    fi
  else
    echo "Kernel is not tainted. Proceeding with installation."
  fi
}

# Function to remove existing audit rules
remove_existing_audit_rules() {
  echo "Removing existing audit rules..."
  auditctl -D
}

# Function to install auditd based on the OS type
install_auditd() {
  if [ -f /etc/redhat-release ]; then
    # RedHat-based systems
    echo "Detected RedHat-based system."
    yum install -y audit
  elif [ -f /etc/lsb-release ]; then
    # Ubuntu-based systems
    echo "Detected Ubuntu-based system."
    apt update
    apt install -y auditd
  else
    echo "Unsupported OS. Exiting."
    exit 1
  fi
}

# Function to add audit rules
add_audit_rules() {
  echo "Adding audit rules..."

   rm -f /etc/audit/rules.d/audit.rules
   cat >> /etc/audit/rules.d/audit.rules << EOF
# This file contains the auditctl rules that are loaded
# whenever the audit daemon is started via the initscripts.
# The rules are simply the parameters that would be passed
# to auditctl.

# First rule - delete all
-D

# Increase the buffers to survive stress events.
# Make this bigger for busy systems
-b 8192

# Feel free to add below this line. See auditctl man page
-w /bin/sh -p x -k rce
-w /bin/bash -p x -k rce
-w /usr/bin/wget -p x -k rce
-w /usr/bin/curl -p x -k rce
-w /usr/bin/python -p x -k rce
-w /bin/dash -p x -k rce

# New rule to monitor write attempts in Zimbra webapps directory
-w /opt/zimbra/jetty/webapps -p wa -k webapps_write_attempts
EOF
}

# Function to add the execve syscall auditing rule
add_execve_rule() {
  echo "Adding execve syscall audit rule..."
  auditctl -a always,exit -F arch=b64 -S execve -k rce
}


# Run the functions
check_kernel_taint
remove_existing_audit_rules
install_auditd
add_audit_rules
add_execve_rule


# Reload auditd to apply the new rules
echo "Reloading auditd to apply the rules..."
systemctl restart auditd

echo "Auditd setup complete."

echo "You can track events using:"
echo "ausearch -k rce"
echo "tail -f /var/log/audit/audit.log | grep rce"

Install Auditd using the script by running as root:

chmod +x /root/install-auditd
/root/install-auditd

Auditd logs into /var/log/audit/audit.log. Again consider setting up RSyslog remote logging. Here is an example log line of what a failed write attempt would look like in the Jetty public folder:

type=SYSCALL msg=audit(1727443142.767:1275): arch=c000003e syscall=257 success=no exit=-13 a0=ffffff9c a1=7ffe14bea566 a2=941 a3=1b6 items=1 ppid=25529 pid=30908 auid=0 uid=998 gid=998 euid=998 suid=998 fsuid=998 egid=998 sgid=998 fsgid=998 tty=pts1 ses=21 comm="touch" exe="/usr/bin/touch" subj=unconfined key="webapps_write_attempts"ARCH=x86_64 SYSCALL=openat AUID="root" UID="zimbra" GID="zimbra" EUID="zimbra" SUID="zimbra" FSUID="zimbra" EGID="zimbra" SGID="zimbra" FSGID="zimbra"
type=CWD msg=audit(1727443142.767:1275): cwd="/opt/zimbra/jetty_base/webapps/zimbra/public"
type=PATH msg=audit(1727443142.767:1275): item=0 name="/opt/zimbra/jetty_base/webapps/zimbra/public" inode=547990 dev=fc:02 mode=040555 ouid=998 ogid=998 rdev=00:00 nametype=PARENT cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0OUID="zimbra" OGID="zimbra"
type=PROCTITLE msg=audit(1727443142.767:1275): proctitle=746F7563680075726674

Auditd is safe to install on Zimbra, the ACLs are currently being investigated by Zimbra engineering. For now ACLs do not seem to cause issues on a running Zimbra set-up. Rebooting etc. works normally. But patch updates and upgrades still need to be investigated.

You can disable ACLs if needed by running:

/usr/local/sbin/zmacl_beta disable

Further reading

Jump to: navigation, search