Skip to content

Commit 3ba9dd7

Browse files
authored
Merge pull request #220 from mrtwnklr/jf_domain_cfg_1_rebased_with_pw_storage_and_sync
Support for authentication realms configuration
2 parents 83941db + 5d0ee15 commit 3ba9dd7

File tree

7 files changed

+223
-8
lines changed

7 files changed

+223
-8
lines changed

README.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -466,27 +466,36 @@ you need to install the `ifupdown2` package. Note that this will remove
466466
You can set realms / domains as authentication sources in the `domains.cfg` configuration file.
467467
If this file is not present, only the `Linux PAM` and `Proxmox VE authentication server` realms
468468
are available. Supported types are `pam`, `pve`, `ad` and `ldap`.
469+
It’s possible to automatically sync users and groups for LDAP-based realms (LDAP & Microsoft Active Directory) with `sync: true`.
469470
One realm should have the `default: 1` property to mark it as the default:
470471

471472
```
472473
pve_domains_cfg:
473-
- name: pam
474-
type: pam
474+
- name: pam
475+
type: pam
476+
attributes:
475477
comment: Linux PAM standard authentication
476-
- name: pve
477-
type: pve
478+
- name: pve
479+
type: pve
480+
attributes:
478481
comment: Proxmox VE authentication server
479-
- name: AD
480-
type: ad
482+
- name: ad
483+
type: ad
484+
attributes:
481485
comment: Active Directory authentication
482486
domain: yourdomain.com
483487
server1: dc01.yourdomain.com
484488
default: 1
485489
secure: 1
486490
server2: dc02.yourdomain.com
487-
- name: LDAP
488-
type: ldap
491+
- name: ldap
492+
type: ldap
493+
sync: true
494+
attributes:
495+
comment: LDAP authentication
489496
base_dn: CN=Users,dc=yourdomain,dc=com
497+
bind_dn: "uid=svc-reader,CN=Users,dc=yourdomain,dc=com"
498+
bind_password: "{{ secret_ldap_svc_reader_password }}"
490499
server1: ldap1.yourdomain.com
491500
user_attr: uid
492501
secure: 1

defaults/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pve_manage_hosts_enabled: yes
3636
pve_cluster_addr0: "{{ ansible_default_ipv4.address if ansible_default_ipv4.address is defined else ansible_default_ipv6.address if ansible_default_ipv6.address is defined }}"
3737
# pve_cluster_addr1: "{{ ansible_eth1.ipv4.address }}
3838
pve_datacenter_cfg: {}
39+
pve_domains_cfg: []
3940
pve_cluster_ha_groups: []
4041
# additional roles for your cluster (f.e. for monitoring)
4142
pve_pools: []

tasks/main.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,27 @@
249249
with_items: "{{ pve_users }}"
250250
when: "not pve_cluster_enabled | bool or (pve_cluster_enabled | bool and inventory_hostname == _init_node)"
251251

252+
- import_tasks: realms_config.yml
253+
when:
254+
- not pve_cluster_enabled | bool or (pve_cluster_enabled and inventory_hostname == groups[pve_group][0])
255+
- pve_domains_cfg | length > 0
256+
257+
- name: Select ldap-based realms with sync
258+
set_fact:
259+
pve_ldap_realms_with_sync: |
260+
{{ pve_domains_cfg | selectattr('type', 'in', ['ad', 'ldap'])
261+
| selectattr('sync', 'defined') }}
262+
263+
- name: Sync ldap-based realms
264+
include_tasks: realms_sync.yml
265+
loop: "{{ pve_ldap_realms_with_sync | flatten(levels=1) }}"
266+
loop_control:
267+
loop_var: pve_ldap_realm
268+
when:
269+
- not pve_cluster_enabled | bool or (pve_cluster_enabled and inventory_hostname == groups[pve_group][0])
270+
- pve_domains_cfg | length > 0
271+
- pve_ldap_realm.sync | bool
272+
252273
- name: Configure Proxmox ACLs
253274
proxmox_acl:
254275
path: "{{ item.path }}"

tasks/realms_config.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
- name: Check domains.cfg exists
3+
stat:
4+
path: "/etc/pve/domains.cfg"
5+
register: _domains_cfg
6+
7+
- name: Create domains.cfg if it does not exist
8+
file:
9+
path: "/etc/pve/domains.cfg"
10+
state: "touch"
11+
when:
12+
- not _domains_cfg.stat.exists
13+
14+
- name: Configure domains.cfg
15+
# The parser for domains.cfg requires a blank line after each domain,
16+
# and there's a TAB character before printing each key / value pair for a domain
17+
copy:
18+
dest: "/etc/pve/domains.cfg"
19+
owner: "root"
20+
group: "www-data"
21+
mode: "0640"
22+
content: |
23+
{% for domain in pve_domains_cfg %}
24+
{{ domain.type }}: {{ domain.name }}
25+
{% if domain.attributes %}
26+
{% for k,v in domain.attributes.items() %}
27+
{% if k != 'bind_password' %}
28+
{{ k }} {{ v }}
29+
{% endif %}
30+
{% endfor %}
31+
{% endif %}
32+
33+
{% endfor %}
34+
35+
- name: Select ldap-based realms with bind_password
36+
set_fact:
37+
pve_ldap_realms_with_bind_pw: |
38+
{{ pve_domains_cfg | selectattr('type', 'in', ['ad', 'ldap'])
39+
| selectattr('attributes.bind_password', 'defined') }}
40+
41+
- name: Ensure /etc/pve/priv/realm/ exists
42+
ansible.builtin.file:
43+
path: /etc/pve/priv/realm
44+
state: directory
45+
owner: root
46+
group: www-data
47+
mode: 0700
48+
when: pve_ldap_realms_with_bind_pw | length
49+
50+
- name: Ensure ldap-based realm secret files exists
51+
ansible.builtin.file:
52+
path: "/etc/pve/priv/realm/{{ item.name }}.pw"
53+
access_time: preserve
54+
modification_time: preserve
55+
state: touch
56+
mode: 0600
57+
with_items:
58+
- "{{ pve_ldap_realms_with_bind_pw }}"
59+
60+
- name: Update ldap-based realm secret files
61+
ansible.builtin.copy:
62+
content: "{{ item.attributes.bind_password }}"
63+
dest: "/etc/pve/priv/realm/{{ item.name }}.pw"
64+
owner: root
65+
group: www-data
66+
mode: 0600
67+
with_items:
68+
- "{{ pve_ldap_realms_with_bind_pw }}"

tasks/realms_sync.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
# expects to be called with variable pve_ldap_realm set
3+
4+
- name: Get pre-sync state of groups
5+
ansible.builtin.shell: pveum group list --output-format json-pretty
6+
register: groups_before
7+
changed_when: false
8+
9+
- name: Get pre-sync state of users
10+
ansible.builtin.shell: pveum user list --output-format json-pretty
11+
register: users_before
12+
changed_when: false
13+
14+
- name: "Sync ldap-based realm {{ pve_ldap_realm.name }}"
15+
ansible.builtin.shell: |
16+
pveum realm sync {{ pve_ldap_realm.name }}
17+
changed_when: false
18+
19+
- name: Get post-sync state of groups
20+
ansible.builtin.shell: pveum group list --output-format json-pretty
21+
register: groups_after
22+
changed_when: false
23+
24+
- name: Get post-sync state of users
25+
ansible.builtin.shell: pveum user list --output-format json-pretty
26+
register: users_after
27+
changed_when: false
28+
29+
- name: Create temporary file for pre-post-sync comparation
30+
ansible.builtin.tempfile:
31+
state: file
32+
suffix: pve_realm_sync_pre
33+
register: pre_sync_content
34+
changed_when: false
35+
36+
- name: Save pre-sync state of groups and users
37+
ansible.builtin.copy:
38+
content: |
39+
{{ groups_before.stdout | from_json | sort(attribute='groupid') | to_yaml }}
40+
{{ users_before.stdout | from_json | sort(attribute='userid') | to_yaml }}
41+
dest: "{{ pre_sync_content.path }}"
42+
changed_when: false
43+
when: not ansible_check_mode
44+
45+
- name: "Compare to post-sync state of groups and users for realm {{ pve_ldap_realm.name }}"
46+
ansible.builtin.copy:
47+
content: |
48+
{{ groups_after.stdout | from_json | sort(attribute='groupid') | to_yaml }}
49+
{{ users_after.stdout | from_json | sort(attribute='userid') | to_yaml }}
50+
dest: "{{ pre_sync_content.path }}"
51+
when: not ansible_check_mode
52+
diff: true
53+
54+
- name: Remove the temporary file for pre-post-sync comparation
55+
ansible.builtin.file:
56+
path: "{{ pre_sync_content.path }}"
57+
state: absent
58+
when: not ansible_check_mode
59+
changed_when: false

tests/group_vars/all

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,35 @@ pve_ssl_certificate: "{{ lookup('file', ssl_host_cert_path) }}"
1515
pve_cluster_enabled: yes
1616
pve_datacenter_cfg:
1717
console: xtermjs
18+
pve_domains_cfg:
19+
- name: pam
20+
type: pam
21+
attributes:
22+
comment: Linux PAM standard authentication
23+
- name: pve
24+
type: pve
25+
attributes:
26+
comment: Proxmox VE authentication server
27+
- name: ad
28+
type: ad
29+
attributes:
30+
comment: Active Directory authentication
31+
domain: yourdomain.com
32+
server1: dc01.yourdomain.com
33+
default: 1
34+
secure: 1
35+
server2: dc02.yourdomain.com
36+
- name: ldap
37+
type: ldap
38+
attributes:
39+
comment: LDAP authentication
40+
base_dn: CN=Users,dc=yourdomain,dc=com
41+
bind_dn: "uid=svc-reader,CN=Users,dc=yourdomain,dc=com"
42+
bind_password: "my-password"
43+
server1: ldap1.yourdomain.com
44+
user_attr: uid
45+
secure: 1
46+
server2: ldap2.yourdomain.com
1847
pve_cluster_ha_groups:
1948
- name: proxmox_5_01
2049
comment: "Resources on proxmox-5-01"

tests/test.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,34 @@
1616
vars:
1717
query: "([?type=='cluster'].quorate)[0]"
1818

19+
- name: Query PVE realms
20+
shell: "pvesh get /access/domains --output=json"
21+
register: _pve_realms
22+
changed_when: False
23+
24+
- name: Construct realm list
25+
set_fact:
26+
realm_list: "{{ realm_list | default([]) }} + [ '{{ item.type }}' ]"
27+
with_items: "{{ pve_domains_cfg }}"
28+
29+
- name: Check that PVE realms exist
30+
assert:
31+
that: "realm_list is subset(_pve_realms.stdout | from_json | json_query(query))"
32+
vars:
33+
query: "[*].type"
34+
run_once: True
35+
36+
- name: Check PVE realms configuration
37+
assert:
38+
that:
39+
- item.type == realm.type
40+
- item.name == realm.realm
41+
- item.attributes.comment == realm.comment
42+
vars:
43+
realm: '{{ _pve_realms.stdout | from_json
44+
| json_query("[?realm==''" + item.name + "'']") | first }}'
45+
with_items: "{{ pve_domains_cfg }}"
46+
1947
- name: Query PVE groups
2048
shell: "pvesh get /access/groups --output=json"
2149
register: _pve_groups

0 commit comments

Comments
 (0)