ldap/server: Initial version of ldapspn helper script
ldapspn supports listing, creating and deleting Kerberos service principals.
This commit is contained in:
parent
aaae860b22
commit
dc51edb84a
2 changed files with 143 additions and 0 deletions
135
roles/ldap/server/files/ldapspn.py
Executable file
135
roles/ldap/server/files/ldapspn.py
Executable file
|
@ -0,0 +1,135 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
from time import time
|
||||||
|
from struct import pack
|
||||||
|
|
||||||
|
import ldap3
|
||||||
|
|
||||||
|
|
||||||
|
def ldap_connect():
|
||||||
|
server = ldap3.Server("ldapi:///var/run/ldapi")
|
||||||
|
conn = ldap3.Connection(
|
||||||
|
server,
|
||||||
|
authentication=ldap3.SASL,
|
||||||
|
sasl_mechanism=ldap3.EXTERNAL,
|
||||||
|
sasl_credentials="",
|
||||||
|
client_strategy=ldap3.SYNC,
|
||||||
|
)
|
||||||
|
conn.bind()
|
||||||
|
conn.search(
|
||||||
|
search_base="",
|
||||||
|
search_filter="(objectClass=*)",
|
||||||
|
search_scope=ldap3.BASE,
|
||||||
|
attributes=["namingContexts"],
|
||||||
|
)
|
||||||
|
basedn = conn.response[0]["attributes"]["namingContexts"][0]
|
||||||
|
return (conn, basedn)
|
||||||
|
|
||||||
|
|
||||||
|
def get_last_modified(principal):
|
||||||
|
""" create krbExtraData value with KRB5_TL_MOD_PRINC type """
|
||||||
|
data = pack(
|
||||||
|
"<ccL%dsc" % len(principal),
|
||||||
|
b"\x00",
|
||||||
|
b"\x02",
|
||||||
|
int(time()),
|
||||||
|
bytes(principal, "UTF-8"),
|
||||||
|
b"\x00",
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def get_kvno():
|
||||||
|
""" return krbExtraData value with KRB_TL_MKVNO type with value 0 """
|
||||||
|
return pack("<cccc", b"\x00", b"\x08", b"\x01", b"\x00")
|
||||||
|
|
||||||
|
|
||||||
|
def principal_create(args, conn, basedn):
|
||||||
|
if not args.container:
|
||||||
|
(principal, realm) = args.principal.split("@", 2)
|
||||||
|
(service, hostname) = principal.split("/", 2)
|
||||||
|
conn.search(
|
||||||
|
search_base=basedn,
|
||||||
|
search_filter=f"(&(cn={hostname})(objectClass=device))",
|
||||||
|
)
|
||||||
|
if len(conn.response) != 1:
|
||||||
|
print(
|
||||||
|
"ERROR: Could not get container dn for principal '{args.principal}'",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
args.container = conn.response[0]["dn"]
|
||||||
|
conn.add(
|
||||||
|
f"krbPrincipalName={args.principal},{args.container}",
|
||||||
|
attributes={
|
||||||
|
"objectClass": [
|
||||||
|
"krbPrincipal",
|
||||||
|
"krbPrincipalAux",
|
||||||
|
"krbTicketPolicyAux",
|
||||||
|
],
|
||||||
|
"krbPrincipalName": args.principal,
|
||||||
|
"krbExtraData": [get_last_modified("root/admin@FOO.SH"), get_kvno()],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if conn.result["result"] != 0:
|
||||||
|
print(
|
||||||
|
f"ERROR: Failed to add principal: {conn.result['description']}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def principal_delete(args, conn, basedn):
|
||||||
|
conn.search(
|
||||||
|
search_base=basedn,
|
||||||
|
search_filter=f"(&(krbPrincipalName={args.principal})(objectClass=krbPrincipalAux))",
|
||||||
|
attributes=[],
|
||||||
|
)
|
||||||
|
if len(conn.response) != 1:
|
||||||
|
print(f"ERROR: Could not find principal '{args.principal}'", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
conn.delete(conn.response[0]["dn"])
|
||||||
|
if conn.result["result"] != 0:
|
||||||
|
print(
|
||||||
|
f"ERROR: Failed to delete principal: {conn.result['description']}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def principal_list(args, conn, baesdn):
|
||||||
|
conn.search(
|
||||||
|
search_base=basedn,
|
||||||
|
search_filter="(objectClass=krbPrincipalAux)",
|
||||||
|
attributes=["krbPrincipalName"],
|
||||||
|
)
|
||||||
|
for result in conn.response:
|
||||||
|
for name in result["attributes"]["krbPrincipalName"]:
|
||||||
|
print(name)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
subparsers = parser.add_subparsers()
|
||||||
|
|
||||||
|
create = subparsers.add_parser("add")
|
||||||
|
create.set_defaults(func=principal_create)
|
||||||
|
create.add_argument("--principal", metavar="PRINCIPAL", type=str, required=True)
|
||||||
|
create.add_argument("--container", metavar="CONTAINER", type=str, required=False)
|
||||||
|
|
||||||
|
delete = subparsers.add_parser("del")
|
||||||
|
delete.set_defaults(func=principal_delete)
|
||||||
|
delete.add_argument("--principal", metavar="PRINCIPAL", type=str, required=True)
|
||||||
|
|
||||||
|
show = subparsers.add_parser("list")
|
||||||
|
show.set_defaults(func=principal_list)
|
||||||
|
parser.set_defaults(func=principal_list)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
try:
|
||||||
|
(conn, basedn) = ldap_connect()
|
||||||
|
args.func(args, conn, basedn)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit(1)
|
|
@ -61,6 +61,14 @@
|
||||||
hour: 0
|
hour: 0
|
||||||
minute: 10
|
minute: 10
|
||||||
user: ldap
|
user: ldap
|
||||||
|
|
||||||
|
- name: copy spn helper script
|
||||||
|
copy:
|
||||||
|
dest: /usr/local/sbin/ldapspn
|
||||||
|
src: ldapspn.py
|
||||||
|
mode: 0755
|
||||||
|
owner: root
|
||||||
|
group: "{{ ansible_wheel }}"
|
||||||
when: ldap_master is defined
|
when: ldap_master is defined
|
||||||
|
|
||||||
- name: remove nss cert databases
|
- name: remove nss cert databases
|
||||||
|
|
Loading…
Add table
Reference in a new issue