patch undefined: Updated the DNS configuration in the resolv.conf.j2 template to include both the local host and the Ansible-managed DNS server.

Added a new line to the `resolv.conf.j2` template to specify the Ansible-managed DNS server (`{{ addc_ansible_host }}`). This ensures that the system uses both the local host and the managed DNS server for DNS resolution.
This commit is contained in:
2025-10-19 22:25:19 +02:00
parent 0104e69124
commit 6c75e2910b
18 changed files with 549 additions and 239 deletions

56
tasks/0backupcheck.yml Normal file
View File

@@ -0,0 +1,56 @@
---
- name: Check if backup directory exists
stat:
path: "{{ backup_path }}"
register: backup_dir_stat
- name: Check if backup directory is not empty
find:
paths: "{{ backup_path }}"
file_type: any
recurse: false
when: backup_dir_stat.stat.exists and backup_dir_stat.stat.isdir
register: backup_dir_contents
- name: Check if each required file exists
stat:
path: "{{ dir_path }}/{{ item }}"
loop: "{{ backup_required_files }}"
register: required_file_stats
- name: Determine missing files
set_fact:
missing_files: >-
{{ required_file_stats.results
| selectattr('stat.exists', 'equalto', false)
| map(attribute='item')
| list }}
- name: Set fact if all required files are present
set_fact:
all_required_files_present: true
when: missing_files | length == 0
- name: Debug - Show status
debug:
msg: >-
{% if all_required_files_present | default(false) %}
All required files are present.
{% else %}
Missing required files: {{ missing_files }}
{% endif %}
- name: Set fact if backup directory exists and is not empty
set_fact:
backup_dir_valid: true
when:
- backup_dir_stat.stat.exists
- backup_dir_stat.stat.isdir
- backup_dir_contents.matched | int > 0
- all_required_files_present
- name: Debug - Show final result
debug:
msg: "Backup directory exists and is not empty."
when: backup_dir_valid | default(false)

View File

@@ -1,29 +0,0 @@
---
- name: Backup original /etc/resolv.conf if not already backed up
copy:
src: /etc/resolv.conf
dest: "{{ samba_resolv_conf_backup_path }}"
remote_src: yes
force: no
when:
- samba_ad_dc_state == "present"
- ansible_virtualization_type != "docker"
- name: Template /etc/resolv.conf with custom DNS nameservers
template:
src: resolv.conf.j2
dest: /etc/resolv.conf
owner: root
group: root
mode: '0644'
when:
- samba_ad_dc_state == "present"
- ansible_virtualization_type != "docker"
- name: Set /etc/hosts entry for Samba AD DC
lineinfile:
path: /etc/hosts
line: "{{ ansible_default_ipv4.address }} {{ samba_hostname }}.{{ samba_realm | lower }} {{ samba_hostname }}"
state: present
create: yes

View File

@@ -1,11 +0,0 @@
---
- name: Restore original /etc/resolv.conf from backup
copy:
src: "{{ samba_resolv_conf_backup_path }}"
dest: /etc/resolv.conf
remote_src: yes
force: yes
when:
- samba_ad_dc_state == "absent"
- ansible_virtualization_type != "docker"
- samba_resolv_conf_backup_path is defined

View File

@@ -1,47 +1,62 @@
---
- name: Install required packages
apt:
ansible.builtin.package:
name:
- acl
- attr
- samba
- winbind
- libpam-winbind
- libnss-winbind
- krb5-config
- krb5-user
- winbind
- smbclient
- dnsutils
state: present
update_cache: yes
- dnsutils
- python3-setproctitle
# - smbclient
- ntp
state: latest
- name: Stop samba-ad-dc before provisioning (if running)
service:
ansible.builtin.service:
name: samba-ad-dc
state: stopped
enabled: no
ignore_errors: yes
- name: Provision AD domain
include_tasks: provision.yml
- name: Deploy smb.conf
template:
src: smb.conf.j2
dest: "{{ samba_conf_path }}"
owner: root
group: root
mode: '0644'
notify: Restart Samba AD DC
- name: Enable and start samba-ad-dc service
service:
name: samba-ad-dc
state: started
enabled: yes
- name: Configure Kerberos
include_tasks: kerberos.yml
- name: Set DNS resolver and hosts entry
include_tasks: dns_hosts.yml
- name: Run verification checks
include_tasks: verify.yml
when: samba_verify | bool
# - name: Check if backup exist
# - name: Provision AD domain
# include_tasks: provision.yml
# - name: Deploy smb.conf
# ansible.builtin.template:
# src: smb.conf.j2
# dest: "{{ samba_conf_path }}"
# owner: root
# group: root
# mode: '0644'
# notify: Restart Samba AD DC
# - name: Enable and start samba-ad-dc service
# ansible.builtin.service:
# name: samba-ad-dc
# state: started
# enabled: yes
# - name: Configure Kerberos
# include_tasks: kerberos.yml
# - name: Set DNS resolver and hosts entry
# include_tasks: dns_hosts.yml
# - name: Run verification checks
# include_tasks: verify.yml
# when: samba_verify | bool

View File

@@ -1,8 +1,17 @@
---
- name: Configure Kerberos (krb5.conf)
template:
src: krb5.conf.j2
- name: Extract krb5.conf path from provision output
# The samba-tool output usually contains the path on a specific line.
# We extract the path using regex and the 'search' filter.
ansible.builtin.set_fact:
krb5_conf_path: "{{ samba_provision_output.stdout | regex_search('krb5.conf file is located at (.*)', '\\1') | first }}"
when: samba_provision_output.stdout is defined
- name: Copy krb5.conf to /etc/krb5.conf
ansible.builtin.copy:
src: "{{ krb5_conf_path }}"
dest: /etc/krb5.conf
owner: root
group: root
mode: '0644'
# Only run this if the provision was successful (changed)
when: samba_provision_output.changed

View File

@@ -1,9 +1,23 @@
---
- name: Install or remove Samba AD DC
include_tasks: install.yml
when: samba_ad_dc_state == 'present'
- name: Prepare for Samba AD DC
include_tasks: preparing.yml
- name: Remove Samba AD DC
include_tasks: remove.yml
when: samba_ad_dc_state == 'absent'
- name: Install
include_tasks: install.yml
- name: Provision
include_tasks: provision.yml
- name: Setup DNS resolver
include_tasks: setupresolver.yml
- name: Configure Kerberos
include_tasks: kerberos.yml
- name: Testing Samba AD DC
include_tasks: verify.yml
- name: Configure Time Synchronization
include_tasks: ntpd.yml

54
tasks/ntpd.yml Normal file
View File

@@ -0,0 +1,54 @@
---
- name: Ensure the ntp package is installed
ansible.builtin.package:
name: ntp
state: present
- name: Search common Samba locations for the 'ntp_signd' directory
ansible.builtin.find:
paths:
# Common paths for Samba installations
- /var/lib/samba/
- /usr/local/samba/
- /etc/samba/
pattern: ntp_signd
file_type: directory
register: find_ntp_signd
- name: Set the path variable, failing if not found
ansible.builtin.set_fact:
ntp_signd_path: "{{ find_ntp_signd.files[0].path }}"
# This conditional logic ensures the playbook stops if the directory is missing,
# or if more than one directory named 'ntp_signd' is found (which is unlikely/undesirable).
when: find_ntp_signd.matched == 1
failed_when: find_ntp_signd.matched != 1
- name: Verify permissions on the detected 'ntp_signd' directory
ansible.builtin.stat:
path: "{{ ntp_signd_path }}"
register: ntp_signd_stats
- name: Assert that the permissions allow read access
ansible.builtin.assert:
that:
# Check if the directory exists and has permissions that grant read/execute to 'other' (r-x)
- ntp_signd_stats.stat.exists
- ntp_signd_stats.stat.mode is search('[rwx-]{2}[rwx-]{2}[4-7]')
fail_msg: "FATAL: The detected ntp_signd directory ({{ ntp_signd_path }}) does not have necessary read permissions (mode: {{ ntp_signd_stats.stat.mode }})."
success_msg: "SUCCESS: Permissions on {{ ntp_signd_path }} are correctly configured."
- name: Configure ntp.conf for Active Directory Domain Controller (AD DC)
ansible.builtin.template:
src: templates/ntp.conf.j2 # Path to your NTP template file
dest: /etc/ntp.conf
owner: root
group: root
mode: '0644'
notify:
- Restart ntp service
- name: Enable and start the ntp service
ansible.builtin.service:
name: ntp
state: started
enabled: true

112
tasks/preparing.yml Normal file
View File

@@ -0,0 +1,112 @@
---
# Disable tools, such as resolvconf, that automatically update your /etc/resolv.conf DNS resolver configuration file
- name: Stop and disable systemd-resolved if present
ansible.builtin.systemd:
name: systemd-resolved
enabled: false
state: stopped
when: ansible_facts.services['systemd-resolved.service'] is defined
- name: Remove /etc/resolv.conf if it's a symlink to systemd-resolved
ansible.builtin.file:
path: /etc/resolv.conf
state: absent
when: "'/run/systemd/resolve' in ansible_facts.lsb.description | default('')"
- name: Create static /etc/resolv.conf
ansible.builtin.copy:
dest: /etc/resolv.conf
content: |
nameserver {{ location_internal_dns }}
nameserver {{ location_external_dns }}
owner: root
group: root
mode: '0644'
- name: Disable resolvconf package (if installed)
ansible.builtin.package:
name: resolvconf
state: absent
- name: Disable DNS updates from NetworkManager (if present)
ansible.builtin.blockinfile:
path: /etc/NetworkManager/NetworkManager.conf
block: |
[main]
dns=none
notify: Restart NetworkManager
when: ansible_facts.services['NetworkManager.service'] is defined
- name: Prevent dhclient from modifying resolv.conf (if present)
ansible.builtin.lineinfile:
path: /etc/dhcp/dhclient.conf
regexp: '^#?supersede domain-name-servers'
line: 'supersede domain-name-servers {{ location_internal_dns }}, {{ location_external_dns }};'
create: yes
# Verify that the /etc/hosts file on the DC correctly resolves the fully-qualified domain name (FQDN) and short host name to the LAN IP address of the DC
- name: Set /etc/hosts entry for Samba AD DC
ansible.builtin.lineinfile:
path: /etc/hosts
line: "{{ addc_ansible_host }} {{ addc_hostname | upper }}.{{ addc_tld }} {{ addc_hostname | upper }}"
state: present
create: yes
- name: Ensure '127.0.0.1 localhost' is present and nothing else on that line
ansible.builtin.lineinfile:
path: /etc/hosts
regexp: '^127\.0\.0\.1\s+'
line: '127.0.0.1 localhost'
state: present
# Remove any existing smb.conf file
- name: Get compiled default smb.conf path from smbd
ansible.builtin.shell: smbd -b | grep CONFIGFILE | awk '{print $2}'
register: smb_conf_path
changed_when: false
failed_when: smb_conf_path.rc != 0
- name: Remove smb.conf using discovered path
ansible.builtin.file:
path: "{{ smb_conf_path.stdout }}"
state: absent
# Remove all Samba database files, such as *.tdb and *.ldb files
- name: Get Samba directories from smbd -b
ansible.builtin.shell: smbd -b | egrep "LOCKDIR|STATEDIR|CACHEDIR|PRIVATE_DIR" | awk '{print $2}'
register: samba_dirs
changed_when: false
failed_when: samba_dirs.rc != 0
- name: Filter existing directories
ansible.builtin.find:
paths: "{{ item }}"
file_type: directory
recurse: no
loop: "{{ samba_dirs.stdout_lines }}"
register: existing_dirs
- name: Collect existing directories
ansible.builtin.set_fact:
valid_dirs: "{{ existing_dirs.results | selectattr('matched', '>', 0) | map(attribute='files') | sum(start=[]) | map(attribute='path') | list }}"
- name: Find *.tdb and *.ldb files
ansible.builtin.find:
paths: "{{ item }}"
patterns: "*.tdb,*.ldb"
recurse: yes
use_regex: false
loop: "{{ valid_dirs }}"
register: db_files
- name: Remove found tdb/ldb files
ansible.builtin.file:
path: "{{ item.path }}"
state: absent
loop: "{{ db_files.results | map(attribute='files') | sum(start=[]) }}"
when: item.path is defined
- name: Report removed files
ansible.builtin.debug:
msg: "Removed: {{ item.path }}"
loop: "{{ db_files.results | map(attribute='files') | sum(start=[]) }}"

View File

@@ -1,37 +1,15 @@
---
---
- name: Ensure Samba log directory exists
file:
path: "{{ samba_log_dir }}"
state: directory
owner: root
group: root
mode: '0755'
- name: Provision the Samba AD DC (with logging)
command: >
- name: Provision the Samba AD DC
ansible.builtin.command: >
samba-tool domain provision
--use-rfc2307
--realm={{ samba_realm }}
--domain={{ samba_domain }}
--server-role=dc
--dns-backend={{ samba_dns_backend }}
--adminpass={{ samba_admin_password }}
args:
creates: "{{ samba_samdb_path }}"
--realm={{ addc_auth_domain }}
--domain={{ addc_netbios_domain }}
--server-role={{ addc_server_role }}
--dns-backend={{ addc_dns_backend }}
--adminpass={{ addc_admin_password }}
--option="interfaces=lo eth0"
--option="bind interfaces only=yes"
register: samba_provision_output
no_log: false # You may toggle this if password should be hidden
- name: Write provisioning output to log
copy:
content: "{{ samba_provision_output.stdout }}"
dest: "{{ samba_provision_log_file }}"
owner: root
group: root
mode: '0644'
- name: Redact passwords in provisioning log (optional)
replace:
path: "{{ samba_provision_log_file }}"
regexp: "--adminpass=.*"
replace: "--adminpass=********"
changed_when: samba_provision_output.rc == 0
no_log: true # You may toggle this if password should be hidden

View File

@@ -1,39 +0,0 @@
---
- name: Stop Samba AD DC
service:
name: samba-ad-dc
state: stopped
enabled: no
ignore_errors: true
- name: Remove Samba configuration
file:
path: "{{ samba_conf_path }}"
state: absent
- name: Remove Samba DB and related files
file:
path: "{{ item }}"
state: absent
loop:
- /var/lib/samba
- /etc/krb5.conf
- /etc/samba
- /var/cache/samba
- /var/log/samba
- name: Remove Samba-related packages
apt:
name:
- samba
- krb5-config
- krb5-user
- winbind
- smbclient
- dnsutils
state: absent
purge: yes
autoremove: yes
- name: Restore DNS config
include_tasks: dns_hosts_restore.yml

8
tasks/setupresolver.yml Normal file
View File

@@ -0,0 +1,8 @@
---
- name: Template /etc/resolv.conf with custom DNS nameservers
template:
src: resolv.conf.j2
dest: /etc/resolv.conf
owner: root
group: root
mode: '0644'

View File

@@ -1,40 +1,165 @@
---
- name: Verify Samba AD DC setup
when: samba_verify | bool
block:
- name: Start the samba service
ansible.builtin.service:
name: samba
state: started
enabled: true
- name: Run 'samba-tool domain info'
command: samba-tool domain info 127.0.0.1
register: domain_info
changed_when: false
- name: Create the reverse DNS zone {{ addc_reverse_zone_name }}
community.general.expect:
# Note: The 'expect' module is in the 'community.general' collection
command: "samba-tool dns zonecreate {{ addc_ansible_host }} {{ addc_reverse_zone_name }} -U Administrator"
responses:
# Use the '(?i)' flag for case-insensitive matching of the prompt.
'(?i)password for.*:': "{{ addc_admin_password }}"
no_log: true # Highly recommended to prevent the password from appearing in logs
- name: Assert that the domain is provisioned
assert:
that:
- "'Netbios name' in domain_info.stdout"
- "'Server Role: ACTIVE DIRECTORY DOMAIN CONTROLLER' in domain_info.stdout"
- name: Create the PTR (reverse) DNS record
community.general.expect:
# Command syntax: samba-tool dns add <server> <zone> <record_name> PTR <target_fqdn>
command: >
samba-tool dns add {{ addc_ansible_host }}
{{ addc_reverse_zone_name }}
{{ addc_ip_last_octet }} PTR
{{ addc_hostname | lower }}.{{ addc_tld }}
-U Administrator
responses:
# Expects the standard Samba password prompt
'(?i)password for.*:': "{{ addc_admin_password }}"
no_log: true # Hide sensitive data from logs
- name: Attempt kinit with administrator
command: echo "{{ samba_admin_password }}" | kinit administrator@{{ samba_realm }}
register: kinit_result
changed_when: false
failed_when: kinit_result.rc != 0
- name: Check Kerberos ticket
command: klist
register: klist_result
changed_when: false
- name: Verify Samba file server by listing local shares
ansible.builtin.command: smbclient -L localhost -N
register: smbclient_output
changed_when: false # This is a verification step, it doesn't change the host state
- name: Assert Kerberos ticket exists
assert:
that:
- "'krbtgt/{{ samba_realm }}@{{ samba_realm }}' in klist_result.stdout"
- name: Report the results of the smbclient verification
ansible.builtin.debug:
msg: "Samba Shares found: {{ smbclient_output.stdout }}"
- name: Check Samba AD DC service status
service_facts:
- name: Verify Samba AD authentication by accessing the netlogon share
community.general.expect:
# Command to run: smbclient //localhost/netlogon -UAdministrator -c 'ls'
# The -c 'ls' command lists files on the share.
command: smbclient //localhost/netlogon -UAdministrator -c 'ls'
responses:
# Use the (?i) flag for case-insensitive matching of the prompt.
'(?i)password:': "{{ addc_admin_password }}"
no_log: true # CRITICAL: Prevents the password from being logged
register: auth_verification
changed_when: false # This is a verification/check, not a change
- name: Assert samba-ad-dc service is active
assert:
that:
- "'samba-ad-dc' in ansible_facts.services"
- ansible_facts.services['samba-ad-dc'].state == 'running'
- name: Verify LDAP Service Record (SRV _ldap._tcp)
ansible.builtin.command: host -t SRV _ldap._tcp.{{ addc_tld }}.
register: ldap_srv_check
changed_when: false
failed_when: "'has SRV record' not in ldap_srv_check.stdout"
- name: Debug - Show LDAP SRV check result
ansible.builtin.debug:
var: ldap_srv_check.stdout
- name: Verify Kerberos Service Record (SRV _kerberos._udp)
ansible.builtin.command: host -t SRV _kerberos._udp.{{ addc_tld }}.
register: kerberos_srv_check
changed_when: false
failed_when: "'has SRV record' not in kerberos_srv_check.stdout"
- name: Debug - Show Kerberos SRV check result
ansible.builtin.debug:
var: kerberos_srv_check.stdout
- name: Verify DC's A (Forward) Record
ansible.builtin.command: host -t A {{ addc_hostname | lower }}.{{ addc_tld }}.
register: a_record_check
changed_when: false
failed_when: "{{ addc_ansible_host }} not in a_record_check.stdout"
- name: Debug - Show A Record check result
ansible.builtin.debug:
var: a_record_check.stdout
- name: Verify DC's PTR (Reverse) Record
ansible.builtin.command: host -t PTR {{ addc_ansible_host }}
register: ptr_record_check
changed_when: false
# Assuming dc1.{{ addc_tld }} is the expected output for the reverse record
failed_when: "'domain name pointer {{ addc_hostname | lower }}.{{ addc_tld }}' not in ptr_record_check.stdout"
- name: Debug - Show PTR Record check result
ansible.builtin.debug:
var: ptr_record_check.stdout
- name: Verify Kerberos authentication using kinit
community.general.expect:
# Command to run: kinit administrator
command: kinit administrator
responses:
# Expects the standard Kerberos password prompt
# The (?i) flag ensures case-insensitive matching.
'(?i)password for administrator.*:': "{{ addc_admin_password }}"
no_log: true # CRITICAL: Prevents the password from being logged
register: kinit_check
changed_when: false # This is a verification/check, not a change
- name: Debug - Show kinit verification result (should be empty on success)
ansible.builtin.debug:
msg: "Kerberos kinit verification successful. Output: {{ kinit_check.stdout }}"
- name: Optional: Show the cached Kerberos ticket
ansible.builtin.command: klist
register: klist_output
changed_when: false
when: kinit_check is succeeded
- name: Debug - Show klist output
ansible.builtin.debug:
var: klist_output.stdout
when: klist_check is defined
# - name: Run 'samba-tool domain info'
# command: samba-tool domain info 127.0.0.1
# register: domain_info
# changed_when: false
# - name: Assert that the domain is provisioned
# assert:
# that:
# - "'Netbios name' in domain_info.stdout"
# - "'Server Role: ACTIVE DIRECTORY DOMAIN CONTROLLER' in domain_info.stdout"
# - name: Attempt kinit with administrator
# command: echo "{{ samba_admin_password }}" | kinit administrator@{{ samba_realm }}
# register: kinit_result
# changed_when: false
# failed_when: kinit_result.rc != 0
# - name: Check Kerberos ticket
# command: klist
# register: klist_result
# changed_when: false
# - name: Assert Kerberos ticket exists
# assert:
# that:
# - "'krbtgt/{{ samba_realm }}@{{ samba_realm }}' in klist_result.stdout"
# - name: Check Samba AD DC service status
# service_facts:
# - name: Assert samba-ad-dc service is active
# assert:
# that:
# - "'samba-ad-dc' in ansible_facts.services"
# - ansible_facts.services['samba-ad-dc'].state == 'running'