Compare commits
11 Commits
26dd315bc0
...
497cb6cbf5
| Author | SHA1 | Date | |
|---|---|---|---|
| 497cb6cbf5 | |||
| 7e7c8a4b2d | |||
| 8406e1eab6 | |||
| 50dcdfdfbe | |||
| 36a4ac99ed | |||
| e6e7f22592 | |||
| 570500c886 | |||
| 843ebd52a8 | |||
| 6078e8d9b9 | |||
| 6c75e2910b | |||
| 0104e69124 |
161
README.md
161
README.md
@@ -1,86 +1,145 @@
|
||||
# Ansible Role: samba_ad_dc
|
||||
|
||||
Ansible role to **install**, **provision**, and optionally **remove** a Samba Active Directory Domain Controller (AD DC) on **Debian-based systems** (e.g., Debian, Ubuntu).
|
||||
Ansible role to **install**, **provision**, and optionally **remove** a Samba Active Directory Domain Controller (AD DC) on **Debian‑based systems** (Debian, Ubuntu, etc.).
|
||||
|
||||
---
|
||||
|
||||
## ✅ Features
|
||||
|
||||
- Installs and configures Samba as an AD Domain Controller
|
||||
- Uses `samba-tool` to provision the domain
|
||||
- Idempotent: Won't re-provision if already set up
|
||||
- Reversible: Set `state: absent` to cleanly remove Samba AD DC
|
||||
- Sets up `/etc/hosts` and DNS resolver
|
||||
- Separate Kerberos configuration
|
||||
- Logging of provisioning
|
||||
- Molecule tests included for both `present` and `absent` states
|
||||
* Installs & configures Samba as an AD DC
|
||||
* Provisions the domain with `samba-tool`
|
||||
* Idempotent – skips provisioning if the domain is already present
|
||||
* Reversible – `state: absent` cleans up the DC
|
||||
* Sets up `/etc/hosts` + a **static** `/etc/resolv.conf`
|
||||
* Disables services that would otherwise overwrite `/etc/resolv.conf`
|
||||
(systemd‑resolved, `resolvconf`, `NetworkManager` DNS, `dhclient`)
|
||||
* Configures a local NTP daemon (`ntp.conf` + `ntp_signd`) for time‑sync
|
||||
* Keeps a **per‑host backup** of the Samba config files
|
||||
* Molecule tests cover both `present` and `absent` states
|
||||
|
||||
---
|
||||
|
||||
## 📦 Role Variables
|
||||
|
||||
### Main Variables
|
||||
### Core Samba / Kerberos variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|--------------------------|----------------------------------------------|----------------------|
|
||||
| `samba_ad_dc_state` | `present` to install, `absent` to remove | `present` |
|
||||
| `samba_realm` | Kerberos Realm (e.g., `EXAMPLE.COM`) | `EXAMPLE.COM` |
|
||||
| `samba_domain` | NetBIOS domain name (e.g., `EXAMPLE`) | `EXAMPLE` |
|
||||
| `samba_admin_password` | Admin password for the domain | `StrongAdminPassword123!` |
|
||||
| `samba_dns_backend` | DNS backend (`SAMBA_INTERNAL`, `BIND9_DLZ`) | `SAMBA_INTERNAL` |
|
||||
| `samba_hostname` | Hostname for the server | `inventory_hostname` |
|
||||
| Variable | Description | Default value |
|
||||
|-------------------------|---------------------------------------------|-----------------------------|
|
||||
| `samba_ad_dc_state` | `present` → install, `absent` → remove | `present` |
|
||||
| `samba_realm` | Kerberos realm (e.g., `CORP.EXAMPLE.COM`) | `EXAMPLE.COM` |
|
||||
| `samba_domain` | NetBIOS domain name (e.g., `CORP`) | `EXAMPLE` |
|
||||
| `samba_admin_password` | Password for the domain administrator | `StrongAdminPassword123!` |
|
||||
| `samba_dns_backend` | DNS backend (`SAMBA_INTERNAL`/`BIND9_DLZ`) | `SAMBA_INTERNAL` |
|
||||
| `samba_hostname` | Hostname that will appear in `smb.conf` | `inventory_hostname` |
|
||||
|
||||
### Extended configuration variables (see `defaults/main.yml`)
|
||||
|
||||
| Variable | Description | Default value |
|
||||
|-----------------------|-------------------------------------------------|---------------|
|
||||
| `samba_packages` | Packages to install when `state: present`. Includes the full list needed for a Debian/Ubuntu AD DC. | *See the YAML block below* |
|
||||
| `location_internal_dns` | Internal DNS server used in `/etc/resolv.conf` | `192.168.1.1` |
|
||||
| `location_external_dns` | External (fallback) DNS server | `8.8.8.8` |
|
||||
| `backup_path` | Directory where the role will store a backup of
|
||||
`/etc/samba`, `/etc/krb5.conf`, and other files. | `"/path/to/your/backup/directory"` |
|
||||
|
||||
> **NOTE** – the `samba_packages` block is kept for clarity, but it is *not* an
|
||||
> **Ansible variable** you set on the host; it simply holds the list that
|
||||
> `tasks/install.yml` uses.
|
||||
|
||||
### NTP configuration
|
||||
|
||||
The role also deploys an NTP daemon (file: `tasks/ntpd.yml`):
|
||||
|
||||
* **`/etc/ntp.conf`** is rendered from `templates/ntp.conf.j2`
|
||||
* The NTP service is enabled & started automatically
|
||||
* The handler `Restart ntp service` is called whenever the config changes
|
||||
|
||||
### DNS‑prep
|
||||
|
||||
`tasks/preparing.yml` performs the following important housekeeping steps
|
||||
before any Samba configuration:
|
||||
|
||||
1. Stops and disables `systemd-resolved` (if present).
|
||||
2. Removes `/etc/resolv.conf` when it is a symlink to the resolver.
|
||||
3. Creates a **static** `/etc/resolv.conf` that contains both your internal
|
||||
and external nameservers (`location_internal_dns`, `location_external_dns`).
|
||||
4. Uninstalls the `resolvconf` package (if it is installed).
|
||||
5. Prevents NetworkManager from writing DNS entries by editing
|
||||
`/etc/NetworkManager/NetworkManager.conf`.
|
||||
6. Prevents `dhclient` from touching `/etc/resolv.conf` (implementation left
|
||||
to the user – you can add a `blockinfile` task or a similar file‑lock).
|
||||
|
||||
> **Tip** – if you ever run into “no DNS resolution” on the host *after* the role has finished, double‑check that
|
||||
> `systemd-resolved` is truly disabled and that `/etc/resolv.conf` is **not** a symlink.
|
||||
|
||||
---
|
||||
|
||||
## 🧰 Example Playbook
|
||||
## 📁 Included Task Files
|
||||
|
||||
```yaml
|
||||
- hosts: samba
|
||||
become: true
|
||||
roles:
|
||||
- role: samba_ad_dc
|
||||
vars:
|
||||
samba_realm: "CORP.EXAMPLE.COM"
|
||||
samba_domain: "CORP"
|
||||
samba_admin_password: "SuperSecretPassw0rd!"
|
||||
| File | Purpose |
|
||||
|--------------------------|---------|
|
||||
| `tasks/install.yml` | Installs packages, configures NTP & DNS, then provisions Samba |
|
||||
| `tasks/remove.yml` | Stops & removes Samba packages + cleans up files |
|
||||
| `tasks/kerberos.yml` | Copies `krb5.conf.j2` → `/etc/krb5.conf` |
|
||||
| `tasks/verify.yml` | Runs `samba-tool` checks + `kinit` tests |
|
||||
| `tasks/dns_hosts.yml` | Ensures `/etc/hosts` contains the DC record |
|
||||
| `tasks/logging.yml` | Stores provisioning output to a log file |
|
||||
| `tasks/preparing.yml` | Disables `systemd-resolved`, `resolvconf`, `NetworkManager`, and creates a static `/etc/resolv.conf` |
|
||||
| `tasks/ntpd.yml` | Configures NTP (template `ntp.conf.j2`) and ensures it is running |
|
||||
|
||||
❌ Remove Samba AD DC
|
||||
- hosts: samba
|
||||
become: true
|
||||
roles:
|
||||
- role: samba_ad_dc
|
||||
vars:
|
||||
samba_ad_dc_state: absent
|
||||
---
|
||||
|
||||
📁 Included Tasks
|
||||
## 🧩 Compatibility
|
||||
|
||||
install.yml: Installs and provisions Samba AD
|
||||
* **Operating systems**: Debian 10/11/12 + , Ubuntu 20.04/22.04+
|
||||
* **Ansible**: 2.9 + (the role uses only core modules)
|
||||
|
||||
remove.yml: Stops and removes Samba AD
|
||||
---
|
||||
|
||||
kerberos.yml: Configures Kerberos (/etc/krb5.conf)
|
||||
## 🔒 Security Notes
|
||||
|
||||
verify.yml: Validates the installation (samba-tool, kinit)
|
||||
* Store `samba_admin_password` in Ansible Vault in production.
|
||||
* The role backs up the Samba configuration to the directory defined by
|
||||
`backup_path` – see `tasks/logging.yml` for how the backup is created.
|
||||
|
||||
dns_hosts.yml: Ensures /etc/hosts and DNS resolvers are set
|
||||
---
|
||||
|
||||
logging.yml: Logs provisioning output
|
||||
## 📄 Templates
|
||||
|
||||
📄 Templates
|
||||
| Template | Description |
|
||||
|----------|-------------|
|
||||
| `templates/smb.conf.j2` | Samba configuration for the AD DC |
|
||||
| `templates/krb5.conf.j2` | Kerberos client config |
|
||||
| `templates/resolv.conf.j2` | Static `/etc/resolv.conf` (search domain + nameserver) |
|
||||
| `templates/ntp.conf.j2` | NTP configuration used by `tasks/ntpd.yml` |
|
||||
|
||||
smb.conf.j2: Samba configuration
|
||||
---
|
||||
|
||||
krb5.conf.j2: Kerberos configuration
|
||||
## 🛠️ Troubleshooting
|
||||
|
||||
🔒 Security Notes
|
||||
| Issue | What to check |
|
||||
|-------|---------------|
|
||||
| `samba-tool domain provision` fails with a password error | Verify `samba_admin_password` is correct and **no‑log** (`no_log: true`) is set. |
|
||||
| DNS resolution fails on the DC itself | Ensure `samba_iface` contains `lo` **and** the NIC (e.g., `lo eth0`) and that `/etc/resolv.conf` is not a symlink. |
|
||||
| Time skew on the DC | Verify NTP service is running (`systemctl status ntp`). |
|
||||
| Backup directory missing | Check that `backup_path` is writable by the user running the playbook. |
|
||||
|
||||
Passwords should be stored in Ansible Vault for production.
|
||||
---
|
||||
|
||||
DNS and Kerberos configuration assumes internal AD DNS — adjust for external resolvers if needed.
|
||||
## 📥 Install via Ansible Galaxy
|
||||
|
||||
🧩 Compatibility
|
||||
```bash
|
||||
ansible-galaxy init samba_ad_dc
|
||||
```
|
||||
|
||||
OS: Debian 10/11/12+, Ubuntu 20.04/22.04+
|
||||
After adding this file, run:
|
||||
|
||||
Ansible: 2.9+
|
||||
```bash
|
||||
ansible-galaxy collection install samba_ad_dc
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Feel free to submit PRs – just keep the variable documentation in sync with the files you modify. Happy automating!
|
||||
@@ -1,25 +1,51 @@
|
||||
samba_ad_dc_state: present # or 'absent'
|
||||
|
||||
# AD Provisioning details
|
||||
samba_realm: "EXAMPLE.COM"
|
||||
samba_domain: "EXAMPLE"
|
||||
samba_admin_password: "StrongAdminPassword123!"
|
||||
samba_dns_backend: "SAMBA_INTERNAL"
|
||||
samba_hostname: "{{ inventory_hostname }}"
|
||||
# Hostname = DC1
|
||||
addc_hostname: "DC1"
|
||||
# DC local IP Address = 10.99.0.1
|
||||
addc_ansible_host: "10.99.0.1"
|
||||
# NetBIOS domain name (Workgroup).
|
||||
addc_netbios_domain: "SAMDOM"
|
||||
# Top level Domain = EXAMPLE.COM
|
||||
addc_tld: "EXAMPLE.COM"
|
||||
# Authentication Domain = SAMDOM.EXAMPLE.COM
|
||||
addc_auth_domain: "{{ addc_netbios_domain | upper }}.{{ addc_tld | upper }}"
|
||||
|
||||
samba_log_dir: /var/log/samba
|
||||
samba_provision_log_file: "{{ samba_log_dir }}/ad_provision.log"
|
||||
addc_admin_password: "Passw0rd"
|
||||
addc_dns_backend: "SAMBA_INTERNAL"
|
||||
addc_server_role: "dc"
|
||||
|
||||
# allows skipping verification when needed
|
||||
samba_verify: true
|
||||
addc_ip_network_prefix: "{{ addc_ansible_host.split('.')[:3] | join('.') }}"
|
||||
addc_ip_last_octet: "{{ addc_ansible_host.split('.')[-1] }}"
|
||||
addc_reverse_zone_name: "{{ addc_ip_network_prefix.split('.') | reverse | join('.') }}.in-addr.arpa"
|
||||
|
||||
samba_domain_info:
|
||||
realm: "{{ addc_auth_domain | upper }}"
|
||||
domain: "{{ addc_netbios_domain | upper }}"
|
||||
server_role: "{{ addc_server_role | default('dc') }}"
|
||||
dns_backend: "{{ addc_dns_backend | default('SAMBA_INTERNAL') }}"
|
||||
adminpass: "{{ addc_admin_password }}"
|
||||
interfaces: "{{ samba_iface_list | join(' ') }}"
|
||||
bind_interfaces_only: "yes"
|
||||
|
||||
samba_iface_list:
|
||||
- lo
|
||||
- eth0
|
||||
|
||||
samba_packages:
|
||||
- acl
|
||||
- attr
|
||||
- samba
|
||||
- winbind
|
||||
- libpam-winbind
|
||||
- libnss-winbind
|
||||
- krb5-config
|
||||
- krb5-user
|
||||
- dnsutils
|
||||
- python3-setproctitle
|
||||
# - smbclient
|
||||
|
||||
# template for /etc/resolv.conf
|
||||
samba_dns_nameservers:
|
||||
- 127.0.0.1
|
||||
- 8.8.8.8
|
||||
samba_resolv_conf_backup_path: /etc/resolv.conf.ansible.bak
|
||||
|
||||
# Internal state tracking
|
||||
samba_samdb_path: "/var/lib/samba/private/sam.ldb"
|
||||
samba_conf_path: "/etc/samba/smb.conf"
|
||||
location_internal_dns: 192.168.1.1
|
||||
location_external_dns: 8.8.8.8
|
||||
|
||||
backup_path: "/path/to/your/backup/directory"
|
||||
@@ -1,5 +1,15 @@
|
||||
---
|
||||
- name: Restart NetworkManager
|
||||
ansible.builtin.systemd:
|
||||
name: NetworkManager
|
||||
state: restarted
|
||||
|
||||
- name: Restart Samba AD DC
|
||||
service:
|
||||
ansible.builtin.service:
|
||||
name: samba-ad-dc
|
||||
state: restarted
|
||||
|
||||
- name: Restart ntp service
|
||||
ansible.builtin.service:
|
||||
name: ntp
|
||||
state: restarted
|
||||
12
meta/main.yml
Normal file
12
meta/main.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: Jose
|
||||
description: Setup Samba AD DC
|
||||
# license: MIT
|
||||
# min_ansible_version: 2.9
|
||||
# platforms:
|
||||
# - name: Debian
|
||||
# versions:
|
||||
# - buster
|
||||
# - bullseye
|
||||
dependencies: []
|
||||
56
tasks/0backupcheck.yml
Normal file
56
tasks/0backupcheck.yml
Normal 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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -1,47 +1,50 @@
|
||||
---
|
||||
- name: Install required packages
|
||||
apt:
|
||||
name:
|
||||
- samba
|
||||
- krb5-config
|
||||
- krb5-user
|
||||
- winbind
|
||||
- smbclient
|
||||
- dnsutils
|
||||
state: present
|
||||
update_cache: yes
|
||||
ansible.builtin.package:
|
||||
name: "{{ samba_packages }}"
|
||||
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
|
||||
|
||||
@@ -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
|
||||
@@ -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
54
tasks/ntpd.yml
Normal 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
112
tasks/preparing.yml
Normal 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 | lower }} {{ 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=[]) }}"
|
||||
@@ -1,37 +1,23 @@
|
||||
---
|
||||
---
|
||||
- name: Ensure Samba log directory exists
|
||||
file:
|
||||
path: "{{ samba_log_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
- name: check if domain already provisioned
|
||||
ansible.builtin.stat:
|
||||
path: /var/lib/samba/private/adsync.conf
|
||||
register: samba_provisioned
|
||||
|
||||
- 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={{ samba_domain_info.realm }}
|
||||
--domain={{ samba_domain_info.domain }}
|
||||
--server-role={{ samba_domain_info.server_role }}
|
||||
--dns-backend={{ samba_domain_info.dns_backend }}
|
||||
--adminpass={{ samba_domain_info.adminpass }}
|
||||
--option="interfaces={{ samba_domain_info.interfaces }}"
|
||||
--option="bind interfaces only={{ samba_domain_info.bind_interfaces_only }}"
|
||||
when: not samba_provisioned.stat.exists
|
||||
register: samba_provision_output
|
||||
changed_when: samba_provision_output.rc == 0
|
||||
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=********"
|
||||
|
||||
@@ -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
8
tasks/setupresolver.yml
Normal 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'
|
||||
189
tasks/verify.yml
189
tasks/verify.yml
@@ -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_auth_domain | lower }}
|
||||
-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_auth_domain | lower }}.
|
||||
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_auth_domain | lower }}.
|
||||
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_auth_domain | lower }}.
|
||||
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_auth_domain }} is the expected output for the reverse record
|
||||
failed_when: "'domain name pointer {{ addc_hostname | lower }}.{{ addc_auth_domain | lower }}' 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'
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
[libdefaults]
|
||||
default_realm = {{ samba_realm }}
|
||||
dns_lookup_realm = false
|
||||
dns_lookup_kdc = true
|
||||
ticket_lifetime = 24h
|
||||
forwardable = yes
|
||||
rdns = false
|
||||
|
||||
[realms]
|
||||
{{ samba_realm }} = {
|
||||
kdc = {{ samba_hostname }}
|
||||
admin_server = {{ samba_hostname }}
|
||||
}
|
||||
|
||||
[domain_realm]
|
||||
.{{ samba_realm | lower }} = {{ samba_realm }}
|
||||
{{ samba_realm | lower }} = {{ samba_realm }}
|
||||
37
templates/ntp.conf.j2
Normal file
37
templates/ntp.conf.j2
Normal file
@@ -0,0 +1,37 @@
|
||||
# This configuration file is managed by Ansible.
|
||||
# It is configured to run as an Active Directory Domain Controller.
|
||||
|
||||
# Enable kernel time discipline (important for a DC)
|
||||
tos maxclock 10
|
||||
|
||||
# Use your own local clock as a reliable fallback/default
|
||||
# Local clock. Note that is not the "localhost" address!
|
||||
server 127.127.1.0 # Undisciplined local clock
|
||||
fudge 127.127.1.0 stratum 10
|
||||
|
||||
# Where to retrieve the time from
|
||||
# Optionally, add external sources for greater accuracy (NTP pool)
|
||||
# You should choose servers close to your location or use a reliable pool.
|
||||
server 0.pool.ntp.org iburst prefer
|
||||
server 1.pool.ntp.org iburst prefer
|
||||
server 1.pool.ntp.org iburst prefer
|
||||
|
||||
# Drift file location
|
||||
driftfile /var/lib/ntp/ntp.drift
|
||||
logfile /var/log/ntp
|
||||
ntpsigndsocket {{ ntp_signd_path }}
|
||||
|
||||
# Access control
|
||||
# Default restriction: Allow clients only to query the time
|
||||
restrict default kod nomodify notrap nopeer limited mssntp
|
||||
|
||||
# No restrictions for "localhost"
|
||||
restrict 127.0.0.1
|
||||
|
||||
# Enable the time sources to only provide time to this host
|
||||
restrict 0.pool.ntp.org mask 255.255.255.255 nomodify notrap nopeer noquery
|
||||
restrict 1.pool.ntp.org mask 255.255.255.255 nomodify notrap nopeer noquery
|
||||
restrict 2.pool.ntp.org mask 255.255.255.255 nomodify notrap nopeer noquery
|
||||
|
||||
# tell NTP not to panic and exit
|
||||
tinker panic 0
|
||||
@@ -1,5 +1,3 @@
|
||||
# Managed by Ansible - Samba AD DC DNS
|
||||
|
||||
{% for ns in samba_dns_nameservers %}
|
||||
nameserver {{ ns }}
|
||||
{% endfor %}
|
||||
# Managed by Ansible - Samba AD DC DNS- DO NOT EDIT MANUALLY
|
||||
search {{ addc_auth_domain | lower }}
|
||||
nameserver {{ addc_ansible_host }}
|
||||
@@ -1,15 +0,0 @@
|
||||
[global]
|
||||
workgroup = {{ samba_domain }}
|
||||
realm = {{ samba_realm }}
|
||||
netbios name = {{ samba_hostname | upper }}
|
||||
server role = active directory domain controller
|
||||
dns forwarder = 8.8.8.8
|
||||
idmap_ldb:use rfc2307 = yes
|
||||
|
||||
[sysvol]
|
||||
path = /var/lib/samba/sysvol
|
||||
read only = no
|
||||
|
||||
[netlogon]
|
||||
path = /var/lib/samba/sysvol/{{ samba_realm | lower }}/scripts
|
||||
read only = no
|
||||
Reference in New Issue
Block a user