Цель
Настроить сервер FreeIPA (систему управления идентификацией) с использованием собственного (внешнего) корневого сертификата вместо самоподписанного.
FreeIPA (Free Identity, Policy and Audit) — это open‑source‑решение для централизованного управления идентификацией, аутентификацией и политиками доступа в Linux‑средах; оно объединяет LDAP‑каталог (389 DS), службу аутентификации Kerberos (MIT KDC), DNS‑сервер (BIND) и удостоверяющий центр (Dogtag), позволяя администрировать пользователей, группы, хосты и сервисы, настраивать политики паролей, SUDO‑правила и HBAC, а также обеспечивать доверенные соединения с другими доменами.
Архитектура после изменений:
Root CA (apatsev.corp) └── CA-сертификат для подсистемы FreeIPA/Dogtag (ipa.apatsev.corp) с вашим корпоративным сертификатом
Безопасность: Корневой ключ (
rootCA.key) используется для подписи. Для максимальной безопасности выполните этот шаг на изолированной, защищённой машине, а не на будущем сервере FreeIPA.
Подготовительный этап: Создание инфраструктуры PKI
Шаг 1: Создаём корневой сертификат (Root CA)
ВАЖНО: Этот шаг выполняется на защищённой управляющей машине, а не на сервере FreeIPA!
1.1 Создаём конфигурационный файл для Root CA:
cat > provision/files/rootCA.cnf << 'EOF' [ req ] distinguished_name = req_distinguished_name x509_extensions = v3_ca prompt = no [ req_distinguished_name ] C = RU ST = Omsk Oblast L = Omsk O = MyCompany OU = Apatsev CN = apatsev.corp Root CA [ v3_ca ] basicConstraints = critical, CA:TRUE, pathlen:1 keyUsage = critical, digitalSignature, cRLSign, keyCertSign subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer EOF
Что мы создали:
basicConstraints = CA:TRUE- указывает, что это CA-сертификатkeyUsage = keyCertSign- разрешает подписывать другие сертификатыОстальные поля - ваши организационные данные
1.2 Генерируем приватный ключ:
openssl genrsa -out provision/files/rootCA.key 4096
1.3 Создаём самоподписанный корневой сертификат:
openssl req -x509 -new -key provision/files/rootCA.key \ -sha256 -days 3650 \ -out provision/files/rootCA.crt \ -config provision/files/rootCA.cnf -extensions v3_ca
Проверяем созданный сертификат:
openssl x509 -in provision/files/rootCA.crt -text -noout | grep -A 1 "Basic Constraints\|Key Usage"
Должны увидеть:
X509v3 Basic Constraints: critical CA:TRUE X509v3 Key Usage: critical Digital Signature, Certificate Sign, CRL Sign
Шаг 2: Создаём конфигурацию для сертификата FreeIPA
2.1 Создаём файл ipa.cnf:
cat > provision/files/ipa.cnf << 'EOF' [ req ] prompt = no distinguished_name = dn req_extensions = req_ext [ dn ] CN = ipa.apatsev.corp O = MyCompany [ req_ext ] basicConstraints = critical, CA:TRUE keyUsage = critical, digitalSignature, cRLSign, keyCertSign subjectAltName = @alt_names subjectKeyIdentifier = hash [ alt_names ] DNS.1 = ipa.apatsev.corp DNS.2 = ipa EOF
Ключевые моменты:
CN = ipa.apatsev.corp- полное доменное имя сервераCA:TRUEиkeyCertSign- ОБЯЗАТЕЛЬНЫ для работы FreeIPA!subjectAltName- альтернативные DNS-имена сервера
НАСТРОЙКА ANSIBLE
Шаг 4: Создаём inventory файл
Создайте файл inventory.yml:
cat > inventory.yml << 'EOF' all: children: ipaserver: hosts: "freeipa-instance": ansible_host: "158.160.100.220" vars: ansible_user: almalinux ipaadmin_password: ADMPassword1 ipadm_password: ADMPassword1 ipaserver_no_host_dns: true ipaserver_ip_addresses: - '{{ ansible_default_ipv4.address|default(ansible_all_ipv4_addresses[0]) }}' ipaserver_domain: apatsev.corp ipaserver_realm: APATSEV.CORP ipaserver_hostname: ipa.apatsev.corp ipaserver_setup_dns: true ipaserver_forwarders: - 8.8.8.8 EOF
Шаг 5: Создаём скрипт для подписи сертификатов
Создайте файл external-ca.sh:
cat > provision/files/external-ca.sh << 'EOM' #!/bin/bash set -euo pipefail script_root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" provision_dir="$(cd "${script_root}/.." && pwd)" master="${1:-}" domain="${2:-}" usage() { cat <<EOF Usage: $0 master-fqdn domain EOF } if [ -z "$master" ]; then echo "ERROR: master is not set" usage exit 1 fi if [ -z "$domain" ]; then echo "ERROR: domain is not set" usage exit 1 fi if [[ "$master" != *".${domain}" ]]; then echo "WARNING: master (${master}) does not look like it belongs to domain ${domain}" fi csr_file="${provision_dir}/${master}-ipa.csr" if [ ! -f "$csr_file" ]; then echo "ERROR: ${csr_file} not found" exit 1 fi root_ca_key="${script_root}/rootCA.key" root_ca_crt="${script_root}/rootCA.crt" ipa_conf="${script_root}/ipa.cnf" for required in "$root_ca_key" "$root_ca_crt" "$ipa_conf"; do if [ ! -f "$required" ]; then echo "ERROR: required file ${required} is missing" exit 1 fi done signed_cert="${provision_dir}/${master}-ipa.crt" chain_file="${provision_dir}/${master}-chain.crt" openssl x509 -req \ -in "$csr_file" \ -CA "$root_ca_crt" \ -CAkey "$root_ca_key" \ -out "$signed_cert" \ -days 1825 \ -sha256 \ -extfile "$ipa_conf" \ -extensions req_ext cat "$signed_cert" "$root_ca_crt" > "$chain_file" echo "Signed ${csr_file} with ${root_ca_crt} and produced ${signed_cert} and ${chain_file}" EOM
Делаем скрипт исполняемым:
chmod +x provision/files/external-ca.sh
Шаг 6: Создаём основной playbook
Создайте файл playbook.yml:
cat > provision/playbook.yml << 'EOF' --- - name: Playbook to configure IPA server step1 hosts: ipaserver become: true collections: - freeipa.ansible_freeipa vars: ipaserver_external_ca: yes roles: - role: freeipa.ansible_freeipa.ipaserver state: present post_tasks: - name: Copy CSR /root/ipa.csr from node to "{{ groups.ipaserver[0] + '-ipa.csr' }}" ansible.builtin.fetch: src: /root/ipa.csr dest: "{{ groups.ipaserver[0] + '-ipa.csr' }}" flat: yes - name: Get /root/ipa.csr, sign with our Root CA and produce chain hosts: localhost gather_facts: false tasks: - name: Run external-ca.sh ansible.builtin.command: > /bin/bash files/external-ca.sh "{{ groups.ipaserver[0] }}" "{{ ipaserver_domain | default(groups.ipaserver[0].split('.')[1:] | join('.')) }}" args: chdir: "{{ playbook_dir }}" - name: Playbook to configure IPA server step2 hosts: ipaserver become: true collections: - freeipa.ansible_freeipa vars: ipaserver_external_cert_files: "/root/chain.crt" pre_tasks: - name: Copy "{{ groups.ipaserver[0] + '-chain.crt' }}" to /root/chain.crt on node ansible.builtin.copy: src: "{{ groups.ipaserver[0] + '-chain.crt' }}" dest: /root/chain.crt force: yes mode: preserve roles: - role: freeipa.ansible_freeipa.ipaserver state: present EOF
Запуск установки
Шаг 7: Подготовка управляющей машины
На машине, с которой будете запускать Ansible:
# Устанавливаем необходимые коллекции Ansible ansible-galaxy collection install freeipa.ansible_freeipa # Запускаем проверку подключения ansible -i inventory.yml all -m ping
Шаг 8: Запуск установки
ansible-playbook -i inventory.yml provision/playbook.yml
Что происходит по этапам:
Этап 1: FreeIPA устанавливается в режиме "внешний CA", генерируется CSR
Этап 2: Наш скрипт подписывает CSR корневым сертификатом
Этап 3: FreeIPA завершает установку с подписанным сертификатом
Проверка установки
После успешного выполнения playbook:
Проверка 1: Статус служб
$ sudo systemctl status ipa ● ipa.service - Identity, Policy, Audit Loaded: loaded (/usr/lib/systemd/system/ipa.service; enabled; preset: disabled) Active: active (exited) since Mon 2025-12-15 04:38:03 UTC; 19min ago Process: 18000 ExecStart=/usr/sbin/ipactl start (code=exited, status=0/SUCCESS) Main PID: 18000 (code=exited, status=0/SUCCESS) CPU: 1.752s Dec 15 04:38:03 ipa.apatsev.corp ipactl[18000]: Starting Directory Service Dec 15 04:38:03 ipa.apatsev.corp ipactl[18000]: Starting krb5kdc Service Dec 15 04:38:03 ipa.apatsev.corp ipactl[18000]: Starting kadmin Service Dec 15 04:38:03 ipa.apatsev.corp ipactl[18000]: Starting named Service Dec 15 04:38:03 ipa.apatsev.corp ipactl[18000]: Starting httpd Service Dec 15 04:38:03 ipa.apatsev.corp ipactl[18000]: Starting ipa-custodia Service Dec 15 04:38:03 ipa.apatsev.corp ipactl[18000]: Starting pki-tomcatd Service Dec 15 04:38:03 ipa.apatsev.corp ipactl[18000]: Starting ipa-otpd Service Dec 15 04:38:03 ipa.apatsev.corp ipactl[18000]: Starting ipa-dnskeysyncd Service Dec 15 04:38:03 ipa.apatsev.corp systemd[1]: Finished Identity, Policy, Audit.
Проверка 2: Веб-интерфейс
Откройте в браузере: https://ipa.apatsev.corp
ВНИМАНИЕ: Браузер будет ругаться на самоподписанный сертификат. Импортируйте
rootCA.crtв хранилище доверенных корневых сертификатов вашего браузера.


Проверка 3: Командная строка
# Проверяем, что мы администраторы $ kinit admin $ ipa user-find -------------- 1 user matched -------------- User login: admin Last name: Administrator Home directory: /home/admin Login shell: /bin/bash Principal name: admin@APATSEV.CORP Principal alias: admin@APATSEV.CORP, root@APATSEV.CORP UID: 1280400000 GID: 1280400000 Account disabled: False ---------------------------- Number of entries returned 1 ----------------------------
Проверка 4: Сертификат
$ openssl x509 -in /etc/ipa/ca.crt -text -noout | grep -A 1 "Issuer\|CA:TRUE" Issuer: C=RU, ST=Omsk Oblast, L=Omsk, O=MyCompany, OU=Apatsev, CN=apatsev.corp Root CA Validity -- CA:TRUE X509v3 Key Usage: critical
Типичные проблемы и решения
Проблема 1: "CA:TRUE not found in certificate"
Симптом: Установка падает на этапе 3 Решение: Убедитесь, что в ipa.cnf есть строки:
basicConstraints = critical, CA:TRUE keyUsage = critical, digitalSignature, cRLSign, keyCertSign
Проблема 2: Нет доступа к веб-интерфейсу
Решение:
Проверьте firewall:
sudo firewall-cmd --list-all sudo firewall-cmd --permanent --add-service={http,https,ldap,ldaps,kerberos} sudo firewall-cmd --reload
Проблема 3: Ошибки DNS
Решение: Добавьте в /etc/hosts на всех клиентах:
178.154.227.14 ipa.apatsev.corp ipa
Безопасность
Что делать после установки:
Защитите приватные ключи:
# На управляющей машине chmod 600 provision/files/rootCA.key
Бекап и восстановление:
# На сервере FreeIPA $ ipa-backup --data Preparing backup on ipa.apatsev.corp Local roles match globally used roles, proceeding. Stopping Directory Server Backing up ipaca in APATSEV-CORP to LDIF Backing up userRoot in APATSEV-CORP to LDIF Backing up APATSEV-CORP Starting Directory Server Backed up to /var/lib/ipa/backup/ipa-data-2025-12-15-04-54-52 The ipa-backup command was successful
$ ipa-restore /var/lib/ipa/backup/ipa-data-2025-12-15-04-54-52 Directory Manager (existing master) password: Preparing restore from /var/lib/ipa/backup/ipa-data-2025-12-15-04-54-52 on ipa.apatsev.corp Performing DATA restore from DATA backup Temporary setting umask to 022 Restoring data will overwrite existing live data. Continue to restore? [no]: yes Each master will individually need to be re-initialized or re-created from this one. The replication agreements on masters running IPA 3.1 or earlier will need to be manually re-enabled. See the man page for details. Disabling all replication. Stopping Directory Server Restoring from userRoot in APATSEV-CORP Restoring from ipaca in APATSEV-CORP Starting Directory Server Restoring umask to 18 The ipa-restore command was successful
Настройте брандмауэр:
sudo firewall-cmd --permanent --remove-service=ssh sudo firewall-cmd --reload
Чек-лист перед запуском
Hostname сервера:
ipa.apatsev.corpВсе файлы в
provision/files/:rootCA.keyrootCA.crtipa.cnfexternal-ca.sh(исполняемый)
В
inventory.ymlправильные IP и паролиНа управляющей машине установлен Ansible и коллекции
Есть SSH доступ к серверу
DNS записи или /etc/hosts настроены
Исходный код: https://github.com/patsevanton/install-freeipa-external-ca
Подписывайтесь: https://t.me/notes_devops_engineer
