Adding a new trusted certificate authority

In this blog post we show you how to add a custom certificate authority to the trusted certificate authorities of an OS distribution. Additional, we’ll publish an Ansible playbook to manage the trusted certificates.

Debian / Ubuntu

To trust a certificate authority on a Debian or Ubuntu system, you’ve to save your custom certificate authority file(s) to the directory /usr/local/share/ca-certificates. After that, run the binary /usr/sbin/update-ca-certificates to update the trusted certificate authority file of the OS.

Red Hat / Centos

On a Red Hat Enterprise Linux 6 system, just add your certificate authority file(s) to the directory /usr/local/share/ca-certificates. For RHEL7 use the directory /etc/pki/ca-trust/source/anchors. On both systems you have to exectue the command /bin/update-ca-trust for update the certificate authority file.

Ansible Playbook

You can manage your trusted certificates with this Ansible playbook. It should work on Red Hat Enterprise Linux / Centos 6 & 7 and Debian 7 & 8. If you would like to use it on Fedora or Ubuntu, you’ve to add some when conditions or expand the ca_path dict. After you run the playbook, the certificates will be added and the certificate authority file will be updated, so they are trusted by the OS.

---
- hosts:  all

  vars:
    ca_path_debian: /usr/local/share/ca-certificates
    ca_path:
      RedHat:
        6: /usr/local/share/ca-certificates
        7: /etc/pki/ca-trust/source/anchors
      Debian:
        7: '{{ ca_path_debian }}'
        8: '{{ ca_path_debian }}'

  tasks:
  - name: install ca package on rhel systems
    yum:
      name: ca-certificates
      state: present
    when: ansible_os_family == "RedHat"

  - name: install ca package on debian systems
    apt:
      name: ca-certificates
      state: present
      update_cache: yes
    when: ansible_os_family == "Debian"

  - name: enable dynamic ca configuration on rhel6
    shell: /bin/update-ca-trust enable
    when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int == 6

  - name: copy certificate authority to trusted ca path of the os
    copy:
      src: '{{ item }}'
      dest: '{{ ca_path[ansible_os_family][ansible_distribution_major_version|int] }}/'
      owner: root
      group: root
      mode: 0644
    with_fileglob:
      - files/ca/*
    notify:
      - update trusted ca debian
      - update trusted ca redhat

  handlers:
  - name: update trusted ca debian
    shell: /usr/sbin/update-ca-certificates
    when: ansible_os_family == "Debian"

  - name: update trusted ca redhat
    shell: /bin/update-ca-trust
    when: ansible_os_family == "RedHat"

  sudo: yes

Now you just can add your trusted certificates in the directory that is defined in the playbook, in our case files/ca.

./myca.yml
./files
./files/ca
./files/ca/confirm-subca.crt
./files/ca/confirm-rootca.crt

Verification

After you used the manual steps or run the Ansible playbook, you can verify, if your certificate authority was successfully added.

On Red Hat / Centos, please check the following file. It includes all trusted certificate authorities.

/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt

And on Debian / Ubuntu just use this file for verification.

/etc/ssl/certs/ca-certificates.crt

ansible_distribution vs ansible_os_family

In the playbook above, we use the Ansible fact ansible_os_family that differs from ansible_distribution. Every ansible_distribution is linked to an ansible_os_family. Here is the Ansible dict with the allocation of the OS distribution to the OS family.

OS_FAMILY = dict(
    RedHat = 'RedHat', Fedora = 'RedHat', CentOS = 'RedHat', Scientific = 'RedHat',
    SLC = 'RedHat', Ascendos = 'RedHat', CloudLinux = 'RedHat', PSBM = 'RedHat',
    OracleLinux = 'RedHat', OVS = 'RedHat', OEL = 'RedHat', Amazon = 'RedHat',
    XenServer = 'RedHat', Ubuntu = 'Debian', Debian = 'Debian', Raspbian = 'Debian', SLES = 'Suse',
    SLED = 'Suse', openSUSE = 'Suse', SuSE = 'Suse', Gentoo = 'Gentoo', Funtoo = 'Gentoo',
    Archlinux = 'Archlinux', Mandriva = 'Mandrake', Mandrake = 'Mandrake',
    Solaris = 'Solaris', Nexenta = 'Solaris', OmniOS = 'Solaris', OpenIndiana = 'Solaris',
    SmartOS = 'Solaris', AIX = 'AIX', Alpine = 'Alpine', MacOSX = 'Darwin',
    FreeBSD = 'FreeBSD', HPUX = 'HP-UX'
)

In case you’re running Ansible on an Ubuntu system, the facts are set like that:

ansible_distribution = Ubuntu
ansible_os_family = Debian

 

14 Comments

  • Binh Thanh Nguyen

    Thanks, nice tips

  • Lex

    Thanks, useful.

    Some fixes for 2017: It’s enough to use

    ca_path:
    RedHat: /etc/pki/ca-trust/source/anchors
    Debian: /usr/local/share/ca-certificates

    In vars without [ansible_distribution_major_version|int] in task, this will also make it easier to deal with ubuntu without adding major version for each distro and since centos 6 understands /etc/pki/ca-trust/source/anchors as well.

    Also apt/rpm can be shorted to

    – name: install ca package
    package:
    name: ca-certificates
    state: present

    instead of 1 task per rhel/debian.

    • Dominique Barton

      Thanks for the update! 🙂

  • Stephe

    Thanks for this article. I found it really helpful and was exactly what I was looking for

  • Guyen

    This is good information. Thanks. If we wanted to provide a curated list of all trusted Root CAs and Intermediate CAs to our Redhat administrators could we simply include all of them in a single .pem file or is there a better way to distribute the official trust list?

  • Vijay Yadav

    What about Amazon Linux 2, do you know about that. It also has the folder /etc/pki/ca-trust/source/anchors as REHL 7 but i have seen somewhere that for AMI 2 we have to the location /etc/pki/tls/certs. I am confused which is the correct one.

  • Oliver

    Anyone were able to get around the Redhat 7 bug with update-ca-trust which only update the /etc/pki/tls/certs/ca-bundle.trust.crt and not the ca-bundle.crt (which curl, wget and many application will use by default).

  • MDUB88

    Here’s the updated playbook. I tested it on Debian 10 and CentOS 7 and 8. It works.


    – hosts: all

    vars:
    ca_path_debian: /usr/local/share/ca-certificates
    ca_path:
    RedHat: /etc/pki/ca-trust/source/anchors
    Debian: ‘{{ ca_path_debian }}’

    tasks:
    – name: install ca package
    package:
    name: ca-certificates
    state: present

    – name: copy certificate authority to trusted ca path of the os
    copy:
    src: ‘{{ item }}’
    dest: ‘{{ ca_path[ansible_os_family] }}/’
    owner: root
    group: root
    mode: 0644
    with_fileglob:
    – files/ca/*
    notify:
    – update trusted ca debian
    – update trusted ca redhat

    handlers:
    – name: update trusted ca debian
    shell: /usr/sbin/update-ca-certificates
    when: ansible_os_family == “Debian”

    – name: update trusted ca redhat
    shell: /bin/update-ca-trust
    when: ansible_os_family == “RedHat”

    become: true

  • doula

    A big help!

  • shower renovation

    Brilliant information. You share a useful article that can use anybody.