From 5a83c08f4c2362e990b2f67b54b9037abe801177 Mon Sep 17 00:00:00 2001 From: Rafael Guterres Jeffman Date: Sat, 25 Jan 2020 00:10:55 -0300 Subject: [PATCH] New service management module. There is a new service management module placed in the pluginsfolder: plugins/modules/ipaservice.py The service module allows to ensure presence and absence of services, and manage members and certificates of the service. Here is the documentation for the module: README-service.md New example playbooks have been added: playbooks/service/service-host-is-absent.yml playbooks/service/service-host-is-present.yml playbooks/service/service-is-absent.yml playbooks/service/service-is-disabled.yml playbooks/service/service-is-present-with-all-attributes.yml playbooks/service/service-is-present-without-host-object.yml playbooks/service/service-is-present.yml playbooks/service/service-member-allow_create_keytab-absent.yml playbooks/service/service-member-allow_create_keytab-present.yml playbooks/service/service-member-allow_retrieve_keytab-absent.yml playbooks/service/service-member-allow_retrieve_keytab-present.yml playbooks/service/service-member-certificate-absent.yml playbooks/service/service-member-certificate-present.yml playbooks/service/service-member-principal-absent.yml playbooks/service/service-member-principal-present.yml New tests added for the module: tests/service/test-service.yml --- README-service.md | 319 +++++++ README.md | 2 + playbooks/service/service-host-is-absent.yml | 14 + playbooks/service/service-host-is-present.yml | 13 + playbooks/service/service-is-absent.yml | 12 + playbooks/service/service-is-disabled.yml | 12 + ...service-is-present-with-all-attributes.yml | 23 + .../service-is-present-with-host-force.yml | 13 + ...service-is-present-without-host-object.yml | 12 + playbooks/service/service-is-present.yml | 11 + ...vice-member-allow_create_keytab-absent.yml | 24 + ...ice-member-allow_create_keytab-present.yml | 23 + ...ce-member-allow_retrieve_keytab-absent.yml | 24 + ...e-member-allow_retrieve_keytab-present.yml | 23 + .../service-member-certificate-absent.yml | 16 + .../service-member-certificate-present.yml | 15 + .../service-member-principal-absent.yml | 14 + .../service-member-principal-present.yml | 13 + plugins/modules/ipaservice.py | 811 ++++++++++++++++++ tests/service/certificate/cert1.der | Bin 0 -> 771 bytes tests/service/certificate/cert1.pem | 19 + tests/service/certificate/cert2.der | Bin 0 -> 771 bytes tests/service/certificate/cert2.pem | 19 + tests/service/certificate/private1.key | 28 + tests/service/certificate/private2.key | 28 + .../certificate/test_service_certificate.yml | 225 +++++ tests/service/test_service.yml | 536 ++++++++++++ .../test_service_without_skip_host_check.yml | 476 ++++++++++ 28 files changed, 2725 insertions(+) create mode 100644 README-service.md create mode 100644 playbooks/service/service-host-is-absent.yml create mode 100644 playbooks/service/service-host-is-present.yml create mode 100644 playbooks/service/service-is-absent.yml create mode 100644 playbooks/service/service-is-disabled.yml create mode 100644 playbooks/service/service-is-present-with-all-attributes.yml create mode 100644 playbooks/service/service-is-present-with-host-force.yml create mode 100644 playbooks/service/service-is-present-without-host-object.yml create mode 100644 playbooks/service/service-is-present.yml create mode 100644 playbooks/service/service-member-allow_create_keytab-absent.yml create mode 100644 playbooks/service/service-member-allow_create_keytab-present.yml create mode 100644 playbooks/service/service-member-allow_retrieve_keytab-absent.yml create mode 100644 playbooks/service/service-member-allow_retrieve_keytab-present.yml create mode 100644 playbooks/service/service-member-certificate-absent.yml create mode 100644 playbooks/service/service-member-certificate-present.yml create mode 100644 playbooks/service/service-member-principal-absent.yml create mode 100644 playbooks/service/service-member-principal-present.yml create mode 100644 plugins/modules/ipaservice.py create mode 100644 tests/service/certificate/cert1.der create mode 100644 tests/service/certificate/cert1.pem create mode 100644 tests/service/certificate/cert2.der create mode 100644 tests/service/certificate/cert2.pem create mode 100644 tests/service/certificate/private1.key create mode 100644 tests/service/certificate/private2.key create mode 100644 tests/service/certificate/test_service_certificate.yml create mode 100644 tests/service/test_service.yml create mode 100644 tests/service/test_service_without_skip_host_check.yml diff --git a/README-service.md b/README-service.md new file mode 100644 index 00000000..8125190b --- /dev/null +++ b/README-service.md @@ -0,0 +1,319 @@ +Service module +============== + +Description +----------- + +The service module allows to ensure presence and absence of services. + + +Features +-------- + +* Service management + + +Supported FreeIPA Versions +-------------------------- + +FreeIPA versions 4.4.0 and up are supported by the ipaservice module. + +Option `skip_host_check` requires FreeIPA version 4.7.0 or later. + + +Requirements +------------ + +**Controller** +* Ansible version: 2.8+ + +**Node** +* Supported FReeIPA version (see above) + + +Usage +===== + +Example inventory file + +```ini +[ipaserver] +ipaserver.test.local +``` + + +Example playbook to make sure service is present: + +```yaml +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service is present + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + certificate: + - MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAw + DzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8xDT + ALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVGFYpH + VkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk+OfEkzLUAhWBOwEraELJzM + LJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa8KvgRVRyGLoVvGbLJvmjfMXp0nIT + oTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0amnvsgparYXhypHaRLnikn0vQINt55YoEd1s + 4KrvEcD2VdZkIMPbLRu2zFvMprF3cjQQG4LT9ggfEXNIPZ1nQWAnAsu7OJEkNF+E4Mkmpc + xj9aGUVt5bsq1D+Tzj3GsidSX0nSNcZ2JltXRnL/5v63g5cZyE+nAgMBAAGjUzBRMB0GA1 + UdDgQWBBRV0j7JYukuH/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV0j7JYukuH/r/t9+Q + eNlRLXDlEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgVy1+1kNwHs + 5y1Zp0WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJPrp2/DAv1m5DtnDhBYqic + uPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfwQ5pPvKkn6WxYUYkGwIt1OH + 2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHoVOIXHwNoooyEiaio3693l6no + obyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+sKC2E8xEj9uKQ5GTWRh59VnRBVC + /SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA5yqV7MVq + pac_type: PAD + auth_ind: otp + requires_pre_auth: false + ok_as_delegate: false + ok_to_auth_as_delegate: false + skip-host-check: true + force: true +``` + + +Example playbook to make sure service is absent: + +```yaml +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service is present + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + state: absent +``` + + +Example playbook to make sure service is disabled: + +```yaml +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service is present + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + state: disabled +``` + +Example playbook to add a service even if the host object does not exist, but only if it does have a DNS entry: + +```yaml +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service is present + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + skip_host_check: true + force: false +``` + +Example playbook to add a service if it does have a DNS entry, but host object exits: + +```yaml +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service is present + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + skip_host_check: false + force: true +``` + +Example playbook to ensure service has a certificate: + +```yaml +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service member certificate is present. + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + certificate: + - MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAw + DzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8xDT + ALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVGFYpH + VkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk+OfEkzLUAhWBOwEraELJzM + LJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa8KvgRVRyGLoVvGbLJvmjfMXp0nIT + oTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0amnvsgparYXhypHaRLnikn0vQINt55YoEd1s + 4KrvEcD2VdZkIMPbLRu2zFvMprF3cjQQG4LT9ggfEXNIPZ1nQWAnAsu7OJEkNF+E4Mkmpc + xj9aGUVt5bsq1D+Tzj3GsidSX0nSNcZ2JltXRnL/5v63g5cZyE+nAgMBAAGjUzBRMB0GA1 + UdDgQWBBRV0j7JYukuH/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV0j7JYukuH/r/t9+Q + eNlRLXDlEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgVy1+1kNwHs + 5y1Zp0WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJPrp2/DAv1m5DtnDhBYqic + uPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfwQ5pPvKkn6WxYUYkGwIt1OH + 2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHoVOIXHwNoooyEiaio3693l6no + obyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+sKC2E8xEj9uKQ5GTWRh59VnRBVC + /SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA5yqV7MVq + action: member + state: present +``` + +Example playbook to add a principal to the service: + +```yaml +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Principal host/test.example.com present in service. + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + principal: host/principal.example.com + action: member +``` + +Example playbook to enable a host to manage service: + +```yaml +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure host can manage service, again. + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + host: host1.example.com + action: member +``` + +Example playbook to allow users, groups, hosts or hostgroups to create a keytab of this service: + +```yaml +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Allow users, groups, hosts or host groups to create a keytab of this service. + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + allow_create_keytab_user: + - user01 + - user02 + allow_create_keytab_group: + - group01 + - group02 + allow_create_keytab_host: + - host1.example.com + - host2.example.com + allow_create_keytab_hostgroup: + - hostgroup01 + - hostgroup02 + action: member +``` + +Example playbook to allow users, groups, hosts or hostgroups to retrieve a keytab of this service: + +```yaml +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Allow users, groups, hosts or host groups to retrieve a keytab of this service. + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + allow_retrieve_keytab_user: + - user01 + - user02 + allow_retrieve_keytab_group: + - group01 + - group02 + allow_retrieve_keytab_host: + - "{{ host1_fqdn }}" + - "{{ host2_fqdn }}" + allow_retrieve_keytab_hostgroup: + - hostgroup01 + - hostgroup02 + action: member +``` + + +Variables +--------- + +ipaservice + +Variable | Description | Required +-------- | ----------- | -------- +`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no +`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no +`name` \| `service` | The list of service name strings. | yes +`certificate` \| `usercertificate` | Base-64 encoded service certificate. | no +`pac_type` \| `ipakrbauthzdata` | Supported PAC type. It can be one of `MS-PAC`, `PAD`, or `NONE`. | no +`auth_ind` \| `krbprincipalauthind` | Defines a whitelist for Authentication Indicators. It can be any of `otp`, `radius`, `pkinit`, or `hardened`. | no +`requires_pre_auth` \| `ipakrbrequirespreauth` | Pre-authentication is required for the service. Default to true. (bool) | no +`ok_as_delegate` \| `ipakrbokasdelegate` | Client credentials may be delegated to the service. Default to false. (bool) | no +`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client. Default to false. (bool) | no +`skip_host_check` | Force service to be created even when host object does not exist to manage it. Default to false. (bool)| no +`force` | Force principal name even if host not in DNS. Default to false. (bool) | no +`host` \| `managedby_host`| Hosts that can manage the service. | no +`allow_create_keytab_user` \| `ipaallowedtoperform_write_keys_user` | Users allowed to create a keytab of this host. | no +`allow_create_keytab_group` \| `ipaallowedtoperform_write_keys_group`| Groups allowed to create a keytab of this host. | no +`allow_create_keytab_host` \| `ipaallowedtoperform_write_keys_host`| Hosts allowed to create a keytab of this host. | no +`allow_create_keytab_hostgroup` \| `ipaallowedtoperform_write_keys_group`| Host groups allowed to create a keytab of this host. | no +`allow_retrieve_keytab_user` \| `ipaallowedtoperform_read_keys_user` | Users allowed to retrieve a keytab of this host. | no +`allow_retrieve_keytab_group` \| `ipaallowedtoperform_read_keys_group` | Groups allowed to retrieve a keytab of this host. | no +`allow_retrieve_keytab_host` \| `ipaallowedtoperform_read_keys_host` | Hosts allowed to retrieve a keytab from of host. | no +`allow_retrieve_keytab_hostgroup` \| `ipaallowedtoperform_read_keys_hostgroup` | Host groups allowed to retrieve a keytab of this host. | no +`action` | Work on service or member level. It can be on of `member` or `service` and defaults to `service`. | no +`state` | The state to ensure. It can be one of `present`, `absent`, or `disabled`, default: `present`. | no + + +Authors +======= + +Rafael Jeffman diff --git a/README.md b/README.md index eea80266..f5d4c257 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Features * Modules for host management * Modules for hostgroup management * Modules for pwpolicy management +* Modules for service management * Modules for sudocmd management * Modules for sudocmdgroup management * Modules for sudorule management @@ -413,6 +414,7 @@ Modules in plugin/modules * [ipahost](README-host.md) * [ipahostgroup](README-hostgroup.md) * [ipapwpolicy](README-pwpolicy.md) +* [ipaservice](README-service.md) * [ipasudocmd](README-sudocmd.md) * [ipasudocmdgroup](README-sudocmdgroup.md) * [ipasudorule](README-sudorule.md) diff --git a/playbooks/service/service-host-is-absent.yml b/playbooks/service/service-host-is-absent.yml new file mode 100644 index 00000000..5963340f --- /dev/null +++ b/playbooks/service/service-host-is-absent.yml @@ -0,0 +1,14 @@ +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure management host is absent. + - ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + host: "{{ groups.ipaserver[0] }}" + action: member + state: absent diff --git a/playbooks/service/service-host-is-present.yml b/playbooks/service/service-host-is-present.yml new file mode 100644 index 00000000..2460051e --- /dev/null +++ b/playbooks/service/service-host-is-present.yml @@ -0,0 +1,13 @@ +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure management host is present. + - ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + host: "{{ groups.ipaserver[0] }}" + action: member diff --git a/playbooks/service/service-is-absent.yml b/playbooks/service/service-is-absent.yml new file mode 100644 index 00000000..fe65771e --- /dev/null +++ b/playbooks/service/service-is-absent.yml @@ -0,0 +1,12 @@ +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service is absent + - ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + state: absent diff --git a/playbooks/service/service-is-disabled.yml b/playbooks/service/service-is-disabled.yml new file mode 100644 index 00000000..2bf01fb1 --- /dev/null +++ b/playbooks/service/service-is-disabled.yml @@ -0,0 +1,12 @@ +--- +- name: Playbook to disable IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service is disabled + - ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + state: disabled diff --git a/playbooks/service/service-is-present-with-all-attributes.yml b/playbooks/service/service-is-present-with-all-attributes.yml new file mode 100644 index 00000000..f7e59ebc --- /dev/null +++ b/playbooks/service/service-is-present-with-all-attributes.yml @@ -0,0 +1,23 @@ +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service is present + - ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + certificate: + - MIICBjCCAW8CFHnm32VcXaUDGfEGdDL/erPSijUAMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwHhcNMjAwMTIzMDA1NjQ2WhcNMjEwMTIyMDA1NjQ2WjBCMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYrdVmsr7iT3f67DM5bb1osSEe5/c91UUMEIcFq5wrgBhzVfs8iIMDVC1yiUGTsDLJNJc4nb1tUxeR9K5fh25E6n/eWDBP75NStotjAXRU4Ahi3FNRhWFOKesds5xNqgDk5/dY8UekJv2yUblQuZzeF8b2XFrmHuCaYuFctzPfWwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACF+5RS8Ce0HRixGPu4Xd51i+Kzblg++lx8fDJ8GW5G16/Z1AsB72Hc7etJL2PksHlue/xCq6SA9fIfHc4TBNCiWjPSP1NhHJeYyoPiSkcYsqXuxWyoyRLbnAhBVvhoiqZbUt3u3tGB0uMMA0yJvj07mP7Nea2KdBYVH8X1pM0V+ + pac_type: + - MS-PAC + - PAD + auth_ind: otp + force: no + requires_pre_auth: yes + ok_as_delegate: no + ok_to_auth_as_delegate: no + action: service + state: present diff --git a/playbooks/service/service-is-present-with-host-force.yml b/playbooks/service/service-is-present-with-host-force.yml new file mode 100644 index 00000000..2268ea8f --- /dev/null +++ b/playbooks/service/service-is-present-with-host-force.yml @@ -0,0 +1,13 @@ +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service is present + - ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/ihavenodns.info + force: yes + # state: absent diff --git a/playbooks/service/service-is-present-without-host-object.yml b/playbooks/service/service-is-present-without-host-object.yml new file mode 100644 index 00000000..ddf72b8e --- /dev/null +++ b/playbooks/service/service-is-present-without-host-object.yml @@ -0,0 +1,12 @@ +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service is present + - ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.ansible.com + skip_host_check: yes diff --git a/playbooks/service/service-is-present.yml b/playbooks/service/service-is-present.yml new file mode 100644 index 00000000..06e88343 --- /dev/null +++ b/playbooks/service/service-is-present.yml @@ -0,0 +1,11 @@ +--- +- name: Playbook to manage IPA service. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service is present + - ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com diff --git a/playbooks/service/service-member-allow_create_keytab-absent.yml b/playbooks/service/service-member-allow_create_keytab-absent.yml new file mode 100644 index 00000000..d4a15ea4 --- /dev/null +++ b/playbooks/service/service-member-allow_create_keytab-absent.yml @@ -0,0 +1,24 @@ +--- +- name: Service member allow_create_keytab absent + hosts: ipaserver + become: true + + tasks: + - name: Service HTTP/www.example.com members allow_create_keytab absent for users, groups, hosts and hostgroups + ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + allow_create_keytab_user: + - user01 + - user02 + allow_create_keytab_group: + - group01 + - group02 + allow_create_keytab_host: + - host01.example.com + - host02.example.com + allow_create_keytab_hostgroup: + - hostgroup01 + - hostgroup02 + action: member + state: absent diff --git a/playbooks/service/service-member-allow_create_keytab-present.yml b/playbooks/service/service-member-allow_create_keytab-present.yml new file mode 100644 index 00000000..b28b6dc2 --- /dev/null +++ b/playbooks/service/service-member-allow_create_keytab-present.yml @@ -0,0 +1,23 @@ +--- +- name: Service member allow_create_keytab present + hosts: ipaserver + become: true + + tasks: + - name: Service HTTP/www.example.com members allow_create_keytab present for users, groups, hosts and hostgroups + ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + allow_create_keytab_user: + - user01 + - user02 + allow_create_keytab_group: + - group01 + - group02 + allow_create_keytab_host: + - host01.example.com + - host02.example.com + allow_create_keytab_hostgroup: + - hostgroup01 + - hostgroup02 + action: member diff --git a/playbooks/service/service-member-allow_retrieve_keytab-absent.yml b/playbooks/service/service-member-allow_retrieve_keytab-absent.yml new file mode 100644 index 00000000..ceada70e --- /dev/null +++ b/playbooks/service/service-member-allow_retrieve_keytab-absent.yml @@ -0,0 +1,24 @@ +--- +- name: Service member allow_retrieve_keytab absent + hosts: ipaserver + become: true + + tasks: + - name: Service HTTP/www.example.com members allow_retrieve_keytab absent for users, groups, hosts and hostgroups + ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + allow_retrieve_keytab_user: + - user01 + - user02 + allow_retrieve_keytab_group: + - group01 + - group02 + allow_retrieve_keytab_host: + - host01.example.com + - host02.example.com + allow_retrieve_keytab_hostgroup: + - hostgroup01 + - hostgroup02 + action: member + state: absent diff --git a/playbooks/service/service-member-allow_retrieve_keytab-present.yml b/playbooks/service/service-member-allow_retrieve_keytab-present.yml new file mode 100644 index 00000000..ac98904b --- /dev/null +++ b/playbooks/service/service-member-allow_retrieve_keytab-present.yml @@ -0,0 +1,23 @@ +--- +- name: Service member allow_retrieve_keytab present + hosts: ipaserver + become: true + + tasks: + - name: Service HTTP/www.example.com members allow_retrieve_keytab present for users, groups, hosts and hostgroups + ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + allow_retrieve_keytab_user: + - user01 + - user02 + allow_retrieve_keytab_group: + - group01 + - group02 + allow_retrieve_keytab_host: + - host01.example.com + - host02.example.com + allow_retrieve_keytab_hostgroup: + - hostgroup01 + - hostgroup02 + action: member diff --git a/playbooks/service/service-member-certificate-absent.yml b/playbooks/service/service-member-certificate-absent.yml new file mode 100644 index 00000000..57b71e5e --- /dev/null +++ b/playbooks/service/service-member-certificate-absent.yml @@ -0,0 +1,16 @@ +--- +- name: Service certificate absent. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service certificate is absent + - ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + + certificate: + - MIICBjCCAW8CFHnm32VcXaUDGfEGdDL/erPSijUAMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwHhcNMjAwMTIzMDA1NjQ2WhcNMjEwMTIyMDA1NjQ2WjBCMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYrdVmsr7iT3f67DM5bb1osSEe5/c91UUMEIcFq5wrgBhzVfs8iIMDVC1yiUGTsDLJNJc4nb1tUxeR9K5fh25E6n/eWDBP75NStotjAXRU4Ahi3FNRhWFOKesds5xNqgDk5/dY8UekJv2yUblQuZzeF8b2XFrmHuCaYuFctzPfWwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACF+5RS8Ce0HRixGPu4Xd51i+Kzblg++lx8fDJ8GW5G16/Z1AsB72Hc7etJL2PksHlue/xCq6SA9fIfHc4TBNCiWjPSP1NhHJeYyoPiSkcYsqXuxWyoyRLbnAhBVvhoiqZbUt3u3tGB0uMMA0yJvj07mP7Nea2KdBYVH8X1pM0V+ + action: member + state: absent diff --git a/playbooks/service/service-member-certificate-present.yml b/playbooks/service/service-member-certificate-present.yml new file mode 100644 index 00000000..bfa01d05 --- /dev/null +++ b/playbooks/service/service-member-certificate-present.yml @@ -0,0 +1,15 @@ +--- +- name: Service certificate present. + hosts: ipaserver + become: true + gather_facts: false + + tasks: + # Ensure service certificate is present + - ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + certificate: + - MIICBjCCAW8CFHnm32VcXaUDGfEGdDL/erPSijUAMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwHhcNMjAwMTIzMDA1NjQ2WhcNMjEwMTIyMDA1NjQ2WjBCMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYrdVmsr7iT3f67DM5bb1osSEe5/c91UUMEIcFq5wrgBhzVfs8iIMDVC1yiUGTsDLJNJc4nb1tUxeR9K5fh25E6n/eWDBP75NStotjAXRU4Ahi3FNRhWFOKesds5xNqgDk5/dY8UekJv2yUblQuZzeF8b2XFrmHuCaYuFctzPfWwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACF+5RS8Ce0HRixGPu4Xd51i+Kzblg++lx8fDJ8GW5G16/Z1AsB72Hc7etJL2PksHlue/xCq6SA9fIfHc4TBNCiWjPSP1NhHJeYyoPiSkcYsqXuxWyoyRLbnAhBVvhoiqZbUt3u3tGB0uMMA0yJvj07mP7Nea2KdBYVH8X1pM0V+ + action: member + state: present diff --git a/playbooks/service/service-member-principal-absent.yml b/playbooks/service/service-member-principal-absent.yml new file mode 100644 index 00000000..6bfb168c --- /dev/null +++ b/playbooks/service/service-member-principal-absent.yml @@ -0,0 +1,14 @@ +--- +- name: Service member principal absent + hosts: ipaserver + become: true + + tasks: + - name: Service HTTP/www.exmaple.com member principals host/test.exmaple.com absent + ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + principal: + - host/test.exmaple.com + action: member + state: absent diff --git a/playbooks/service/service-member-principal-present.yml b/playbooks/service/service-member-principal-present.yml new file mode 100644 index 00000000..aa94f32e --- /dev/null +++ b/playbooks/service/service-member-principal-present.yml @@ -0,0 +1,13 @@ +--- +- name: Service member principal present + hosts: ipaserver + become: true + + tasks: + - name: Service HTTP/www.exmaple.com member principals host/test.exmaple.com present + ipaservice: + ipaadmin_password: MyPassword123 + name: HTTP/www.example.com + principal: + - host/test.exmaple.com + action: member diff --git a/plugins/modules/ipaservice.py b/plugins/modules/ipaservice.py new file mode 100644 index 00000000..d3074ffe --- /dev/null +++ b/plugins/modules/ipaservice.py @@ -0,0 +1,811 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Authors: +# Rafael Guterres Jeffman +# +# Copyright (C) 2019 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +ANSIBLE_METADATA = { + "metadata_version": "1.0", + "supported_by": "community", + "status": ["preview"], +} + + +DOCUMENTATION = """ +--- +module: ipaservice +short description: Manage FreeIPA service +description: Manage FreeIPA service +options: + ipaadmin_principal: + description: The admin principal + default: admin + ipaadmin_password: + description: The admin password + required: false + name: + description: The service to manage + required: true + aliases: ["service"] + certificate: + description: Base-64 encoded service certificate. + required: false + type: list + aliases=['usercertificate'] + pac_type: + description: Supported PAC type. + required: false + choices: ["MS-PAC", "PAD", "NONE"] + type: list + aliases: ["pac_type", "ipakrbauthzdata"] + auth_ind: + description: Defines a whitelist for Authentication Indicators. + required: false + choices: ["otp", "radius", "pkinit", "hardened"] + aliases: ["krbprincipalauthind"] + skip_host_check: + description: Skip checking if host object exists. + required: False + type: bool + force: + description: Force principal name even if host is not in DNS. + required: False + type: bool + requires_pre_auth: + description: Pre-authentication is required for the service. + required: false + type: bool + default: False + aliases: ["ipakrbrequirespreauth"] + ok_as_delegate: + description: Client credentials may be delegated to the service. + required: false + type: bool + default: False + aliases: ["ipakrbokasdelegate"] + ok_to_auth_as_delegate: Allow service to authenticate on behalf of a client. + description: . + required: false + type: bool + default: False + aliases:["ipakrboktoauthasdelegate"] + principal: + description: + required: false + type: list + aliases: ["krbprincipalname"] + host: + description: Host that can manage the service. + required: false + type: list + aliases: ["managedby_host"] + allow_create_keytab_user: + descrption: Users allowed to create a keytab of this host. + required: false + type: list + aliases: ["ipaallowedtoperform_write_keys_user"] + allow_create_keytab_group: + descrption: Groups allowed to create a keytab of this host. + required: false + type: list + aliases: ["ipaallowedtoperform_write_keys_group"] + allow_create_keytab_host: + descrption: Hosts allowed to create a keytab of this host. + required: false + type: list + aliases: ["ipaallowedtoperform_write_keys_host"] + allow_create_keytab_hostgroup: + descrption: Host group allowed to create a keytab of this host. + required: false + type: list + aliases: ["ipaallowedtoperform_write_keys_hostgroup"] + allow_retrieve_keytab_user: + descrption: User allowed to retrieve a keytab of this host. + required: false + type: list + aliases: ["ipaallowedtoperform_read_keys_user"] + allow_retrieve_keytab_group: + descrption: Groups allowed to retrieve a keytab of this host. + required: false + type: list + aliases: ["ipaallowedtoperform_read_keys_group"] + allow_retrieve_keytab_host: + descrption: Hosts allowed to retrieve a keytab of this host. + required: false + type: list + aliases: ["ipaallowedtoperform_read_keys_host"] + allow_retrieve_keytab_hostgroup: + descrption: Host groups allowed to retrieve a keytab of this host. + required: false + type: list + aliases: ["ipaallowedtoperform_read_keys_hostgroup"] + action: + description: Work on service or member level + default: service + choices: ["member", "service"] + state: + description: State to ensure + default: present + choices: ["present", "absent", "enabled", "disabled"] +author: + - Rafael Jeffman +""" + +EXAMPLES = """ + # Ensure service is present + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + pac_type: + - MS-PAC + - PAD + auth_ind: otp + skip_host_check: true + force: false + requires_pre_auth: true + ok_as_delegate: false + ok_to_auth_as_delegate: false + + # Ensure service is absent + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + state: absent + + # Ensure service member certificate is present. + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + certificate: + - MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAw + DzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8xDT + ALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVGFYpH + VkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk+OfEkzLUAhWBOwEraELJzM + LJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa8KvgRVRyGLoVvGbLJvmjfMXp0nIT + oTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0amnvsgparYXhypHaRLnikn0vQINt55YoEd1s + 4KrvEcD2VdZkIMPbLRu2zFvMprF3cjQQG4LT9ggfEXNIPZ1nQWAnAsu7OJEkNF+E4Mkmpc + xj9aGUVt5bsq1D+Tzj3GsidSX0nSNcZ2JltXRnL/5v63g5cZyE+nAgMBAAGjUzBRMB0GA1 + UdDgQWBBRV0j7JYukuH/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV0j7JYukuH/r/t9+Q + eNlRLXDlEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgVy1+1kNwHs + 5y1Zp0WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJPrp2/DAv1m5DtnDhBYqic + uPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfwQ5pPvKkn6WxYUYkGwIt1OH + 2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHoVOIXHwNoooyEiaio3693l6no + obyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+sKC2E8xEj9uKQ5GTWRh59VnRBVC + /SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA5yqV7MVq + action: member + state: present + + # Ensure principal host/test.example.com present in service. + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + principal: + - host/test.example.com + action: member + + # Ensure host can manage service. + - ipaservice: + ipaadmin_password: SomeADMINpassword + name: HTTP/www.example.com + host: + - host1.example.com + - host2.example.com + action: member +""" + +RETURN = """ +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ + temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ + encode_certificate, gen_add_del_lists, module_params_get, to_text, \ + api_check_param + + +def find_service(module, name): + _args = { + "all": True, + } + + _result = api_command(module, "service_find", to_text(name), _args) + + if len(_result["result"]) > 1: + module.fail_json( + msg="There is more than one service '%s'" % (name)) + elif len(_result["result"]) == 1: + _res = _result["result"][0] + certs = _res.get("usercertificate") + if certs is not None: + _res["usercertificate"] = [encode_certificate(cert) for + cert in certs] + return _res + else: + return None + + +def gen_args(pac_type, auth_ind, skip_host_check, force, requires_pre_auth, + ok_as_delegate, ok_to_auth_as_delegate): + _args = {} + + if pac_type is not None: + _args['ipakrbauthzdata'] = pac_type + if auth_ind is not None: + _args['krbprincipalauthind'] = auth_ind + if skip_host_check is not None: + _args['skip_host_check'] = (skip_host_check) + if force is not None: + _args['force'] = (force) + if requires_pre_auth is not None: + _args['ipakrbrequirespreauth'] = (requires_pre_auth) + if ok_as_delegate is not None: + _args['ipakrbokasdelegate'] = (ok_as_delegate) + if ok_to_auth_as_delegate is not None: + _args['ipakrboktoauthasdelegate'] = (ok_to_auth_as_delegate) + + return _args + + +def check_parameters(module, state, action, names, parameters): + assert isinstance(parameters, dict) + + # invalid parameters for everything but state 'present', action 'service'. + invalid = ['pac_type', 'auth_ind', 'skip_host_check', + 'force', 'requires_pre_auth', 'ok_as_delegate', + 'ok_to_auth_as_delegate'] + + # invalid parameters when not handling service members. + invalid_not_member = \ + ['principal', 'certificate', 'host', 'allow_create_keytab_user', + 'allow_create_keytab_group', 'allow_create_keytab_host', + 'allow_create_keytab_hostgroup', 'allow_retrieve_keytab_user', + 'allow_retrieve_keytab_group', 'allow_retrieve_keytab_host', + 'allow_retrieve_keytab_hostgroup'] + + if state == 'present': + if len(names) != 1: + module.fail_json(msg="Only one service can be added at a time.") + + if action == 'service': + invalid = [] + + elif state == 'absent': + if len(names) < 1: + module.fail_json(msg="No name given.") + + if action == "service": + invalid.extend(invalid_not_member) + + elif state == 'disabled': + invalid.extend(invalid_not_member) + if action != "service": + module.fail_json( + msg="Invalid action '%s' for state '%s'" % (action, state)) + + else: + module.fail_json(msg="Invalid state '%s'" % (state)) + + for _invalid in invalid: + if parameters[_invalid] is not None: + module.fail_json( + msg="Argument '%s' can not be used with state '%s'" % + (_invalid, state)) + + +def init_ansible_module(): + ansible_module = AnsibleModule( + argument_spec=dict( + # general + ipaadmin_principal=dict(type="str", default="admin"), + ipaadmin_password=dict(type="str", required=False, no_log=True), + + name=dict(type="list", aliases=["service"], default=None, + required=True), + # service attributesstr + certificate=dict(type="list", aliases=['usercertificate'], + default=None, required=False), + principal=dict(type="list", aliases=["krbprincipalname"], + default=None), + pac_type=dict(type="list", aliases=["ipakrbauthzdata"], + choices=["MS-PAC", "PAD", "NONE"]), + auth_ind=dict(type="str", + aliases=["krbprincipalauthind"], + choices=["otp", "radius", "pkinit", "hardened"]), + skip_host_check=dict(type="bool"), + force=dict(type="bool"), + requires_pre_auth=dict( + type="bool", aliases=["ipakrbrequirespreauth"]), + ok_as_delegate=dict(type="bool", aliases=["ipakrbokasdelegate"]), + ok_to_auth_as_delegate=dict(type="bool", + aliases=["ipakrboktoauthasdelegate"]), + host=dict(type="list", aliases=["managedby_host"], required=False), + allow_create_keytab_user=dict( + type="list", required=False, + aliases=['ipaallowedtoperform_write_keys_user']), + allow_retrieve_keytab_user=dict( + type="list", required=False, + aliases=['ipaallowedtoperform_read_keys_user']), + allow_create_keytab_group=dict( + type="list", required=False, + aliases=['ipaallowedtoperform_write_keys_group']), + allow_retrieve_keytab_group=dict( + type="list", required=False, + aliases=['ipaallowedtoperform_read_keys_group']), + allow_create_keytab_host=dict( + type="list", required=False, + aliases=['ipaallowedtoperform_write_keys_host']), + allow_retrieve_keytab_host=dict( + type="list", required=False, + aliases=['ipaallowedtoperform_read_keys_host']), + allow_create_keytab_hostgroup=dict( + type="list", required=False, + aliases=['ipaallowedtoperform_write_keys_hostgroup']), + allow_retrieve_keytab_hostgroup=dict( + type="list", required=False, + aliases=['ipaallowedtoperform_read_keys_hostgroup']), + # action + action=dict(type="str", default="service", + choices=["member", "service"]), + # state + state=dict(type="str", default="present", + choices=["present", "absent", + "enabled", "disabled"]), + ), + supports_check_mode=True, + ) + + ansible_module._ansible_debug = True + + return ansible_module + + +def main(): + ansible_module = init_ansible_module() + + # Get parameters + + # general + ipaadmin_principal = module_params_get(ansible_module, + "ipaadmin_principal") + ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") + names = module_params_get(ansible_module, "name") + + # service attributes + principal = module_params_get(ansible_module, "principal") + certificate = module_params_get(ansible_module, "certificate") + pac_type = module_params_get(ansible_module, "pac_type") + auth_ind = module_params_get(ansible_module, "auth_ind") + skip_host_check = module_params_get(ansible_module, "skip_host_check") + force = module_params_get(ansible_module, "force") + requires_pre_auth = module_params_get(ansible_module, "requires_pre_auth") + ok_as_delegate = module_params_get(ansible_module, "ok_as_delegate") + ok_to_auth_as_delegate = module_params_get(ansible_module, + "ok_to_auth_as_delegate") + + host = module_params_get(ansible_module, "host") + + allow_create_keytab_user = module_params_get( + ansible_module, "allow_create_keytab_user") + allow_create_keytab_group = module_params_get( + ansible_module, "allow_create_keytab_group") + allow_create_keytab_host = module_params_get( + ansible_module, "allow_create_keytab_host") + allow_create_keytab_hostgroup = module_params_get( + ansible_module, "allow_create_keytab_hostgroup") + + allow_retrieve_keytab_user = module_params_get( + ansible_module, "allow_retrieve_keytab_user") + allow_retrieve_keytab_group = module_params_get( + ansible_module, "allow_retrieve_keytab_group") + allow_retrieve_keytab_host = module_params_get( + ansible_module, "allow_create_keytab_host") + allow_retrieve_keytab_hostgroup = module_params_get( + ansible_module, "allow_retrieve_keytab_hostgroup") + + # action + action = module_params_get(ansible_module, "action") + # state + state = module_params_get(ansible_module, "state") + + # check parameters + check_parameters(ansible_module, state, action, names, vars()) + + # Init + + changed = False + exit_args = {} + ccache_dir = None + ccache_name = None + try: + if not valid_creds(ansible_module, ipaadmin_principal): + ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, + ipaadmin_password) + api_connect() + + has_skip_host_check = api_check_param( + "service_add", "skip_host_check") + if skip_host_check and not has_skip_host_check: + ansible_module.fail_json( + msg="Skipping host check is not supported by your IPA version") + + commands = [] + + for name in names: + res_find = find_service(ansible_module, name) + + if state == "present": + if action == "service": + args = gen_args( + pac_type, auth_ind, skip_host_check, force, + requires_pre_auth, ok_as_delegate, + ok_to_auth_as_delegate) + if not has_skip_host_check and 'skip_host_check' in args: + del args['skip_host_check'] + + if res_find is None: + commands.append([name, 'service_add', args]) + + certificate_add = certificate or [] + certificate_del = [] + host_add = host or [] + host_del = [] + principal_add = principal or [] + principal_del = [] + allow_create_keytab_user_add = \ + allow_create_keytab_user or [] + allow_create_keytab_user_del = [] + allow_create_keytab_group_add = \ + allow_create_keytab_group or [] + allow_create_keytab_group_del = [] + allow_create_keytab_host_add = \ + allow_create_keytab_host or [] + allow_create_keytab_host_del = [] + allow_create_keytab_hostgroup_add = \ + allow_create_keytab_hostgroup or [] + allow_create_keytab_hostgroup_del = [] + allow_retrieve_keytab_user_add = \ + allow_retrieve_keytab_user or [] + allow_retrieve_keytab_user_del = [] + allow_retrieve_keytab_group_add = \ + allow_retrieve_keytab_group or [] + allow_retrieve_keytab_group_del = [] + allow_retrieve_keytab_host_add = \ + allow_retrieve_keytab_host or [] + allow_retrieve_keytab_host_del = [] + allow_retrieve_keytab_hostgroup_add = \ + allow_retrieve_keytab_hostgroup or [] + allow_retrieve_keytab_hostgroup_del = [] + + else: + for remove in ['skip_host_check', 'force']: + if remove in args: + del args[remove] + + if not compare_args_ipa(ansible_module, args, + res_find): + commands.append([name, "service_mod", args]) + + certificate_add, certificate_del = gen_add_del_lists( + certificate, res_find.get("usercertificate")) + + host_add, host_del = gen_add_del_lists( + host, res_find.get('managedby_host', [])) + + principal_add, principal_del = gen_add_del_lists( + principal, res_find.get("principal")) + + (allow_create_keytab_user_add, + allow_create_keytab_user_del) = \ + gen_add_del_lists( + allow_create_keytab_user, res_find.get( + 'ipaallowedtoperform_write_keys_user', + [])) + (allow_retrieve_keytab_user_add, + allow_retrieve_keytab_user_del) = \ + gen_add_del_lists( + allow_retrieve_keytab_user, res_find.get( + 'ipaallowedtoperform_read_keys_user', + [])) + (allow_create_keytab_group_add, + allow_create_keytab_group_del) = \ + gen_add_del_lists( + allow_create_keytab_group, res_find.get( + 'ipaallowedtoperform_write_keys_group', + [])) + (allow_retrieve_keytab_group_add, + allow_retrieve_keytab_group_del) = \ + gen_add_del_lists( + allow_retrieve_keytab_group, + res_find.get( + 'ipaallowedtoperform_read_keys_group', + [])) + (allow_create_keytab_host_add, + allow_create_keytab_host_del) = \ + gen_add_del_lists( + allow_create_keytab_host, + res_find.get( + 'ipaallowedtoperform_write_keys_host', + [])) + (allow_retrieve_keytab_host_add, + allow_retrieve_keytab_host_del) = \ + gen_add_del_lists( + allow_retrieve_keytab_host, + res_find.get( + 'ipaallowedtoperform_read_keys_host', + [])) + (allow_create_keytab_hostgroup_add, + allow_create_keytab_hostgroup_del) = \ + gen_add_del_lists( + allow_create_keytab_hostgroup, + res_find.get( + 'ipaallowedtoperform_write_keys_hostgroup', + [])) + (allow_retrieve_keytab_hostgroup_add, + allow_retrieve_keytab_hostgroup_del) = \ + gen_add_del_lists( + allow_retrieve_keytab_hostgroup, + res_find.get( + 'ipaallowedtoperform_read_keys_hostgroup', + [])) + + elif action == "member": + if res_find is None: + ansible_module.fail_json(msg="No service '%s'" % name) + + existing = res_find.get('usercertificate', []) + if certificate is None: + certificate_add = [] + else: + certificate_add = [c for c in certificate + if c not in existing] + certificate_del = [] + host_add = host or [] + host_del = [] + principal_add = principal or [] + principal_del = [] + + allow_create_keytab_user_add = \ + allow_create_keytab_user or [] + allow_create_keytab_user_del = [] + allow_create_keytab_group_add = \ + allow_create_keytab_group or [] + allow_create_keytab_group_del = [] + allow_create_keytab_host_add = \ + allow_create_keytab_host or [] + allow_create_keytab_host_del = [] + allow_create_keytab_hostgroup_add = \ + allow_create_keytab_hostgroup or [] + allow_create_keytab_hostgroup_del = [] + allow_retrieve_keytab_user_add = \ + allow_retrieve_keytab_user or [] + allow_retrieve_keytab_user_del = [] + allow_retrieve_keytab_group_add = \ + allow_retrieve_keytab_group or [] + allow_retrieve_keytab_group_del = [] + allow_retrieve_keytab_host_add = \ + allow_retrieve_keytab_host or [] + allow_retrieve_keytab_host_del = [] + allow_retrieve_keytab_hostgroup_add = \ + allow_retrieve_keytab_hostgroup or [] + allow_retrieve_keytab_hostgroup_del = [] + + # Add principals + for _principal in principal_add: + commands.append([name, "service_add_principal", + { + "krbprincipalname": + _principal, + }]) + + # Remove principals + for _principal in principal_del: + commands.append([name, "service_remove_principal", + { + "krbprincipalname": + _principal, + }]) + + for _certificate in certificate_add: + commands.append([name, "service_add_cert", + { + "usercertificate": + _certificate, + }]) + # Remove certificates + for _certificate in certificate_del: + commands.append([name, "service_remove_cert", + { + "usercertificate": + _certificate, + }]) + + # Add hosts. + if host is not None and len(host) > 0 and len(host_add) > 0: + commands.append([name, "service_add_host", + {"host": host_add}]) + # Remove hosts + if host is not None and len(host) > 0 and len(host_del) > 0: + commands.append([name, "service_remove_host", + {"host": host_del}]) + + # Allow create keytab + if len(allow_create_keytab_user_add) > 0 or \ + len(allow_create_keytab_group_add) > 0 or \ + len(allow_create_keytab_host_add) > 0 or \ + len(allow_create_keytab_hostgroup_add) > 0: + commands.append( + [name, "service_allow_create_keytab", + {'user': allow_create_keytab_user_add, + 'group': allow_create_keytab_group_add, + 'host': allow_create_keytab_host_add, + 'hostgroup': allow_create_keytab_hostgroup_add + }]) + + # Disallow create keytab + if len(allow_create_keytab_user_del) > 0 or \ + len(allow_create_keytab_group_del) > 0 or \ + len(allow_create_keytab_host_del) > 0 or \ + len(allow_create_keytab_hostgroup_del) > 0: + commands.append( + [name, "service_disallow_create_keytab", + {'user': allow_create_keytab_user_del, + 'group': allow_create_keytab_group_del, + 'host': allow_create_keytab_host_del, + 'hostgroup': allow_create_keytab_hostgroup_del + }]) + + # Allow retrieve keytab + if len(allow_retrieve_keytab_user_add) > 0 or \ + len(allow_retrieve_keytab_group_add) > 0 or \ + len(allow_retrieve_keytab_hostgroup_add) > 0 or \ + len(allow_retrieve_keytab_hostgroup_add) > 0: + commands.append( + [name, "service_allow_retrieve_keytab", + {'user': allow_retrieve_keytab_user_add, + 'group': allow_retrieve_keytab_group_add, + 'host': allow_retrieve_keytab_host_add, + 'hostgroup': allow_retrieve_keytab_hostgroup_add + }]) + + # Disllow retrieve keytab + if len(allow_retrieve_keytab_user_del) > 0 or \ + len(allow_retrieve_keytab_group_del) > 0 or \ + len(allow_retrieve_keytab_host_del) > 0 or \ + len(allow_retrieve_keytab_hostgroup_del) > 0: + commands.append( + [name, "service_disallow_retrieve_keytab", + {'user': allow_retrieve_keytab_user_del, + 'group': allow_retrieve_keytab_group_del, + 'host': allow_retrieve_keytab_host_del, + 'hostgroup': allow_retrieve_keytab_hostgroup_del + }]) + + elif state == "absent": + if action == "service": + if res_find is not None: + commands.append([name, 'service_del', {}]) + + elif action == "member": + if res_find is None: + ansible_module.fail_json(msg="No service '%s'" % name) + + # Remove principals + if principal is not None: + for _principal in principal: + commands.append([name, "service_remove_principal", + { + "krbprincipalname": + _principal, + }]) + # Remove certificates + if certificate is not None: + existing = res_find.get('usercertificate', []) + for _certificate in certificate: + if _certificate in existing: + commands.append([name, "service_remove_cert", + { + "usercertificate": + _certificate, + }]) + + # Add hosts + if host is not None: + commands.append( + [name, "service_remove_host", {"host": host}]) + + # Allow create keytab + if allow_create_keytab_user is not None or \ + allow_create_keytab_group is not None or \ + allow_create_keytab_host is not None or \ + allow_create_keytab_hostgroup is not None: + commands.append( + [name, "service_disallow_create_keytab", + {'user': allow_create_keytab_user, + 'group': allow_create_keytab_group, + 'host': allow_create_keytab_host, + 'hostgroup': allow_create_keytab_hostgroup + }]) + + # Allow retriev keytab + if allow_retrieve_keytab_user is not None or \ + allow_retrieve_keytab_group is not None or \ + allow_retrieve_keytab_host is not None or \ + allow_retrieve_keytab_hostgroup is not None: + commands.append( + [name, "service_disallow_retrieve_keytab", + {'user': allow_retrieve_keytab_user, + 'group': allow_retrieve_keytab_group, + 'host': allow_retrieve_keytab_host, + 'hostgroup': allow_retrieve_keytab_hostgroup + }]) + + elif state == "disabled": + if action == "service": + if res_find is not None and \ + len(res_find.get('usercertificate', [])) > 0: + commands.append([name, 'service_disable', {}]) + else: + ansible_module.fail_json( + msg="Invalid action '%s' for state '%s'" % + (action, state)) + else: + ansible_module.fail_json(msg="Unkown state '%s'" % state) + + # Execute commands + errors = [] + for name, command, args in commands: + try: + result = api_command(ansible_module, command, name, args) + + if "completed" in result: + if result["completed"] > 0: + changed = True + else: + changed = True + except Exception as ex: + ansible_module.fail_json(msg="%s: %s: %s" % (command, name, + str(ex))) + # Get all errors + # All "already a member" and "not a member" failures in the + # result are ignored. All others are reported. + if "failed" in result and len(result["failed"]) > 0: + for item in result["failed"]: + failed_item = result["failed"][item] + for member_type in failed_item: + for member, failure in failed_item[member_type]: + if "already a member" in failure \ + or "not a member" in failure: + continue + errors.append("%s: %s %s: %s" % ( + command, member_type, member, failure)) + if len(errors) > 0: + ansible_module.fail_json(msg=", ".join(errors)) + + except Exception as ex: + ansible_module.fail_json(msg=str(ex)) + + finally: + temp_kdestroy(ccache_dir, ccache_name) + + # Done + ansible_module.exit_json(changed=changed, **exit_args) + + +if __name__ == "__main__": + main() diff --git a/tests/service/certificate/cert1.der b/tests/service/certificate/cert1.der new file mode 100644 index 0000000000000000000000000000000000000000..b1b90efde6d33ab44ca7b62941eee06fdbc05da9 GIT binary patch literal 771 zcmXqLV)}2;#Q1yxGZP~dlZe5^6M6Lu+crHd-@kXE>cabLgbS>BkANhkjbLS#J;867*=|%sE9*)_Al& zo^Ql|K=js!)el@liX?W4?nyhX_H%K~(U+HsgcsV>ckkm8zq0RVqTJ?-KZQ2ceo=U+ zvoK+`+p>AT6_&2L5ht@!c8T{qP4!D(ILz)2~?WT*nbB^vg zl`USb?8vx7*FK@~qqs@)&1)rMOa(f-H+Kp-yZ-RwnUocC%sxueHTUUmtt|(6)=zoNh`BR7>G4sSHKTaAk4`4pM}+c8Au@qJ1{PR!OqCAAY8ZZ znsb5NxuUDHN}`O#B$S+9|M~DH;MjwwJJ%<6cBtM=yi^j@dE@KsjF3(K>*ns~;r=>% z!rM6(j!7%#?D)Zb#*@!GXs(~`7OR{4)q6JT?7GQ4b;ohnbCs(8F^}>b(?2-R^53&k z{bf!>U?x*%+oK6jQm(nl)rOTghUj1FI8-mcUstWbVP8W*yhzJFd&xU1p8XNgO8xyqL7L&Y J*3>sgvjD#_KokG~ literal 0 HcmV?d00001 diff --git a/tests/service/certificate/cert1.pem b/tests/service/certificate/cert1.pem new file mode 100644 index 00000000..ab3704bb --- /dev/null +++ b/tests/service/certificate/cert1.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQEL +BQAwDzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQx +MDhaMA8xDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC+XVVGFYpHVkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk ++OfEkzLUAhWBOwEraELJzMLJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa +8KvgRVRyGLoVvGbLJvmjfMXp0nIToTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0am +nvsgparYXhypHaRLnikn0vQINt55YoEd1s4KrvEcD2VdZkIMPbLRu2zFvMprF3cj +QQG4LT9ggfEXNIPZ1nQWAnAsu7OJEkNF+E4Mkmpcxj9aGUVt5bsq1D+Tzj3GsidS +X0nSNcZ2JltXRnL/5v63g5cZyE+nAgMBAAGjUzBRMB0GA1UdDgQWBBRV0j7JYuku +H/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV0j7JYukuH/r/t9+QeNlRLXDlEDAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgVy1+1kNwHs5y1Zp0 +WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJPrp2/DAv1m5DtnDhBYqic +uPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfwQ5pPvKkn6WxYUYkG +wIt1OH2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHoVOIXHwNoooyE +iaio3693l6noobyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+sKC2E8xEj +9uKQ5GTWRh59VnRBVC/SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA5yqV +7MVq +-----END CERTIFICATE----- diff --git a/tests/service/certificate/cert2.der b/tests/service/certificate/cert2.der new file mode 100644 index 0000000000000000000000000000000000000000..e176c2ba50270b331c457e784dea5f5f6a09e53a GIT binary patch literal 771 zcmXqLV)}2;#Q1yxGZP~dlZcyC&Wnw@6$cKLNIea6o%isBP`jG}FB_*;n@8JsUPeZ4 zRt5upLtX=JHs(+kW*(N3)Z!8YIdNVi0|O%i6GIadBNKxtab9B(*AU9Z)X>DJgls4y zD+6;ABR>Ps9b8OJjEoFBqUW_=jJhSTa4EOQWbfoxa~FKQcEYD`_q<~n8PigZY;C@0 z^Yen-9Hq{g3ECU_S(@i^)hWu*uG3#2i{a%C2_f_e0 zyK4BiJUAmzmAk*vazc>ogUvRL-$WHU|K=*c-1y;<-aEPXkE3NZE^H}%o_3N3RUs+DfoW1jW_p2Mh88hbd$GQHV z%_^VyyXyR_!ndA@>sbv2Lpx$#iLkbFGBGnUFfI-@2sDrdhO;amix`W@+bt^UPu(VT zKdy|FJn(kPg=wzwt_JcTX=N4(1F;6|3iv?^gc%wCv#=U411aQS2gW5Z*clnZ?tKY- z&d7Z3c6Zoz0ltpsR}0D>JYF@wVVA|6&38f#%bNw550&j%Tw$Lg-rB2?bZni-iooaM z=Q2!Xv{T+q;C4M9xp7r)QKM^s?5;PT0^*eP6c-$~e(~Vs-*`9EzO(=Je;&K=J1%B& zsO{SP)iJMbuK)PX;``>ycBk^>=11i2{#>eLdSfClZ=Rxt@=Euw`tMR=IPK@$Qsyjm z`d(kgCw8zgUo_U|=em}CJf}}h{G;t;Ho2rhTI#4%E5rZ&SGbSn1RQD!brx`U{_yGZ zoVvmfMbDV;pL#BTCw7OHpsJR3?D^;AUt^o+H!;n=`tQ>U0ZF%#8=v!+d|CN-W%8W) JIzc