diff --git a/changelogs/fragments/6668-ldap-client-cert.yml b/changelogs/fragments/6668-ldap-client-cert.yml
new file mode 100644
index 0000000000..e566b4ae13
--- /dev/null
+++ b/changelogs/fragments/6668-ldap-client-cert.yml
@@ -0,0 +1,2 @@
+minor_changes:
+  - ldap_* - add new arguments ``client_cert`` and ``client_key`` to the LDAP modules in order to allow certificate authentication (https://github.com/ansible-collections/community.general/pull/6668).
diff --git a/plugins/doc_fragments/ldap.py b/plugins/doc_fragments/ldap.py
index 451b3f3e06..e11ab065d8 100644
--- a/plugins/doc_fragments/ldap.py
+++ b/plugins/doc_fragments/ldap.py
@@ -29,6 +29,18 @@ options:
       - Set the path to PEM file with CA certs.
     type: path
     version_added: "6.5.0"
+  client_cert:
+    type: path
+    description:
+      - PEM formatted certificate chain file to be used for SSL client authentication.
+      - Required if O(client_key) is defined.
+    version_added: "7.1.0"
+  client_key:
+    type: path
+    description:
+      - PEM formatted file that contains your private key to be used for SSL client authentication.
+      - Required if O(client_cert) is defined.
+    version_added: "7.1.0"
   dn:
     required: true
     description:
diff --git a/plugins/module_utils/ldap.py b/plugins/module_utils/ldap.py
index 6553713210..ef444e9778 100644
--- a/plugins/module_utils/ldap.py
+++ b/plugins/module_utils/ldap.py
@@ -42,11 +42,17 @@ def gen_specs(**specs):
         'validate_certs': dict(default=True, type='bool'),
         'sasl_class': dict(choices=['external', 'gssapi'], default='external', type='str'),
         'xorder_discovery': dict(choices=['enable', 'auto', 'disable'], default='auto', type='str'),
+        'client_cert': dict(default=None, type='path'),
+        'client_key': dict(default=None, type='path'),
     })
 
     return specs
 
 
+def ldap_required_together():
+    return [['client_cert', 'client_key']]
+
+
 class LdapGeneric(object):
     def __init__(self, module):
         # Shortcuts
@@ -60,6 +66,8 @@ class LdapGeneric(object):
         self.verify_cert = self.module.params['validate_certs']
         self.sasl_class = self.module.params['sasl_class']
         self.xorder_discovery = self.module.params['xorder_discovery']
+        self.client_cert = self.module.params['client_cert']
+        self.client_key = self.module.params['client_key']
 
         # Establish connection
         self.connection = self._connect_to_ldap()
@@ -102,6 +110,10 @@ class LdapGeneric(object):
         if self.ca_path:
             ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, self.ca_path)
 
+        if self.client_cert and self.client_key:
+            ldap.set_option(ldap.OPT_X_TLS_CERTFILE, self.client_cert)
+            ldap.set_option(ldap.OPT_X_TLS_KEYFILE, self.client_key)
+
         connection = ldap.initialize(self.server_uri)
 
         if self.referrals_chasing == 'disabled':
diff --git a/plugins/modules/ldap_attrs.py b/plugins/modules/ldap_attrs.py
index c2cac86444..d93b4672a4 100644
--- a/plugins/modules/ldap_attrs.py
+++ b/plugins/modules/ldap_attrs.py
@@ -182,7 +182,7 @@ import traceback
 
 from ansible.module_utils.basic import AnsibleModule, missing_required_lib
 from ansible.module_utils.common.text.converters import to_native, to_bytes, to_text
-from ansible_collections.community.general.plugins.module_utils.ldap import LdapGeneric, gen_specs
+from ansible_collections.community.general.plugins.module_utils.ldap import LdapGeneric, gen_specs, ldap_required_together
 
 import re
 
@@ -300,6 +300,7 @@ def main():
             state=dict(type='str', default='present', choices=['absent', 'exact', 'present']),
         ),
         supports_check_mode=True,
+        required_together=ldap_required_together(),
     )
 
     if not HAS_LDAP:
diff --git a/plugins/modules/ldap_entry.py b/plugins/modules/ldap_entry.py
index 619bbf9279..11fa5278dc 100644
--- a/plugins/modules/ldap_entry.py
+++ b/plugins/modules/ldap_entry.py
@@ -151,7 +151,7 @@ import traceback
 
 from ansible.module_utils.basic import AnsibleModule, missing_required_lib
 from ansible.module_utils.common.text.converters import to_native, to_bytes
-from ansible_collections.community.general.plugins.module_utils.ldap import LdapGeneric, gen_specs
+from ansible_collections.community.general.plugins.module_utils.ldap import LdapGeneric, gen_specs, ldap_required_together
 
 LDAP_IMP_ERR = None
 try:
@@ -255,6 +255,7 @@ def main():
         ),
         required_if=[('state', 'present', ['objectClass'])],
         supports_check_mode=True,
+        required_together=ldap_required_together(),
     )
 
     if not HAS_LDAP:
diff --git a/plugins/modules/ldap_passwd.py b/plugins/modules/ldap_passwd.py
index f47fa330e3..3f9e6ec4ad 100644
--- a/plugins/modules/ldap_passwd.py
+++ b/plugins/modules/ldap_passwd.py
@@ -72,7 +72,7 @@ modlist:
 import traceback
 
 from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible_collections.community.general.plugins.module_utils.ldap import LdapGeneric, gen_specs
+from ansible_collections.community.general.plugins.module_utils.ldap import LdapGeneric, gen_specs, ldap_required_together
 
 LDAP_IMP_ERR = None
 try:
@@ -133,6 +133,7 @@ def main():
     module = AnsibleModule(
         argument_spec=gen_specs(passwd=dict(no_log=True)),
         supports_check_mode=True,
+        required_together=ldap_required_together(),
     )
 
     if not HAS_LDAP:
diff --git a/plugins/modules/ldap_search.py b/plugins/modules/ldap_search.py
index a733ca7a5d..d8fa2efd07 100644
--- a/plugins/modules/ldap_search.py
+++ b/plugins/modules/ldap_search.py
@@ -121,7 +121,7 @@ import traceback
 from ansible.module_utils.basic import AnsibleModule, missing_required_lib
 from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
 from ansible.module_utils.six import string_types, text_type
-from ansible_collections.community.general.plugins.module_utils.ldap import LdapGeneric, gen_specs
+from ansible_collections.community.general.plugins.module_utils.ldap import LdapGeneric, gen_specs, ldap_required_together
 
 LDAP_IMP_ERR = None
 try:
@@ -145,6 +145,7 @@ def main():
             base64_attributes=dict(type='list', elements='str'),
         ),
         supports_check_mode=True,
+        required_together=ldap_required_together(),
     )
 
     if not HAS_LDAP:
diff --git a/tests/integration/targets/ldap_search/tasks/tests/auth.yml b/tests/integration/targets/ldap_search/tasks/tests/auth.yml
new file mode 100644
index 0000000000..a8c7a13ee9
--- /dev/null
+++ b/tests/integration/targets/ldap_search/tasks/tests/auth.yml
@@ -0,0 +1,47 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- debug:
+    msg: Running tests/auth.yml
+
+####################################################################
+## Search ##########################################################
+####################################################################
+- name: Test simple search for password authenticated user
+  ldap_search:
+    dn: "ou=users,dc=example,dc=com"
+    scope: "onelevel"
+    filter: "(uid=ldaptest)"
+    bind_dn: "uid=ldaptest,ou=users,dc=example,dc=com"
+    bind_pw: "test1pass!"
+  ignore_errors: true
+  register: output
+
+- name: assert that test LDAP user can read its password
+  assert:
+    that:
+       - output is not failed
+       - output.results | length == 1
+       - output.results.0.userPassword is defined
+
+- name: Test simple search for cert authenticated user
+  ldap_search:
+    dn: "ou=users,dc=example,dc=com"
+    server_uri: "ldap://localhost/"
+    start_tls: true
+    ca_path: /usr/local/share/ca-certificates/ca.crt
+    scope: "onelevel"
+    filter: "(uid=ldaptest)"
+    client_cert: "/root/user.crt"
+    client_key: "/root/user.key"
+  ignore_errors: true
+  register: output
+
+- name: assert that test LDAP user can read its password
+  assert:
+    that:
+       - output is not failed
+       - output.results | length == 1
+       - output.results.0.userPassword is defined
diff --git a/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif b/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif
new file mode 100644
index 0000000000..fc97e5f5c3
--- /dev/null
+++ b/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif
@@ -0,0 +1,15 @@
+dn: cn=config
+add: olcTLSCACertificateFile
+olcTLSCACertificateFile: /usr/local/share/ca-certificates/ca.crt
+-
+add: olcTLSCertificateFile
+olcTLSCertificateFile: /etc/ldap/localhost.crt
+-
+add: olcTLSCertificateKeyFile
+olcTLSCertificateKeyFile: /etc/ldap/localhost.key
+-
+add: olcAuthzRegexp
+olcAuthzRegexp: {0}"UID=([^,]*)" uid=$1,ou=users,dc=example,dc=com
+-
+add: olcTLSVerifyClient
+olcTLSVerifyClient: allow
diff --git a/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif.license b/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif.license
new file mode 100644
index 0000000000..edff8c7685
--- /dev/null
+++ b/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif.license
@@ -0,0 +1,3 @@
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+SPDX-FileCopyrightText: Ansible Project
diff --git a/tests/integration/targets/setup_openldap/files/initial_config.ldif b/tests/integration/targets/setup_openldap/files/initial_config.ldif
index 8d0e40de79..cb21f2cfdb 100644
--- a/tests/integration/targets/setup_openldap/files/initial_config.ldif
+++ b/tests/integration/targets/setup_openldap/files/initial_config.ldif
@@ -18,6 +18,7 @@ homeDirectory: /home/ldaptest
 cn: LDAP Test
 gecos: LDAP Test
 displayName: LDAP Test
+userPassword: test1pass!
 mail: ldap.test@example.com
 sn: Test
 
diff --git a/tests/integration/targets/setup_openldap/tasks/main.yml b/tests/integration/targets/setup_openldap/tasks/main.yml
index 25077de166..00f8f6a108 100644
--- a/tests/integration/targets/setup_openldap/tasks/main.yml
+++ b/tests/integration/targets/setup_openldap/tasks/main.yml
@@ -44,6 +44,22 @@
         cmd: "export DEBIAN_FRONTEND=noninteractive; cat /root/debconf-slapd.conf | debconf-set-selections; dpkg-reconfigure -f noninteractive slapd"
         creates: "/root/slapd_configured"
 
+    - name: Enable secure ldap
+      lineinfile:
+        path: /etc/default/slapd
+        regexp: "^SLAPD_SERVICES"
+        line: 'SLAPD_SERVICES="ldap:/// ldaps:/// ldapi:///"'
+
+    - name: Create certificates
+      shell: |
+        openssl req -x509 -batch -sha256 -days 1825 -newkey rsa:2048 -nodes -keyout /root/ca.key -out /usr/local/share/ca-certificates/ca.crt
+        openssl req -batch -sha256 -days 365 -newkey rsa:2048 -subj "/CN=$(hostname)" -addext "subjectAltName = DNS:localhost" -nodes -keyout /etc/ldap/localhost.key -out /etc/ldap/localhost.csr
+        openssl x509 -req  -CA /usr/local/share/ca-certificates/ca.crt -CAkey /root/ca.key -CAcreateserial -in /etc/ldap/localhost.csr -out /etc/ldap/localhost.crt
+        chgrp openldap /etc/ldap/localhost.key
+        chmod 0640 /etc/ldap/localhost.key
+        openssl req -batch -sha256 -days 365 -newkey rsa:2048 -subj "/UID=ldaptest" -nodes -keyout /root/user.key -out /root/user.csr
+        openssl x509 -req  -CA /usr/local/share/ca-certificates/ca.crt -CAkey /root/ca.key -CAcreateserial -in /root/user.csr -out /root/user.crt
+
     - name: Start OpenLDAP service
       become: true
       service:
@@ -61,10 +77,14 @@
         mode: '0644'
       loop:
         - rootpw_cnconfig.ldif
+        - cert_cnconfig.ldif
         - initial_config.ldif
 
     - name: Configure admin password for cn=config
-      shell: "ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/rootpw_cnconfig.ldif"
+      shell: "ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/{{ item }}"
+      loop:
+        - rootpw_cnconfig.ldif
+        - cert_cnconfig.ldif
 
     - name: Add initial config
       become: true