Hello,

Please consider changing the AppArmor file to also #include <abstractions/ssl_keys> alongside <abstractions/ssl_certs>.


I've been troubleshooting the problem with strace, and at least one problem is that, in the AppArmor file included in the package, SSL _certificates_ are permitted, but SSL _private keys_ are not, while quassel-core needs _both_ to start up with, say, LetsEncrypt vended certificates.

Example follows. This was done inside a Debian stable-slim podman rootless container but the problem is clearly not specific to containers:

quasselcore@8984b81a04c8:~$ strace -f -otest.out /usr/bin/quasselcore --configdir=/var/lib/quassel --logfile=/var/log/quassel/core.log --loglevel=Info --port=4242 --listen=::,0.0.0.0 --require-ssl --ssl-cert=/etc/letsencrypt/live/gurdasani.com-0001/fullchain.pem --ssl-key=/etc/letsencrypt/live/gurdasani.com-0001/privkey.pem
quasselcore@8984b81a04c8:~$ grep -F privkey.pem test.out
16    statx(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/privkey.pem", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFLNK|0777, stx_size=46, ...}) = 0 16    faccessat(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/privkey.pem", F_OK) = 0 16    openat(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/privkey.pem", O_RDONLY|O_CLOEXEC) = -1 EACCES (Permission denied)
quasselcore@8984b81a04c8:~$ cat /var/log/quassel/core.log
2025-12-02 21:27:51 [Warn ] SslServer: Failed to open key file /etc/letsencrypt/live/gurdasani.com-0001/privkey.pem error: 5 2025-12-02 21:27:51 [Error] --require-ssl is set, but no SSL certificate is available. Exiting. Please see https://quassel-irc.org/faq/cert to learn how to enable SSL support.
quasselcore@8984b81a04c8:~$

dmesg reveals the problem:

[1361010.288205] audit: type=1400 audit(1764710871.342:1137): apparmor="DENIED" operation="open" class="file" profile="/usr/bin/quasselcore" name="/etc/letsencrypt/archive/gurdasani.com-0001/privkey27.pem" pid=960846 comm="quasselcore" requested_mask="r" denied_mask="r" fsuid=1002 ouid=0

(1002 is the UID under which the rootless container is running but that's not material to the issue.)

Looking at /etc/apparmor.d/usr.bin.quasselcore (on the system) we see

# Author: Felix Geyer <[email protected]>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License published by the Free Software Foundation.

#include <tunables/global>

/usr/bin/quasselcore {
  #include <abstractions/base>
  #include <abstractions/nameservice>
  #include <abstractions/user-tmp>
  #include <abstractions/ssl_certs>

  /var/lib/quassel/ rw,
  /var/lib/quassel/** rwkl,

  /var/log/quassel/* rw,

  /etc/xdg/Trolltech.conf r,
  deny /etc/xdg/Trolltech.conf k,
  /etc/ssl/openssl.cnf r,
  /usr/lib/ssl/openssl.cnf r,

  # QSysInfo::machineUniqueId()
  /var/lib/dbus/machine-id r,
  /etc/machine-id r,

  # QSysInfo::bootUniqueId()
  @{PROC}/sys/kernel/random/boot_id r,

  # Site-specific additions and overrides. See local/README for details.
  #include <local/usr.bin.quasselcore>
}


Note that abstractions/ssl_certs is included but abstractions/ssl_keys is not.

privkey.pem is present in the latter but not the former. fullchain.pem is present in the former but not the latter.

$ grep -E '(privkey|fullchain)' /etc/apparmor.d/abstractions/*
grep: /etc/apparmor.d/abstractions/apparmor_api: Is a directory
/etc/apparmor.d/abstractions/ssl_certs: /{etc,var/lib}/dehydrated/certs/*/fullchain*.pem r, /etc/apparmor.d/abstractions/ssl_certs: /etc/letsencrypt/archive/*/fullchain*.pem r, /etc/apparmor.d/abstractions/ssl_certs: /etc/certbot/archive/*/fullchain*.pem r, /etc/apparmor.d/abstractions/ssl_keys: /{etc,var/lib}/dehydrated/certs/*/privkey*.pem r, /etc/apparmor.d/abstractions/ssl_keys: /etc/letsencrypt/archive/*/privkey*.pem r, /etc/apparmor.d/abstractions/ssl_keys: /etc/certbot/archive/*/privkey*.pem r,
grep: /etc/apparmor.d/abstractions/ubuntu-browsers.d: Is a directory

Editing /etc/apparmor.d/local/usr.bin.quasselcore to contain the following

  #include <abstractions/ssl_keys>

and then restarting apparmor allows the private key to be accessed and quasselcore to start.

quasselcore@9ffdd1fecbb6:~$ strace -f -otest.out /usr/bin/quasselcore --configdir=/var/lib/quassel --logfile=/var/log/quassel/core.log --loglevel=Info --port=4242 --listen=::,0.0.0.0 --require-ssl --ssl-cert=/etc/letsencrypt/live/gurdasani.com-0001/fullchain.pem --ssl-key=/etc/letsencrypt/live/gurdasani.com-0001/privkey.pem
^Cquasselcore@9ffdd1fecbb6:~$ grep -E 'fullchain|privkey' test.out
11    statx(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/fullchain.pem", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFLNK|0777, stx_size=48, ...}) = 0 11    faccessat(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/fullchain.pem", F_OK) = 0 11    openat(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/fullchain.pem", O_RDONLY|O_CLOEXEC) = 7 11    statx(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/privkey.pem", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFLNK|0777, stx_size=46, ...}) = 0 11    faccessat(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/privkey.pem", F_OK) = 0 11    openat(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/privkey.pem", O_RDONLY|O_CLOEXEC) = 8 11    statx(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/fullchain.pem", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFLNK|0777, stx_size=48, ...}) = 0 11    faccessat(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/fullchain.pem", F_OK) = 0 11    openat(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/fullchain.pem", O_RDONLY|O_CLOEXEC) = 7 11    statx(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/privkey.pem", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFLNK|0777, stx_size=46, ...}) = 0 11    faccessat(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/privkey.pem", F_OK) = 0 11    openat(AT_FDCWD, "/etc/letsencrypt/live/gurdasani.com-0001/privkey.pem", O_RDONLY|O_CLOEXEC) = 8
quasselcore@9ffdd1fecbb6:~$ cat /var/log/quassel/core.log
2025-12-02 21:53:31 [Warn ] No storage backend selected!
2025-12-02 21:53:31 [Info ] Core is currently not configured! Please connect with a Quassel Client for basic setup. 2025-12-02 21:53:31 [Info ] Listening for GUI clients on IPv6 :: port 4242 using protocol version 10 2025-12-02 21:53:31 [Info ] Listening for GUI clients on IPv4 0.0.0.0 port 4242 using protocol version 10
2025-12-02 21:53:33 [Info ] Caught signal 2
2025-12-02 21:53:33 [Info ] Quitting...
2025-12-02 21:53:33 [Info ] Core shutting down...


Thank you,

Amit Gurdasani

Reply via email to