mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-07-25 00:44:42 +00:00
Merge pull request #971 from rjeffman/pwpolicy_update_params
pwpolicy: Add support for password check and grace limit.
This commit is contained in:
@@ -87,6 +87,36 @@ Example playbook to ensure maxlife is set to 49 in global policy:
|
|||||||
maxlife: 49
|
maxlife: 49
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Example playbook to ensure password grace period is set to 3 in global policy:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
- name: Playbook to handle pwpolicies
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
# Ensure maxlife is set to 49 in global policy
|
||||||
|
- ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
gracelimit: 3
|
||||||
|
```
|
||||||
|
|
||||||
|
Example playbook to ensure password grace period is set to unlimited in global policy:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
- name: Playbook to handle pwpolicies
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
# Ensure maxlife is set to 49 in global policy
|
||||||
|
- ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
gracelimit: -1
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Variables
|
Variables
|
||||||
=========
|
=========
|
||||||
@@ -107,6 +137,11 @@ Variable | Description | Required
|
|||||||
`maxfail` \| `krbpwdmaxfailure` | Consecutive failures before lockout. (int) | no
|
`maxfail` \| `krbpwdmaxfailure` | Consecutive failures before lockout. (int) | no
|
||||||
`failinterval` \| `krbpwdfailurecountinterval` | Period after which failure count will be reset in seconds. (int) | no
|
`failinterval` \| `krbpwdfailurecountinterval` | Period after which failure count will be reset in seconds. (int) | no
|
||||||
`lockouttime` \| `krbpwdlockoutduration` | Period for which lockout is enforced in seconds. (int) | no
|
`lockouttime` \| `krbpwdlockoutduration` | Period for which lockout is enforced in seconds. (int) | no
|
||||||
|
`maxrepeat` \| `ipapwdmaxrepeat` | Maximum number of same consecutive characters. Requires IPA 4.9+ (int) | no
|
||||||
|
`maxsequence` \| `ipapwdmaxsequence` | The maximum length of monotonic character sequences (abcd). Requires IPA 4.9+ (int) | no
|
||||||
|
`dictcheck` \| `ipapwdictcheck` | Check if the password is a dictionary word. Requires IPA 4.9+ (int) | no
|
||||||
|
`usercheck` \| `ipapwdusercheck` | Check if the password contains the username. Requires IPA 4.9+ (int) | no
|
||||||
|
`gracelimit` \| `passwordgracelimit` | Number of LDAP authentications allowed after expiration. Requires IPA 4.9.10 (int) | no
|
||||||
`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. | yes
|
`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. | yes
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
11
playbooks/pwpolicy/pwpolicy_grace_limit.yml
Normal file
11
playbooks/pwpolicy/pwpolicy_grace_limit.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Playbook to manage password policy
|
||||||
|
hosts: ipaserver
|
||||||
|
become: no
|
||||||
|
gather_facts: no
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Set password policy grace limit.
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
gracelimit: 3
|
||||||
14
playbooks/pwpolicy/pwpolicy_password_check.yml
Normal file
14
playbooks/pwpolicy/pwpolicy_password_check.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
- name: Playbook to manage password policy
|
||||||
|
hosts: ipaserver
|
||||||
|
become: no
|
||||||
|
gather_facts: no
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Set password checking parameters.
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
maxrepeat: 2
|
||||||
|
maxsequence: 3
|
||||||
|
dictcheck: yes
|
||||||
|
usercheck: yes
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
# Authors:
|
# Authors:
|
||||||
# Thomas Woerner <twoerner@redhat.com>
|
# Thomas Woerner <twoerner@redhat.com>
|
||||||
|
# Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2022 Red Hat
|
# Copyright (C) 2019-2022 Red Hat
|
||||||
# see file 'COPYING' for use and warranty information
|
# see file 'COPYING' for use and warranty information
|
||||||
@@ -88,6 +89,41 @@ options:
|
|||||||
type: int
|
type: int
|
||||||
required: false
|
required: false
|
||||||
aliases: ["krbpwdlockoutduration"]
|
aliases: ["krbpwdlockoutduration"]
|
||||||
|
maxrepeat:
|
||||||
|
description: >
|
||||||
|
Maximum number of same consecutive characters.
|
||||||
|
Requires IPA 4.9+
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
aliases: ["ipapwdmaxrepeat"]
|
||||||
|
maxsequence:
|
||||||
|
description: >
|
||||||
|
The maximum length of monotonic character sequences (abcd).
|
||||||
|
Requires IPA 4.9+
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
aliases: ["ipapwdmaxsequence"]
|
||||||
|
dictcheck:
|
||||||
|
description: >
|
||||||
|
Check if the password is a dictionary word.
|
||||||
|
Requires IPA 4.9+
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
aliases: ["ipapwdictcheck"]
|
||||||
|
usercheck:
|
||||||
|
description: >
|
||||||
|
Check if the password contains the username.
|
||||||
|
Requires IPA 4.9+
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
aliases: ["ipapwdusercheck"]
|
||||||
|
gracelimit:
|
||||||
|
description: >
|
||||||
|
Number of LDAP authentications allowed after expiration.
|
||||||
|
Requires IPA 4.10.1+
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
aliases: ["passwordgracelimit"]
|
||||||
state:
|
state:
|
||||||
description: State to ensure
|
description: State to ensure
|
||||||
type: str
|
type: str
|
||||||
@@ -95,6 +131,7 @@ options:
|
|||||||
choices: ["present", "absent"]
|
choices: ["present", "absent"]
|
||||||
author:
|
author:
|
||||||
- Thomas Woerner (@t-woerner)
|
- Thomas Woerner (@t-woerner)
|
||||||
|
- Rafael Guterres Jeffman (@rjeffman)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
EXAMPLES = """
|
EXAMPLES = """
|
||||||
@@ -135,7 +172,8 @@ def find_pwpolicy(module, name):
|
|||||||
|
|
||||||
|
|
||||||
def gen_args(maxlife, minlife, history, minclasses, minlength, priority,
|
def gen_args(maxlife, minlife, history, minclasses, minlength, priority,
|
||||||
maxfail, failinterval, lockouttime):
|
maxfail, failinterval, lockouttime, maxrepeat, maxsequence,
|
||||||
|
dictcheck, usercheck, gracelimit):
|
||||||
_args = {}
|
_args = {}
|
||||||
if maxlife is not None:
|
if maxlife is not None:
|
||||||
_args["krbmaxpwdlife"] = maxlife
|
_args["krbmaxpwdlife"] = maxlife
|
||||||
@@ -155,10 +193,47 @@ def gen_args(maxlife, minlife, history, minclasses, minlength, priority,
|
|||||||
_args["krbpwdfailurecountinterval"] = failinterval
|
_args["krbpwdfailurecountinterval"] = failinterval
|
||||||
if lockouttime is not None:
|
if lockouttime is not None:
|
||||||
_args["krbpwdlockoutduration"] = lockouttime
|
_args["krbpwdlockoutduration"] = lockouttime
|
||||||
|
if maxrepeat is not None:
|
||||||
|
_args["ipapwdmaxrepeat"] = maxrepeat
|
||||||
|
if maxsequence is not None:
|
||||||
|
_args["ipapwdmaxrsequence"] = maxsequence
|
||||||
|
if dictcheck is not None:
|
||||||
|
_args["ipapwddictcheck"] = dictcheck
|
||||||
|
if usercheck is not None:
|
||||||
|
_args["ipapwdusercheck"] = usercheck
|
||||||
|
if gracelimit is not None:
|
||||||
|
_args["passwordgracelimit"] = gracelimit
|
||||||
|
|
||||||
return _args
|
return _args
|
||||||
|
|
||||||
|
|
||||||
|
def check_supported_params(
|
||||||
|
module, maxrepeat, maxsequence, dictcheck, usercheck, gracelimit
|
||||||
|
):
|
||||||
|
# All password checking parameters were added by the same commit,
|
||||||
|
# so we only need to test one of them.
|
||||||
|
has_password_check = module.ipa_command_param_exists(
|
||||||
|
"pwpolicy_add", "ipapwdmaxrepeat")
|
||||||
|
# check if gracelimit is supported
|
||||||
|
has_gracelimit = module.ipa_command_param_exists(
|
||||||
|
"pwpolicy_add", "passwordgracelimit")
|
||||||
|
|
||||||
|
# If needed, report unsupported password checking paramteres
|
||||||
|
if not has_password_check:
|
||||||
|
check_password_params = [maxrepeat, maxsequence, dictcheck, usercheck]
|
||||||
|
unsupported = [
|
||||||
|
x for x in check_password_params if x is not None
|
||||||
|
]
|
||||||
|
if unsupported:
|
||||||
|
module.fail_json(
|
||||||
|
msg="Your IPA version does not support arguments: "
|
||||||
|
"maxrepeat, maxsequence, dictcheck, usercheck.")
|
||||||
|
|
||||||
|
if gracelimit is not None and not has_gracelimit:
|
||||||
|
module.fail_json(
|
||||||
|
msg="Your IPA version does not support 'gracelimit'.")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
ansible_module = IPAAnsibleModule(
|
ansible_module = IPAAnsibleModule(
|
||||||
argument_spec=dict(
|
argument_spec=dict(
|
||||||
@@ -183,6 +258,16 @@ def main():
|
|||||||
default=None),
|
default=None),
|
||||||
lockouttime=dict(type="int", aliases=["krbpwdlockoutduration"],
|
lockouttime=dict(type="int", aliases=["krbpwdlockoutduration"],
|
||||||
default=None),
|
default=None),
|
||||||
|
maxrepeat=dict(type="int", aliases=["ipapwdmaxrepeat"],
|
||||||
|
default=None),
|
||||||
|
maxsequence=dict(type="int", aliases=["ipapwdmaxsequence"],
|
||||||
|
default=None),
|
||||||
|
dictcheck=dict(type="bool", aliases=["ipapwdictcheck"],
|
||||||
|
default=None),
|
||||||
|
usercheck=dict(type="bool", aliases=["ipapwusercheck"],
|
||||||
|
default=None),
|
||||||
|
gracelimit=dict(type="int", aliases=["passwordgracelimit"],
|
||||||
|
default=None),
|
||||||
# state
|
# state
|
||||||
state=dict(type="str", default="present",
|
state=dict(type="str", default="present",
|
||||||
choices=["present", "absent"]),
|
choices=["present", "absent"]),
|
||||||
@@ -207,6 +292,11 @@ def main():
|
|||||||
maxfail = ansible_module.params_get("maxfail")
|
maxfail = ansible_module.params_get("maxfail")
|
||||||
failinterval = ansible_module.params_get("failinterval")
|
failinterval = ansible_module.params_get("failinterval")
|
||||||
lockouttime = ansible_module.params_get("lockouttime")
|
lockouttime = ansible_module.params_get("lockouttime")
|
||||||
|
maxrepeat = ansible_module.params_get("maxrepeat")
|
||||||
|
maxsequence = ansible_module.params_get("maxsequence")
|
||||||
|
dictcheck = ansible_module.params_get("dictcheck")
|
||||||
|
usercheck = ansible_module.params_get("usercheck")
|
||||||
|
gracelimit = ansible_module.params_get("gracelimit")
|
||||||
|
|
||||||
# state
|
# state
|
||||||
state = ansible_module.params_get("state")
|
state = ansible_module.params_get("state")
|
||||||
@@ -230,10 +320,16 @@ def main():
|
|||||||
msg="'global_policy' can not be made absent.")
|
msg="'global_policy' can not be made absent.")
|
||||||
invalid = ["maxlife", "minlife", "history", "minclasses",
|
invalid = ["maxlife", "minlife", "history", "minclasses",
|
||||||
"minlength", "priority", "maxfail", "failinterval",
|
"minlength", "priority", "maxfail", "failinterval",
|
||||||
"lockouttime"]
|
"lockouttime", "maxrepeat", "maxsequence", "dictcheck",
|
||||||
|
"usercheck", "gracelimit"]
|
||||||
|
|
||||||
ansible_module.params_fail_used_invalid(invalid, state)
|
ansible_module.params_fail_used_invalid(invalid, state)
|
||||||
|
|
||||||
|
if gracelimit is not None:
|
||||||
|
if gracelimit < -1:
|
||||||
|
ansible_module.fail_json(
|
||||||
|
msg="'gracelimit' must be no less than -1")
|
||||||
|
|
||||||
# Init
|
# Init
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
@@ -241,6 +337,11 @@ def main():
|
|||||||
|
|
||||||
with ansible_module.ipa_connect():
|
with ansible_module.ipa_connect():
|
||||||
|
|
||||||
|
check_supported_params(
|
||||||
|
ansible_module, maxrepeat, maxsequence, dictcheck, usercheck,
|
||||||
|
gracelimit
|
||||||
|
)
|
||||||
|
|
||||||
commands = []
|
commands = []
|
||||||
|
|
||||||
for name in names:
|
for name in names:
|
||||||
@@ -252,7 +353,8 @@ def main():
|
|||||||
# Generate args
|
# Generate args
|
||||||
args = gen_args(maxlife, minlife, history, minclasses,
|
args = gen_args(maxlife, minlife, history, minclasses,
|
||||||
minlength, priority, maxfail, failinterval,
|
minlength, priority, maxfail, failinterval,
|
||||||
lockouttime)
|
lockouttime, maxrepeat, maxsequence, dictcheck,
|
||||||
|
usercheck, gracelimit)
|
||||||
|
|
||||||
# Found the pwpolicy
|
# Found the pwpolicy
|
||||||
if res_find is not None:
|
if res_find is not None:
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
gather_facts: false
|
gather_facts: false
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
|
- name: Setup FreeIPA test facts.
|
||||||
|
import_tasks: ../env_freeipa_facts.yml
|
||||||
|
|
||||||
- name: Ensure maxlife of 90 for global_policy
|
- name: Ensure maxlife of 90 for global_policy
|
||||||
ipapwpolicy:
|
ipapwpolicy:
|
||||||
ipaadmin_password: SomeADMINpassword
|
ipaadmin_password: SomeADMINpassword
|
||||||
@@ -117,3 +120,145 @@
|
|||||||
state: absent
|
state: absent
|
||||||
register: result
|
register: result
|
||||||
failed_when: result.changed or result.failed
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: Ensure maxrepeat of 2 for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
maxrepeat: 2
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure maxrepeat of 2 for global_policy, again
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
maxrepeat: 2
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure maxrepeat of 0 for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
maxrepeat: 0
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure maxsequence of 4 for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
maxrepeat: 4
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure maxsequence of 4 for global_policy, again
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
maxrepeat: 4
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure maxsequence of 0 for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
maxrepeat: 0
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure dictcheck is set for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
dictcheck: yes
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure dictcheck is set for global_policy, again
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
dictcheck: yes
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure dictcheck is not set for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
dictcheck: no
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure usercheck is set for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
usercheck: yes
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure usercheck is set for global_policy, again
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
usercheck: yes
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure usercheck is not set for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
usercheck: no
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
when: ipa_version is version("4.9", ">=")
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: Ensure grace limit is set to 10 for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
gracelimit: 10
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure grace limit is set to 0 for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
gracelimit: 0
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure grace limit is set to 0 for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
gracelimit: 0
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure grace limit is set to 0 for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
gracelimit: -1
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Ensure grace limit is not set to -2 for global_policy
|
||||||
|
ipapwpolicy:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
gracelimit: -2
|
||||||
|
register: result
|
||||||
|
failed_when: not result.failed and "must be at least -1" not in result.msg
|
||||||
|
|
||||||
|
when: ipa_version is version("4.9.10", ">=")
|
||||||
|
|||||||
Reference in New Issue
Block a user