Skip to content
Snippets Groups Projects
Unverified Commit 3b73ad6b authored by Thomas Woerner's avatar Thomas Woerner Committed by GitHub
Browse files

Merge pull request #971 from rjeffman/pwpolicy_update_params

pwpolicy: Add support for password check and grace limit.
parents 789d6eea 58725364
No related branches found
No related tags found
No related merge requests found
...@@ -87,6 +87,36 @@ Example playbook to ensure maxlife is set to 49 in global policy: ...@@ -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 ...@@ -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
......
---
- 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
---
- 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 @@ ...@@ -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: ...@@ -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: ...@@ -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): ...@@ -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, ...@@ -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(): ...@@ -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(): ...@@ -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(): ...@@ -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(): ...@@ -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(): ...@@ -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 @@ ...@@ -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 @@ ...@@ -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", ">=")
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment