Certmonger, in combination with FreeIPA is very handy for automatic management of TLS server certificates, if a little sparsely documented.
If you're using it with something like Apache httpd, which starts as root, reads its config/certificates/keys and then changes to a non-root user to process requests, then it all works very well out of the box.
It can be trickier to get other, less 'mature' services to consume its certificates, however. For instance, I ran into trouble when configuring iRODS to make use if Certmonger-managed certificates:
Certmonger's certificate files are owned by and only readable by root; the irods user cannot read them
Certmonger writues the server certificate and the CA certificate to separate files; iRODS wants them in a single combined chain.pem file
- iRODS can't be told to reload its configuration and/or certificates without restarting
As best I can tell, we must use a post-save command to change the owner/group/mode of the certificate files after Certmonger has created them. In addition, the command can combine the server certificate with the intermediate CA certificate to create a certificate chain file for iRODS to consume. As for reloading, there's only so much Certmonger can help us with
My first attempt was to do this as a one-liner by modifying the certificate tracking entry:
1 getcert resubmit -i name \ 2 -C $'bash -c \' 3 install -m 0400 -o irods -T /etc/pki/tls/private/irods.key /etc/irods/server.key 4 && install -m 0444 -o irods -T <(cat /etc/pki/tls/certs/irods.key /etc/pki/tls/certs/irods-CA.key) /etc/irods/chain.pem 5 && systemctl reload-or-try-restart irods.service --no-block 6 \''
That's pretty ugly, but more importantly, it doesn't work!
The reason is because SELinux policy prevents Certmonger from monkeying around with the files in /etc/irods. This can be seen with:
# ausearch -i --context certmonger_t ---- type=SYSCALL msg=audit(09/12/19 13:19:10.919:22082) : arch=x86_64 syscall=unlink success=no exit=EACCES(Permission denied) a0=0x7ffd11f1ef0f a1=0x7ffd11f1ef0f a2=0x7ffd11f1e400 a3=0x7ffd11f1df80 items=0 ppid=12201 pid=12202 auid=unset uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=unset comm=install exe=/usr/bin/install subj=system_u:system_r:certmonger_t:s0 key=(null) type=AVC msg=audit(09/12/19 13:19:10.919:22082) : avc: denied { write } for pid=12202 comm=install name=irods dev="dm-0" ino=67178532 scontext=system_u:system_r:certmonger_t:s0 tcontext=system_u:object_r:etc_t:s0 tclass=dir
i.e., certmonger_t can't remove the old /etc/irods/server.key before installing the new one.
I initially used semanage fcontext -a to set the context of server.key and chain.pem to cert_t; but the policy still prevented the file from being unlinked. That is because it lives in /etc/irods which has the etc_t context. Rather than overriding the context of that directory, I tried creating a dedicated /etc/irods/tls directory to contain both server.key and chain.pem inside it. The directory and all its files had the cert_t context.
The next hurdle was:
# ausearch -i --context certmonger_t ---- type=SYSCALL msg=audit(18/12/19 16:51:03.457:5216) : arch=x86_64 syscall=chmod success=no exit=EPERM(Operation not permitted) a0=0x7ffe6adc0f0b a1=0400 a2=0x3e1 a3=0x7ffe6adbfa20 items=0 ppid=7474 pid=7475 auid=unset uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=unset comm=install exe=/usr/bin/install subj=system_u:system_r:certmonger_t:s0 key=(null) type=AVC msg=audit(18/12/19 16:51:03.457:5216) : avc: denied { fowner } for pid=7475 comm=install capability=fowner scontext=system_u:system_r:certmonger_t:s0 tcontext=system_u:system_r:certmonger_t:s0 tclass=capability
This time, the policy prevents certmonger_t from changing the owner of the file.
I wanted to avoid having to write an SELinux policy module to e.g., create a irods_cert_t domain and allow certmonger_t to change the ownership of files with it. And even if I did, I'd still probably have to figure out how to grant certmonger_t the ability to restart iRODS, so I looked for alternatives.
Reading the man page for certmonger_selinux revealed the existence of the certmonger_unconfined_t domain. Based on its name it sounds like it would allow a process to run unconfined, which is reasonably secure for the short and simple script which we're about to write. This domain can be entered be executing a file with the certmonger_unconfined_exec_t context.
I created a script, /etc/irods/tls-post-save.sh:
I changed the context of the script to certmonger_unconfined_exec_t and updated the tracking request to use it:
# semanage fcontext -a -t certmonger_unconfined_exec_t /etc/irods/tls-post-save.sh # restorecon /etc/irods/tls-post-save.sh restorecon reset /etc/irods/tls-post-save.sh context unconfined_u:object_r:etc_t:s0->unconfined_u:object_r:certmonger_unconfined_exec_t:s0 # getcert resubmit -i irods -w -C /etc/irods/tls-post-save.sh Resubmitting "irods" to "IPA". # ls -l /etc/irods/{server.key,chain.pem} -r--r--r--. 1 irods root 3786 Dec 18 17:23 /etc/irods/chain.pem -r--------. 1 irods root 1704 Dec 18 17:23 /etc/irods/server.key
Bingo!
Thanks to a helpful comment on Red Hat's Bugzilla has lots of useful commands for exploring the active SELinux policy and determine exactly what certmonger_t is allowed to do.