# AppArmor profile for MariaDB 11.8 server daemon
#
# TESTING:
# - Monitor denials: journalctl -k --since=yesterday --follow --grep="apparmor"
# - Test in complain mode first: aa-complain /etc/apparmor.d/mariadbd
# - See confinement status: aa-status | grep -C 10 mariadb
# - Check specific denials: aa-logprof
# - Test all server functionality: systemctl restart mariadb
# - Verify plugin loading, backups, replication, all packaged Galera SST
#   mechanisms (both donor + joiner), etc.
#
# CONTAINMENT ERRORS:
# - Check syslog/journal for "apparmor=denied" messages
# - Look for "operation=open" denials for file access
# - Check for "operation=exec" denials for program execution
# - Monitor /var/log/audit/audit.log if auditd is installed
#
# PROFILE UPDATE WORKFLOW:
# 1. Identify denial in logs (operation, path, requested_mask)
# 2. Add appropriate rule to this profile
# 3. Reload profile: apparmor_parser -r /etc/apparmor.d/mariadbd
# 4. Test again in complain mode
# 5. Switch to enforce mode when confident: aa-enforce /etc/apparmor.d/mariadbd
# 6. Consider adding site-specific rules to /etc/apparmor.d/local/mariadbd

# Define RUN, HOMEDIRS etc globally
include <tunables/global>

profile mariadbd /usr/sbin/mariadbd flags=(attach_disconnected) {

  # Including <abstractions/base> does not work on Ubuntu 24.04. The issue was
  # fixed upstream https://gitlab.com/apparmor/apparmor/-/merge_requests/1236
  # but has not landed on ubuntu 24.04 yet (tracking in
  # https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/2065685)
  include <abstractions/base>
  include <abstractions/nameservice>
  include <abstractions/user-tmp>
  include <abstractions/mysql>
  include <abstractions/winbind>

  # Minimal essential rules traditionally provided by abstractions/base
  # These rules are commonly needed by system services and avoid the passt inclusion
  capability chown,
  capability fowner,
  # per systemd service file is used with --memlock to lock memory beyond the usual rlimit
  capability ipc_lock,
  # Galera gu_thread_setschedparam still uses this
  capability sys_nice,
  capability kill,
  # Required to raise file-descriptor and memory limits beyond hard defaults
  capability sys_resource,
  # Required to read files owned by root (e.g., SSL keys, config files)
  # while running as unprivileged mysql user (uid 122)
  capability dac_override,
  capability dac_read_search,
  # Required for privilege dropping and user switching operations
  capability setuid,
  capability setgid,
  # Various types of network access
  network inet stream,
  network inet dgram,
  network unix stream,
  network tcp,
  # Probably excessive/unnecessary:
  #@{PROC}/sys/kernel/ngroups_max r,
  #@{PROC}/@{pid}/fd/ r,
  #@{PROC}/@{pid}/stat r,

  # Allow system resource access
  @{PROC}/@{pid}/cgroup r,
  @{PROC}/@{pid}/status r,
  @{PROC}/sys/kernel/core_pattern r,
  @{sys}/block/** r,
  @{sys}/block/*/queue/** r,
  @{sys}/devices/**/queue/** r,
  @{sys}/devices/system/cpu/ r,
  @{sys}/devices/system/node/ r,
  @{sys}/devices/system/node/** r,

  # Allow config access
  /etc/ r,
  /etc/gai.conf r,
  /etc/group r,
  /etc/hosts.allow r,
  /etc/hosts.deny r,
  /etc/ld.so.cache r,
  /etc/letsencrypt/archive/*/**  r,
  /etc/letsencrypt/live/*/privkey.pem    r,
  /etc/mariadb/** r,
  /etc/mtab r,
  /etc/my.cnf r,
  /etc/mysql/** r,
  /etc/nsswitch.conf r,
  /etc/passwd r,
  /etc/services r,
  /etc/ssl/openssl.cnf r,
  /etc/systemd/system/mariadb.service.d/ r,
  /etc/systemd/system/mariadb.service.d/** r,

  # Allow pid, socket, socket lock file access
  /run/mariadbd/** rw,
  /run/mysqld/** rw,

  # Allow systemd notify messages
  /run/systemd/notify w,
  /run/systemd/private rw,

  # Allow execution of server binary
  /usr/sbin/mariadbd mr,
  /usr/sbin/mysqld mr,

  # Allow plugin access
  /usr/lib/{@{multiarch},mysql}/mariadb{,d}/plugin/ r,
  /usr/lib/{@{multiarch},mysql}/mariadb{,d}/plugin/*.so* mr,
  /usr/lib/mysql/plugin/ r,
  /usr/lib/mysql/plugin/*.so* mr,

  # Allow error msg and charset access
  /usr/share/mariadb/ r,
  /usr/share/mariadb/** r,

  # Allow data dir access
  /var/lib/mariadb/ r,
  /var/lib/mariadb/** rwk,
  /var/lib/mysql/ r,
  /var/lib/mysql/** rwk,

  # Allow log file access
  /var/log/mariadb/ r,
  /var/log/mariadb/** rw,
  /var/log/mysql/ r,
  /var/log/mysql/** rw,

  # Allow reading process mounts info (container/filesystem detection)
  @{PROC}/@{pid}/mounts r,
  @{PROC}/@{pid}/mountinfo r,
  @{PROC}/@{pid}/limits r,

  # Allow memory mapping limit introspection
  @{PROC}/sys/vm/mmap_min_addr r,

  # Allow shared memory access
  /{run,dev}/shm/** rw,

  # Allow cgroup hierarchy detection
  @{PROC}/cgroups r,

  # Allow cgroup v2 detection and memory limit introspection
  @{sys}/fs/cgroup/cgroup.controllers r,
  @{sys}/fs/cgroup/**/cpu.max r,
  @{sys}/fs/cgroup/**/memory.max r,
  @{sys}/fs/cgroup/**/memory.pressure rw,

  # For huge/large page support
  @{sys}/kernel/mm/hugepages/ r,
  @{sys}/kernel/mm/hugepages/** r,
  @{sys}/kernel/mm/transparent_hugepage/enabled r,
  @{sys}/kernel/mm/transparent_hugepage/shmem_enabled r,

  # Innodb physical disk size introspection
  @{sys}/dev/block/*/queue/physical_block_size r,
  @{sys}/devices/**/queue/physical_block_size r,

  # Additional sys/block access for RocksDB/InnoDB disk introspection
  /sys/block/ r,
  # Both virtual block devices (e.g., dm, loop) and physical block devices
  # (e.g., sda, nvme) for disk size detection
  /sys/devices/**/block/*/dev r,

  # Allow cracklib dictionary access (for cracklib_password_check plugin)
  /var/cache/cracklib/cracklib_dict.* r,

  # Allow GSS mechanism configuration (for GSSAPI plugin)
  /etc/gss/mech.d/ r,
  /etc/gss/mech.d/* r,

  # Allow ODBC configuration (for Connect engine)
  /etc/odbc.ini r,
  /etc/odbcinst.ini r,
  /var/lib/odbc/*.ini r,
  owner @{HOME}/.odbc.ini rw,

  # Allow Java/OpenJDK configuration (for Connect engine and UDFs)
  /etc/java-*-openjdk/** r,

  # Allow network interface enumeration (for Java/Connect engine)
  @{PROC}/@{pid}/net/if_inet6 r,

  # Allow Galera helpers. This should be mostly fine since it's providing the
  # 'i' option which keeps the shells confined according to the rules of the
  # existing profile.
  /usr/bin/dash rcx,

  # Detailed profile for 'dash' so Galera can use it
  # Origin: support-files/policy/apparmor/usr.sbin.mysqld
  profile /usr/bin/dash flags=(complain) {
    #include <abstractions/base>
    #include <abstractions/bash>
    #include <abstractions/mysql>
    #include <abstractions/nameservice>
    #include <abstractions/perl>

    /usr/bin/cat rix,
    /usr/bin/dash rix,
    /usr/bin/date rix,
    /usr/bin/grep rix,
    /usr/bin/nc.openbsd rix,
    /usr/bin/netstat rix,
    /usr/bin/ps rix,
    /usr/bin/rm rix,
    /usr/bin/sed rix,
    /usr/bin/sleep rix,
    /usr/bin/tar rix,
    /usr/bin/which rix,
    /dev/tty rw,
    /etc/ld.so.cache r,
    /etc/my.cnf r,
    /proc/ r,
    /proc/*/cmdline r,
    /proc/*/fd/ r,
    /proc/*/net/dev r,
    /proc/*/net/if_inet6 r,
    /proc/*/net/tcp r,
    /proc/*/net/tcp6 r,
    /proc/*/stat r,
    /proc/*/status r,
    /proc/sys/kernel/pid_max r,
    /proc/tty/drivers r,
    /proc/uptime r,
    /proc/version r,
    /sbin/ifconfig rix,
    /sys/devices/system/cpu/ r,
    /tmp/** rw,
    /usr/bin/cut rix,
    /usr/bin/dirname rix,
    /usr/bin/gawk rix,
    /usr/bin/mysql rix,
    /usr/bin/perl rix,
    /usr/bin/seq rix,
    /usr/bin/wsrep_sst* rix,
    /usr/bin/wsrep_sst_common r,
    /usr/bin/mariabackup* rix,
    /var/lib/mysql/ r,
    /var/lib/mysql/** rw,
    /var/lib/mysql/*.log w,
    /var/lib/mysql/*.err w,

    # MariaDB additions
    ptrace peer=@{profile_name},

    /usr/bin/hostname rix,
    /usr/bin/ip rix,
    /usr/bin/mktemp rix,
    /usr/bin/ss rix,
    /usr/bin/sync rix,
    /usr/bin/touch rix,
    /usr/bin/uname rix,
    /etc/mysql/*.cnf r,
    /etc/mysql/conf.d/ r,
    /etc/mysql/conf.d/* r,
    /proc/*/attr/current r,
    /proc/*/fdinfo/* r,
    /proc/*/net/* r,
    /proc/locks r,
    /proc/sys/net/ipv4/ip_local_port_range r,
    /run/mysqld/mysqld.sock rw,
    /sbin/ip rix,
    /usr/bin/basename rix,
    /usr/bin/du rix,
    /usr/bin/find rix,
    /usr/bin/lsof rix,
    /usr/bin/my_print_defaults rix,
    /usr/bin/mysqldump rix,
    /usr/bin/pv rix,
    /usr/bin/rsync rix,
    /usr/bin/socat rix,
    /usr/bin/tail rix,
    /usr/bin/timeout rix,
    /usr/bin/xargs rix,
    /usr/bin/xbstream rix,
  }

  # Allow OS detection
  /etc/debian_version r,
  /etc/lsb-release r,
  /etc/os-release r,

  # Allow kernel version detection
  @{PROC}/version r,

  # Allow mmap of root directory (path resolution)
  / r,

  # UUID generation for internal use
  @{PROC}/sys/kernel/random/uuid r,

  # Thread name setting (required by some storage engines and for debugging)
  @{PROC}/@{pid}/task/*/comm rw,

  # Allow coredump filter configuration (for debugging)
  @{PROC}/@{pid}/coredump_filter rw,

  # Allow addr2line for stack trace symbolization on crashes
  /usr/bin/*-linux-gnu-addr2line mrix,

  # Deny invalid runbindable mount rule that causes parser errors
  # This broad deny rule should override any problematic mount operations
  # that might be included from system abstractions
  audit deny mount options=(runbindable),

  # Site-specific additions and overrides. See local/README for details.
  include if exists <local/mariadbd>
}
