From ce28001bae090ff613d14ab55b1e5766825595df Mon Sep 17 00:00:00 2001 From: Kevin Guerineau Date: Fri, 10 May 2024 16:47:51 +0200 Subject: [PATCH] [REF] Split in modules --- common.py | 353 ----------------------------------------- dc_module.py | 118 ++++++++++++++ intermediate_module.py | 116 ++++++++++++++ manage_pki.py | 17 +- root_module.py | 91 +++++++++++ user_module.py | 95 +++++++++++ 6 files changed, 429 insertions(+), 361 deletions(-) create mode 100644 dc_module.py create mode 100644 intermediate_module.py create mode 100644 root_module.py create mode 100644 user_module.py diff --git a/common.py b/common.py index 4458377..0f08c25 100644 --- a/common.py +++ b/common.py @@ -125,356 +125,3 @@ def check_directories(path,verbose=False): with open(os.path.join(path,'index.txt'),'w') as file: pass - -def create_openssl_config(force=False,verbose=False): - if force: - Printing.error("Do you realy want to remove ALL you PKI ? This will destroy ALL YOUR CERTIFICATES AND PRIVATE KEY") - Printing.error("After that, you MUST REGENERATE YOUR PKI with NEW certificates and private key for ALL YOUR DOMAIN CONTROLLERS AND USERS") - destroy = input('If you are realy sure, please enter : "I want to remove all my PKI" : ') - if destroy == 'I want to remove all my PKI': - Printing.information('OK, too late ! Destroying your PKI !') - time.sleep(1) - shutil.rmtree(TisPKI.pki_dir(), ignore_errors=True) - - Printing.information('Check Root CA OpenSSL Config') - - if config.get('general','pki_dir'): - check_directories(path=config.get('general','pki_dir'),verbose=verbose) - else: - Printing.error('No pki_dir set in samba-pki-tools.ini') - sys.exit(1) - - root_ca_config = os.path.join(TisPKI.pki_dir(),'config','openssl_root_ca.ini') - - if not os.path.isfile(root_ca_config): - Printing.information('Root CA OpenSSL configfile not exist. Creating...') - template_dir = os.path.join('templates') - jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) - root_ca_tmpl = jinja_env.get_template('openssl_root_ca.tmpl') - root_ca_tmpl_var = { - 'organization_ou': config.get('openssl_config','organization_name') + ' CA', - 'default_cert_duration': config.get('openssl_config','default_cert_duration'), - 'default_crl_duration' : config.get('openssl_config','default_crl_duration'), - 'pki_dir': TisPKI.pki_dir(), - 'country': config.get('openssl_config','country'), - 'state': config.get('openssl_config','state'), - 'city': config.get('openssl_config','city'), - 'organization_name': config.get('openssl_config','organization_name'), - 'organization_ou': config.get('openssl_config','organization_ou'), - 'organization_cn': config.get('openssl_config','organization_cn'), - 'crl_uri': config.get('openssl_config','crl_uri') - } - config_string = root_ca_tmpl.render(root_ca_tmpl_var) - with open(root_ca_config,'wt') as file: - file.write(config_string) - if os.path.isfile(root_ca_config): - Printing.success('Root CA OpenSSL config file is correctly generated !') - else: - Printing.warning('Root CA OpenSSL config already exist. Skip.') - - # Generate privkey and cert for Root CA - if not os.path.isfile(TisPKI.root_ca_keyfile()) or not os.path.isfile(TisPKI.root_ca_certfile()): - Printing.information('Generate Root CA private key') - gen_root_ca = subprocess.run(f'/usr/bin/openssl req -x509 -new -sha512 -config {root_ca_config} \ - -days 3650 -extensions v3_ca -keyout {TisPKI.root_ca_keyfile()} -out {TisPKI.root_ca_certfile()}', - shell=True, check=False, executable='/bin/bash') - - if gen_root_ca.returncode == 0: - if verbose: - subprocess.run(f'openssl x509 -in {TisPKI.root_ca_certfile()} -text', shell=True, check=True, executable='/bin/bash') - Printing.information(f'Root CA Certfile is stored in {TisPKI.root_ca_certfile()}') - else: - Printing.error('Error on generating Root CA private key') - os.remove(TisPKI.root_ca_keyfile()) - retry = input('If you want to retry, press Y : ') - if retry == "y" or retry == 'Y': - create_openssl_config(force,verbose) - else: - Printing.warning('Root CA private key and certificate already exist. Skip.') - - -def create_openssl_intermediate(name, force=False,verbose=False): - -## if force: -## Printing.error("Do you realy want to remove ALL you PKI ? This will destroy ALL YOUR CERTIFICATES AND PRIVATE KEY") -## Printing.error("After that, you MUST REGENERATE YOUR PKI with NEW certificates and private key for ALL YOUR DOMAIN CONTROLLERS AND USERS") -## destroy = input('If you are realy sure, please enter : "I want to remove all my PKI" : ') -## if destroy == 'I want to remove all my PKI': -## Printing.information('OK, too late ! Destroying your PKI !') -## time.sleep(1) -## shutil.rmtree(TisPKI.pki_dir(), ignore_errors=True) - - - Printing.information(f'Create intermediate CA {name} ') - - check_directories(path=TisPKI.pki_intermediate_dir(name),verbose=verbose) - intermediate_ca_config = os.path.join(TisPKI.intermediate_config_path(name),'create_intermediate_ca.ini') - intermediate_crl_file = os.path.join(TisPKI.intermediate_crl_path(name),f'{name}_intermediate_ca.crl') - root_ca_sign_intermediate = os.path.join(TisPKI.root_config_path(),'openssl_root_ca_sign_intermediate.ini') - - if not os.path.isfile(root_ca_sign_intermediate): - if TisPKI.intermediate_ca: - template_dir = os.path.join('templates') - jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) - root_ca_tmpl = jinja_env.get_template('openssl_sign_intermediate_csr.tmpl') - root_ca_tmpl_var = { - 'organization_ou': config.get('openssl_config','organization_name') + ' CA', - 'pki_dir': TisPKI.pki_dir(), - 'root_ca_keyfile': TisPKI.root_ca_keyfile(), - 'root_ca_certfile': TisPKI.root_ca_certfile(), - 'intermediate_crl_uri': config.get('openssl_config','intermediate_crl_uri'), - 'default_crl_duration' : config.get('openssl_config','default_crl_duration'), - } - - config_string = root_ca_tmpl.render(root_ca_tmpl_var) - with open(root_ca_sign_intermediate,'wt') as file: - file.write(config_string) - if os.path.isfile(root_ca_sign_intermediate): - Printing.success('Root CA OpenSSL sign intermediate config file is correctly generated !') - - if not os.path.isfile(intermediate_ca_config): - Printing.information('Intermediate CA OpenSSL configfile not exist. Creating...') - template_dir = os.path.join('templates') - jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) - intermediate_ca_tmpl = jinja_env.get_template('openssl_create_intermediate_ca.tmpl') - intermediate_ca_tmpl_var = { - 'country': config.get('openssl_config','country'), - 'state': config.get('openssl_config','state'), - 'city': config.get('openssl_config','city'), - 'organization_name': config.get('openssl_config','organization_name'), - 'organization_ou': config.get('openssl_config','organization_ou'), - 'intermediate_organization_cn': name, - 'intermediate_crl_uri': config.get('openssl_config','intermediate_crl_uri') - } - config_string = intermediate_ca_tmpl.render(intermediate_ca_tmpl_var) - with open(intermediate_ca_config,'wt') as file: - file.write(config_string) - if os.path.isfile(intermediate_ca_config): - Printing.success('Intermediate CA OpenSSL configfile is correctly generated !') - else: - Printing.information('Intermediate CA OpenSSL config already exist. Skip.') - - if not os.path.isfile(TisPKI.intermediate_ca_keyfile(name)) and not os.path.isfile(TisPKI.intermediate_ca_certfile(name)): - Printing.information('Generate Intermediate CA private key and CSR') - gen_intermediate_ca = subprocess.run(f"/usr/bin/openssl req -config {intermediate_ca_config} -new -sha512 -keyout {TisPKI.intermediate_ca_keyfile(name)} \ - -out {TisPKI.intermediate_csr_path(name)}/{name.replace(' ','_')}_intermediate_ca.csr", shell=True, check=False, executable='/bin/bash') - - if gen_intermediate_ca.returncode == 0: - Printing.information('Sign Intermediate CA with Root CA') - sign_intermediate_ca = subprocess.run(f"/usr/bin/openssl ca -config {os.path.join(TisPKI.root_config_path(),'openssl_root_ca_sign_intermediate.ini')} \ - -extensions v3_intermediate_ca -days 1825 -notext -md sha512 -create_serial -in {TisPKI.intermediate_csr_path(name)}/{name.replace(' ','_')}_intermediate_ca.csr \ - -out {TisPKI.intermediate_ca_certfile(name)}", shell=True, check=False, executable='/bin/bash') - - if sign_intermediate_ca.returncode == 0: - if verbose: - subprocess.run(f'openssl x509 -in {TisPKI.intermediate_ca_certfile(name)} -text', shell=True, check=True, executable='/bin/bash') - Printing.success(f'Intermediate CA Certfile is stored in : {TisPKI.intermediate_ca_certfile(name)}') - else: - Printing.error('Error on generating Intermediate CA private key') - retry = input('If you want to retry, press Y : ') - if retry == "y" or retry == 'Y': - os.remove(TisPKI.intermediate_ca_keyfile(name)) - create_openssl_intermediate(name, force, verbose) - else: - Printing.error('Error on generating Intermediate CA private key') - retry = input('If you want to retry, press Y : ') - if retry == "y" or retry == 'Y': - os.remove(TisPKI.intermediate_ca_keyfile(name)) - create_openssl_intermediate(name, force, verbose) - else: - Printing.warning('Intermediate CA private key and certificate already exist. Skip.') - - -def generate_dc_certificate(dc_name=None, ca_name=None, force=False, verbose=False): - - if ca_name != "Root": - dc_certfile = os.path.join(TisPKI.intermediate_cert_path(ca_name),f'{dc_name}.crt') - dc_keyfile = os.path.join(TisPKI.intermediate_keyout_path(ca_name),f'{dc_name}.key') - dc_csrfile = os.path.join(TisPKI.intermediate_csr_path(ca_name),f'{dc_name}.csr') - dc_openssl_configfile = os.path.join(TisPKI.intermediate_config_path(ca_name),f'openssl_{dc_name}.ini') - crl_file = os.path.join(TisPKI.intermediate_crl_path(ca_name),'intermediate_ca.crl') - dc_ca_keyfile = TisPKI.intermediate_ca_keyfile(ca_name) - dc_ca_certfile = TisPKI.intermediate_ca_certfile(ca_name) - crl_uri = config.get('openssl_config','intermediate_crl_uri') - pki_dir = TisPKI.pki_intermediate_dir(ca_name) - else: - dc_certfile = os.path.join(TisPKI.root_cert_path(),f'{dc_name}.crt') - dc_keyfile = os.path.join(TisPKI.root_keyout_path(),f'{dc_name}.key') - dc_csrfile = os.path.join(TisPKI.root_csr_path(),f'{dc_name}.csr') - dc_openssl_configfile = os.path.join(TisPKI.root_config_path(),f'openssl_{dc_name}.ini') - crl_file = os.path.join(TisPKI.root_crl_path(),'root_ca.crl') - dc_ca_keyfile = os.path.join(TisPKI.root_keyout_path(),'root_ca.key') - dc_ca_certfile = os.path.join(TisPKI.root_cert_path(),'root_ca.crt') - crl_uri = config.get('openssl_config','crl_uri') - pki_dir = TisPKI.pki_dir() - - if not os.path.isfile(dc_certfile) and not os.path.isfile(dc_keyfile): - Printing.information(f'Generate certificate for {dc_name}') - - dc_guid = subprocess.run('/bin/bash get_guid.sh',shell=True, check=True, executable='/bin/bash') - if dc_guid.returncode != 0: - Printing.error('Unable to find dc_guid') - sys.exit(1) - else: - dc_name_guid = dc_name.split('.')[0] - dc_guid = str(subprocess.check_output(f'/bin/bash get_guid.sh {dc_name_guid}',shell=True).decode("utf-8")).strip() - - if verbose: - Printing.information(f'{dc_name} GUID is : ' + str(dc_guid).strip()) - - template_dir = ('templates') - jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) - dc_tmpl = jinja_env.get_template('openssl_server_cert.tmpl') - dc_tmpl_var = { - 'dc_name': dc_name, - 'dc_guid': str(dc_guid), - 'crl_uri': crl_uri, - 'pki_dir': pki_dir, - 'default_cert_duration': config.get('openssl_config','default_cert_duration'), - 'country': config.get('openssl_config','country'), - 'state': config.get('openssl_config','state'), - 'city': config.get('openssl_config','city'), - 'organization_name': config.get('openssl_config','organization_name'), - 'organization_ou': config.get('openssl_config','organization_ou'), - 'dc_ca_keyfile': dc_ca_keyfile, - 'dc_ca_certfile': dc_ca_certfile, - 'commonName': dc_name - - } - - config_string = dc_tmpl.render(dc_tmpl_var) - with open(dc_openssl_configfile,'wt') as file: - file.write(config_string) - - if os.path.isfile(dc_openssl_configfile): - Printing.information(f'{dc_name} OpenSSL configfile is correctly generated !') - - Printing.information(f'Generate private key and CSR for {dc_name}') - gen_dc_key = subprocess.run(f"openssl req -new -addext 'subjectAltName = email:copy' -newkey rsa:4096 -keyout {dc_keyfile} \ - -out {dc_csrfile} -config {dc_openssl_configfile}" , shell=True, check=False, executable='/bin/bash') - - if gen_dc_key.returncode == 0: - if os.path.isfile(dc_csrfile): - Printing.information(f'Sign certificate for {dc_name}') - sign_dc_cert = subprocess.run(f'openssl ca -config {dc_openssl_configfile} -extensions usr_cert_mskdc \ - -days 3650 -notext -md sha512 -create_serial -in {dc_csrfile} -out {dc_certfile}', shell=True, check=False, executable='/bin/bash') - - if sign_dc_cert.returncode == 0: - if os.path.isfile(dc_certfile): - print('Concatenation of DC and intermediate cert') - subprocess.run(f'cat {dc_certfile} {dc_ca_certfile} > {dc_certfile}_full',shell=True) - Printing.success(f'Certificat is stored in : {dc_certfile}') - Printing.success(f'Key is stored in : {dc_keyfile}') - else: - Printing.error('Error on sign Domain Controler certificate') - retry = input('If you want to retry, press Y : ') - if retry == "y" or retry == 'Y': - os.remove(dc_keyfile) - generate_dc_certificate(dc_name, ca_name, force, verbose) - else: - Printing.error('Error on generating Domain Controler private key') - retry = input('If you want to retry, press Y : ') - if retry == "y" or retry == 'Y': - os.remove(dc_keyfile) - generate_dc_certificate(dc_name, ca_name, force, verbose) - - -def generate_user_certificate(): - default_bits_user = config.get('openssl_config','default_bits_user') - - username = input('Enter username') - - upn_suffix = config.get('samba_ad','upn_suffix') - upn = f'{username}@{upn_suffix}' - - if TisPKI.intermediate_ca: - crl_file = os.path.join(TisPKI.intermediate_crl_path(),'intermediate_ca.crl') - dc_ca_keyfile = os.path.join(TisPKI.intermediate_keyout_path(),'intermediate_ca.key') - dc_ca_certfile = os.path.join(TisPKI.intermediate_cert_path(),'intermediate_ca.crt') - crl_uri = config.get('openssl_config','intermediate_crl_uri') - pki_dir = TisPKI.pki_intermediate_dir() - openssl_user_file = os.path.join(TisPKI.intermediate_config_path(),'openssl_user.ini') - emailAddress = upn - commonName = username - else: - crl_file = os.path.join(TisPKI.root_crl_path(),'root_ca.crl') - dc_ca_keyfile = os.path.join(TisPKI.root_keyout_path(),'root_ca.key') - dc_ca_certfile = os.path.join(TisPKI.root_cert_path(),'root_ca.crt') - crl_uri = config.get('openssl_config','crl_uri') - pki_dir = TisPKI.pki_dir() - openssl_user_file = os.path.join(TisPKI.root_config_path(),'openssl_user.ini') - emailAddress = upn - commonName = username - - template_dir = ('templates') - jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) - user_tmpl = jinja_env.get_template('openssl_user_cert.tmpl') - user_tmpl_var = { - 'crl_uri': crl_uri, - 'pki_dir': pki_dir, - 'default_cert_duration': config.get('openssl_config','default_cert_duration'), - 'country': config.get('openssl_config','country'), - 'state': config.get('openssl_config','state'), - 'city': config.get('openssl_config','city'), - 'organization_name': config.get('openssl_config','organization_name'), - 'organization_ou': config.get('openssl_config','organization_ou'), - 'dc_ca_keyfile': dc_ca_keyfile, - 'dc_ca_certfile': dc_ca_certfile, - 'default_bits_user': config.get('openssl_config','default_bits_user'), - 'emailAddress' : emailAddress, - 'commonName' : commonName - - } - - config_string = user_tmpl.render(user_tmpl_var) - with open(openssl_user_file,'wt') as file: - file.write(config_string) - - if os.path.isfile(openssl_user_file): - print(f'User OpenSSL configfile is correctly generated !') - - - - - print(f'Generate private key for {upn}') - print(subprocess.run(f"openssl req -new -newkey rsa:{default_bits_user} -keyout {TisPKI.root_keyout_path()}/{username}.key -out {TisPKI.root_csr_path()}/{username}.csr -config <(cat {openssl_user_file} <(cat <<-EOF\n[ sanuser ]\notherName=msUPN;UTF8:{upn}\nemail=copy\nEOF\n)\n)",shell=True,check=True, executable='/bin/bash')) - - print(f'Sign certificate') - print(subprocess.run(f'openssl ca -extensions usr_cert_scarduser -days 730 -notext -md sha512 -create_serial -in {TisPKI.root_csr_path()}/{username}.csr -out {TisPKI.root_cert_path()}/{username}.crt -config <(cat {openssl_user_file} <(cat <<-EOF\n[ sanuser ]\notherName=msUPN;UTF8:{upn}\nemail=copy\nEOF\n)\n)',shell=True,check=True, executable='/bin/bash')) - - - print('Remove password in rsa key') - print(subprocess.run(f'openssl rsa -in {TisPKI.root_keyout_path()}/{username}.key -out {TisPKI.root_keyout_path()}/{username}-nopasswd.key',shell=True,check=True, executable='/bin/bash')) - - print('Create p12') - print(subprocess.run(f'openssl pkcs12 -export -inkey {TisPKI.root_keyout_path()}/{username}-nopasswd.key -in {TisPKI.root_cert_path()}/{username}.crt -out {TisPKI.root_p12_path()}/{username}.p12', shell=True,check=True, executable='/bin/bash')) - - -def main(): - - if config.get('general','pki_dir'): - check_directories(root_path=config.get('general','pki_dir')) - else: - print('No pki_dir set in samba-pki-tools.ini') - sys.exit(1) - - if config.get('openssl_config','root_name'): - create_openssl_config() - else: - print('No root_name set in samba-pki-tools.ini') - sys.exit(1) - - if config.getboolean('openssl_config','create_intermediate'): - create_openssl_intermediate() - - if config.get('samba_ad','dc_list'): - generate_dc_certificate() - else: - print('No dc_list set in samba-pki-tools.ini') - sys.exit(1) - - generate_user_certificate() - - -if __name__ == '__main__': - main() diff --git a/dc_module.py b/dc_module.py new file mode 100644 index 0000000..e6e5579 --- /dev/null +++ b/dc_module.py @@ -0,0 +1,118 @@ +#!/usr/bin/python3 +#------------------------------------------------------------------------------- +# Name: Samba PKI Tools +# Purpose: +# +# Author: kguerineau-adm +# +# Created: 10/05/2024 +# Copyright: (c) kguerineau-adm 2024 +# Licence: +#------------------------------------------------------------------------------- + +# Maange DC Certificates + +from common import Printing, TisPKI, check_directories + +import subprocess +import jinja2 +import os +import configparser +import sys +from colorama import Fore, Style +import shutil +import time + + +def generate_dc_certificate(dc_name=None, ca_name=None, force=False, verbose=False): + + if ca_name != "Root": + dc_certfile = os.path.join(TisPKI.intermediate_cert_path(ca_name),f'{dc_name}.crt') + dc_keyfile = os.path.join(TisPKI.intermediate_keyout_path(ca_name),f'{dc_name}.key') + dc_csrfile = os.path.join(TisPKI.intermediate_csr_path(ca_name),f'{dc_name}.csr') + dc_openssl_configfile = os.path.join(TisPKI.intermediate_config_path(ca_name),f'openssl_{dc_name}.ini') + crl_file = os.path.join(TisPKI.intermediate_crl_path(ca_name),'intermediate_ca.crl') + dc_ca_keyfile = TisPKI.intermediate_ca_keyfile(ca_name) + dc_ca_certfile = TisPKI.intermediate_ca_certfile(ca_name) + crl_uri = config.get('openssl_config','intermediate_crl_uri') + pki_dir = TisPKI.pki_intermediate_dir(ca_name) + else: + dc_certfile = os.path.join(TisPKI.root_cert_path(),f'{dc_name}.crt') + dc_keyfile = os.path.join(TisPKI.root_keyout_path(),f'{dc_name}.key') + dc_csrfile = os.path.join(TisPKI.root_csr_path(),f'{dc_name}.csr') + dc_openssl_configfile = os.path.join(TisPKI.root_config_path(),f'openssl_{dc_name}.ini') + crl_file = os.path.join(TisPKI.root_crl_path(),'root_ca.crl') + dc_ca_keyfile = os.path.join(TisPKI.root_keyout_path(),'root_ca.key') + dc_ca_certfile = os.path.join(TisPKI.root_cert_path(),'root_ca.crt') + crl_uri = config.get('openssl_config','crl_uri') + pki_dir = TisPKI.pki_dir() + + if not os.path.isfile(dc_certfile) and not os.path.isfile(dc_keyfile): + Printing.information(f'Generate certificate for {dc_name}') + + dc_guid = subprocess.run('/bin/bash get_guid.sh',shell=True, check=True, executable='/bin/bash') + if dc_guid.returncode != 0: + Printing.error('Unable to find dc_guid') + sys.exit(1) + else: + dc_name_guid = dc_name.split('.')[0] + dc_guid = str(subprocess.check_output(f'/bin/bash get_guid.sh {dc_name_guid}',shell=True).decode("utf-8")).strip() + + if verbose: + Printing.information(f'{dc_name} GUID is : ' + str(dc_guid).strip()) + + template_dir = ('templates') + jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) + dc_tmpl = jinja_env.get_template('openssl_server_cert.tmpl') + dc_tmpl_var = { + 'dc_name': dc_name, + 'dc_guid': str(dc_guid), + 'crl_uri': crl_uri, + 'pki_dir': pki_dir, + 'default_cert_duration': config.get('openssl_config','default_cert_duration'), + 'country': config.get('openssl_config','country'), + 'state': config.get('openssl_config','state'), + 'city': config.get('openssl_config','city'), + 'organization_name': config.get('openssl_config','organization_name'), + 'organization_ou': config.get('openssl_config','organization_ou'), + 'dc_ca_keyfile': dc_ca_keyfile, + 'dc_ca_certfile': dc_ca_certfile, + 'commonName': dc_name + + } + + config_string = dc_tmpl.render(dc_tmpl_var) + with open(dc_openssl_configfile,'wt') as file: + file.write(config_string) + + if os.path.isfile(dc_openssl_configfile): + Printing.information(f'{dc_name} OpenSSL configfile is correctly generated !') + + Printing.information(f'Generate private key and CSR for {dc_name}') + gen_dc_key = subprocess.run(f"openssl req -new -addext 'subjectAltName = email:copy' -newkey rsa:4096 -keyout {dc_keyfile} \ + -out {dc_csrfile} -config {dc_openssl_configfile}" , shell=True, check=False, executable='/bin/bash') + + if gen_dc_key.returncode == 0: + if os.path.isfile(dc_csrfile): + Printing.information(f'Sign certificate for {dc_name}') + sign_dc_cert = subprocess.run(f'openssl ca -config {dc_openssl_configfile} -extensions usr_cert_mskdc \ + -days 3650 -notext -md sha512 -create_serial -in {dc_csrfile} -out {dc_certfile}', shell=True, check=False, executable='/bin/bash') + + if sign_dc_cert.returncode == 0: + if os.path.isfile(dc_certfile): + print('Concatenation of DC and intermediate cert') + subprocess.run(f'cat {dc_certfile} {dc_ca_certfile} > {dc_certfile}_full',shell=True) + Printing.success(f'Certificat is stored in : {dc_certfile}') + Printing.success(f'Key is stored in : {dc_keyfile}') + else: + Printing.error('Error on sign Domain Controler certificate') + retry = input('If you want to retry, press Y : ') + if retry == "y" or retry == 'Y': + os.remove(dc_keyfile) + generate_dc_certificate(dc_name, ca_name, force, verbose) + else: + Printing.error('Error on generating Domain Controler private key') + retry = input('If you want to retry, press Y : ') + if retry == "y" or retry == 'Y': + os.remove(dc_keyfile) + generate_dc_certificate(dc_name, ca_name, force, verbose) \ No newline at end of file diff --git a/intermediate_module.py b/intermediate_module.py new file mode 100644 index 0000000..25405a7 --- /dev/null +++ b/intermediate_module.py @@ -0,0 +1,116 @@ +#!/usr/bin/python3 +#------------------------------------------------------------------------------- +# Name: Samba PKI Tools +# Purpose: +# +# Author: kguerineau-adm +# +# Created: 10/05/2024 +# Copyright: (c) kguerineau-adm 2024 +# Licence: +#------------------------------------------------------------------------------- + +# Maange Intermediate CA + +from common import Printing, TisPKI, check_directories + +import subprocess +import jinja2 +import os +import configparser +import sys +from colorama import Fore, Style +import shutil +import time + + +def create_openssl_intermediate(name, force=False,verbose=False): + +## if force: +## Printing.error("Do you realy want to remove ALL you PKI ? This will destroy ALL YOUR CERTIFICATES AND PRIVATE KEY") +## Printing.error("After that, you MUST REGENERATE YOUR PKI with NEW certificates and private key for ALL YOUR DOMAIN CONTROLLERS AND USERS") +## destroy = input('If you are realy sure, please enter : "I want to remove all my PKI" : ') +## if destroy == 'I want to remove all my PKI': +## Printing.information('OK, too late ! Destroying your PKI !') +## time.sleep(1) +## shutil.rmtree(TisPKI.pki_dir(), ignore_errors=True) + + + Printing.information(f'Create intermediate CA {name}') + + check_directories(path=TisPKI.pki_intermediate_dir(name),verbose=verbose) + intermediate_ca_config = os.path.join(TisPKI.intermediate_config_path(name),'create_intermediate_ca.ini') + intermediate_crl_file = os.path.join(TisPKI.intermediate_crl_path(name),f'{name}_intermediate_ca.crl') + root_ca_sign_intermediate = os.path.join(TisPKI.root_config_path(),'openssl_root_ca_sign_intermediate.ini') + + if not os.path.isfile(root_ca_sign_intermediate): + if TisPKI.intermediate_ca: + template_dir = os.path.join('templates') + jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) + root_ca_tmpl = jinja_env.get_template('openssl_sign_intermediate_csr.tmpl') + root_ca_tmpl_var = { + 'organization_ou': config.get('openssl_config','organization_name') + ' CA', + 'pki_dir': TisPKI.pki_dir(), + 'root_ca_keyfile': TisPKI.root_ca_keyfile(), + 'root_ca_certfile': TisPKI.root_ca_certfile(), + 'intermediate_crl_uri': config.get('openssl_config','intermediate_crl_uri'), + 'default_crl_duration' : config.get('openssl_config','default_crl_duration'), + } + + config_string = root_ca_tmpl.render(root_ca_tmpl_var) + with open(root_ca_sign_intermediate,'wt') as file: + file.write(config_string) + if os.path.isfile(root_ca_sign_intermediate): + Printing.success('Root CA OpenSSL sign intermediate config file is correctly generated !') + + if not os.path.isfile(intermediate_ca_config): + Printing.information('Intermediate CA OpenSSL configfile not exist. Creating...') + template_dir = os.path.join('templates') + jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) + intermediate_ca_tmpl = jinja_env.get_template('openssl_create_intermediate_ca.tmpl') + intermediate_ca_tmpl_var = { + 'country': config.get('openssl_config','country'), + 'state': config.get('openssl_config','state'), + 'city': config.get('openssl_config','city'), + 'organization_name': config.get('openssl_config','organization_name'), + 'organization_ou': config.get('openssl_config','organization_ou'), + 'intermediate_organization_cn': name, + 'intermediate_crl_uri': config.get('openssl_config','intermediate_crl_uri') + } + config_string = intermediate_ca_tmpl.render(intermediate_ca_tmpl_var) + with open(intermediate_ca_config,'wt') as file: + file.write(config_string) + if os.path.isfile(intermediate_ca_config): + Printing.success('Intermediate CA OpenSSL configfile is correctly generated !') + else: + Printing.information('Intermediate CA OpenSSL config already exist. Skip.') + + if not os.path.isfile(TisPKI.intermediate_ca_keyfile(name)) and not os.path.isfile(TisPKI.intermediate_ca_certfile(name)): + Printing.information('Generate Intermediate CA private key and CSR') + gen_intermediate_ca = subprocess.run(f"/usr/bin/openssl req -config {intermediate_ca_config} -new -sha512 -keyout {TisPKI.intermediate_ca_keyfile(name)} \ + -out {TisPKI.intermediate_csr_path(name)}/{name.replace(' ','_')}_intermediate_ca.csr", shell=True, check=False, executable='/bin/bash') + + if gen_intermediate_ca.returncode == 0: + Printing.information('Sign Intermediate CA with Root CA') + sign_intermediate_ca = subprocess.run(f"/usr/bin/openssl ca -config {os.path.join(TisPKI.root_config_path(),'openssl_root_ca_sign_intermediate.ini')} \ + -extensions v3_intermediate_ca -days 1825 -notext -md sha512 -create_serial -in {TisPKI.intermediate_csr_path(name)}/{name.replace(' ','_')}_intermediate_ca.csr \ + -out {TisPKI.intermediate_ca_certfile(name)}", shell=True, check=False, executable='/bin/bash') + + if sign_intermediate_ca.returncode == 0: + if verbose: + subprocess.run(f'openssl x509 -in {TisPKI.intermediate_ca_certfile(name)} -text', shell=True, check=True, executable='/bin/bash') + Printing.success(f'Intermediate CA Certfile is stored in : {TisPKI.intermediate_ca_certfile(name)}') + else: + Printing.error('Error on generating Intermediate CA private key') + retry = input('If you want to retry, press Y : ') + if retry == "y" or retry == 'Y': + os.remove(TisPKI.intermediate_ca_keyfile(name)) + create_openssl_intermediate(name, force, verbose) + else: + Printing.error('Error on generating Intermediate CA private key') + retry = input('If you want to retry, press Y : ') + if retry == "y" or retry == 'Y': + os.remove(TisPKI.intermediate_ca_keyfile(name)) + create_openssl_intermediate(name, force, verbose) + else: + Printing.warning('Intermediate CA private key and certificate already exist. Skip.') \ No newline at end of file diff --git a/manage_pki.py b/manage_pki.py index 07a15fd..2fc0937 100644 --- a/manage_pki.py +++ b/manage_pki.py @@ -13,6 +13,10 @@ from optparse import OptionParser, OptionGroup from argparse import ArgumentParser from common import * +from root_module import * +from intermediate_module import * +from dc_module import * +from user_module import * def main(): @@ -20,20 +24,17 @@ def main(): parser = ArgumentParser(prog='Samba PKI Tools', description='Manage PKI for Samba') - create_group = parser.add_argument_group('Create options') - create_group.add_argument('--initialize', dest="initialize", action="store_true", help="Create PKI") - create_group.add_argument('--full-initialize', dest="full_initialize", action="store_true", help="Create Root CA, intermediate CA and DC certificate. Use --name and --dc-name") - create_group.add_argument('--create-intermediate', dest="create_intermediate", action="store_true", help="Create an intermediate CA. Specify name with --name option.") - create_group.add_argument('--create-dc-cert', dest="create_dc_cert", action="store_true", help="Create a certificat for a Domain Controler") - root_group = parser.add_argument_group('Root CA options') + root_group.add_argument('--create-root', dest="initialize", action="store_true", help="Create PKI") + root_group.add_argument('--full-create', dest="full_initialize", action="store_true", help="Create Root CA, intermediate CA and DC certificate. Use --name and --dc-name") root_group.add_argument('--root-crl', dest='root_crl', help='Regenerate CRL for root CA') root_group.add_argument('--root-show-certs','--root-show-certificates', dest="root_certs", help='List all certificates issues of root CA') intermediate_group = parser.add_argument_group('Intermediate CA options', 'Manage intermediate CA') + intermediate_group.add_argument('--create-intermediate', dest="create_intermediate", action="store_true", help="Create an intermediate CA. Specify name with --name option.") intermediate_group.add_argument('--name', dest='intermediate_name', help='Specify what intermediate CA to manage') - intermediate_group.add_argument('--crl', dest='intermediate_crl', help='Regenerate CRL for intermediate CA') - intermediate_group.add_argument('--show-certs', '--show-certificates', dest='intermediate_list', help='List all certificates issues of intermediate CA') + intermediate_group.add_argument('--crl', dest='intermediate_crl', help='Regenerate CRL for intermediate CA. Specify name with --name option.') + intermediate_group.add_argument('--show-certs', '--show-certificates', dest='intermediate_list', help='List all certificates issues of intermediate CA. Specify name with --name option.') dc_cert = parser.add_argument_group('Domain Controler options', 'Manage DC certificates') dc_cert.add_argument('--dc-cert', dest="dc_cert", action="store_true", help="Create a DC certificate. Specify intermediate CA name with --name option. \ diff --git a/root_module.py b/root_module.py new file mode 100644 index 0000000..e6f707e --- /dev/null +++ b/root_module.py @@ -0,0 +1,91 @@ +#!/usr/bin/python3 +#------------------------------------------------------------------------------- +# Name: Samba PKI Tools +# Purpose: +# +# Author: kguerineau-adm +# +# Created: 10/05/2024 +# Copyright: (c) kguerineau-adm 2024 +# Licence: +#------------------------------------------------------------------------------- + +# Maange Root CA + +from common import Printing, TisPKI, check_directories + +import subprocess +import jinja2 +import os +import configparser +import sys +from colorama import Fore, Style +import shutil +import time + + +def create_openssl_config(force=False,verbose=False): + if force: + Printing.error("Do you realy want to remove ALL you PKI ? This will destroy ALL YOUR CERTIFICATES AND PRIVATE KEY") + Printing.error("After that, you MUST REGENERATE YOUR PKI with NEW certificates and private key for ALL YOUR DOMAIN CONTROLLERS AND USERS") + destroy = input('If you are realy sure, please enter : "I want to remove all my PKI" : ') + if destroy == 'I want to remove all my PKI': + Printing.information('OK, too late ! Destroying your PKI !') + time.sleep(1) + shutil.rmtree(TisPKI.pki_dir(), ignore_errors=True) + + Printing.information('Check Root CA OpenSSL Config') + + if config.get('general','pki_dir'): + check_directories(path=config.get('general','pki_dir'),verbose=verbose) + else: + Printing.error('No pki_dir set in samba-pki-tools.ini') + sys.exit(1) + + root_ca_config = os.path.join(TisPKI.pki_dir(),'config','openssl_root_ca.ini') + + if not os.path.isfile(root_ca_config): + Printing.information('Root CA OpenSSL configfile not exist. Creating...') + template_dir = os.path.join('templates') + jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) + root_ca_tmpl = jinja_env.get_template('openssl_root_ca.tmpl') + root_ca_tmpl_var = { + 'organization_ou': config.get('openssl_config','organization_name') + ' CA', + 'default_cert_duration': config.get('openssl_config','default_cert_duration'), + 'default_crl_duration' : config.get('openssl_config','default_crl_duration'), + 'pki_dir': TisPKI.pki_dir(), + 'country': config.get('openssl_config','country'), + 'state': config.get('openssl_config','state'), + 'city': config.get('openssl_config','city'), + 'organization_name': config.get('openssl_config','organization_name'), + 'organization_ou': config.get('openssl_config','organization_ou'), + 'organization_cn': config.get('openssl_config','organization_cn'), + 'crl_uri': config.get('openssl_config','crl_uri') + } + config_string = root_ca_tmpl.render(root_ca_tmpl_var) + with open(root_ca_config,'wt') as file: + file.write(config_string) + if os.path.isfile(root_ca_config): + Printing.success('Root CA OpenSSL config file is correctly generated !') + else: + Printing.warning('Root CA OpenSSL config already exist. Skip.') + + # Generate privkey and cert for Root CA + if not os.path.isfile(TisPKI.root_ca_keyfile()) or not os.path.isfile(TisPKI.root_ca_certfile()): + Printing.information('Generate Root CA private key') + gen_root_ca = subprocess.run(f'/usr/bin/openssl req -x509 -new -sha512 -config {root_ca_config} \ + -days 3650 -extensions v3_ca -keyout {TisPKI.root_ca_keyfile()} -out {TisPKI.root_ca_certfile()}', + shell=True, check=False, executable='/bin/bash') + + if gen_root_ca.returncode == 0: + if verbose: + subprocess.run(f'openssl x509 -in {TisPKI.root_ca_certfile()} -text', shell=True, check=True, executable='/bin/bash') + Printing.information(f'Root CA Certfile is stored in {TisPKI.root_ca_certfile()}') + else: + Printing.error('Error on generating Root CA private key') + retry = input('If you want to retry, press Y : ') + if retry == "y" or retry == 'Y': + os.remove(TisPKI.root_ca_keyfile()) + create_openssl_config(force,verbose) + else: + Printing.warning('Root CA private key and certificate already exist. Skip.') \ No newline at end of file diff --git a/user_module.py b/user_module.py new file mode 100644 index 0000000..d878241 --- /dev/null +++ b/user_module.py @@ -0,0 +1,95 @@ +#!/usr/bin/python3 +#------------------------------------------------------------------------------- +# Name: Samba PKI Tools +# Purpose: +# +# Author: kguerineau-adm +# +# Created: 10/05/2024 +# Copyright: (c) kguerineau-adm 2024 +# Licence: +#------------------------------------------------------------------------------- + +# Maange Users Certificates + +from common import Printing, TisPKI, check_directories + +import subprocess +import jinja2 +import os +import configparser +import sys +from colorama import Fore, Style +import shutil +import time + +def generate_user_certificate(): + default_bits_user = config.get('openssl_config','default_bits_user') + + username = input('Enter username') + + upn_suffix = config.get('samba_ad','upn_suffix') + upn = f'{username}@{upn_suffix}' + + if TisPKI.intermediate_ca: + crl_file = os.path.join(TisPKI.intermediate_crl_path(),'intermediate_ca.crl') + dc_ca_keyfile = os.path.join(TisPKI.intermediate_keyout_path(),'intermediate_ca.key') + dc_ca_certfile = os.path.join(TisPKI.intermediate_cert_path(),'intermediate_ca.crt') + crl_uri = config.get('openssl_config','intermediate_crl_uri') + pki_dir = TisPKI.pki_intermediate_dir() + openssl_user_file = os.path.join(TisPKI.intermediate_config_path(),'openssl_user.ini') + emailAddress = upn + commonName = username + else: + crl_file = os.path.join(TisPKI.root_crl_path(),'root_ca.crl') + dc_ca_keyfile = os.path.join(TisPKI.root_keyout_path(),'root_ca.key') + dc_ca_certfile = os.path.join(TisPKI.root_cert_path(),'root_ca.crt') + crl_uri = config.get('openssl_config','crl_uri') + pki_dir = TisPKI.pki_dir() + openssl_user_file = os.path.join(TisPKI.root_config_path(),'openssl_user.ini') + emailAddress = upn + commonName = username + + template_dir = ('templates') + jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) + user_tmpl = jinja_env.get_template('openssl_user_cert.tmpl') + user_tmpl_var = { + 'crl_uri': crl_uri, + 'pki_dir': pki_dir, + 'default_cert_duration': config.get('openssl_config','default_cert_duration'), + 'country': config.get('openssl_config','country'), + 'state': config.get('openssl_config','state'), + 'city': config.get('openssl_config','city'), + 'organization_name': config.get('openssl_config','organization_name'), + 'organization_ou': config.get('openssl_config','organization_ou'), + 'dc_ca_keyfile': dc_ca_keyfile, + 'dc_ca_certfile': dc_ca_certfile, + 'default_bits_user': config.get('openssl_config','default_bits_user'), + 'emailAddress' : emailAddress, + 'commonName' : commonName + + } + + config_string = user_tmpl.render(user_tmpl_var) + with open(openssl_user_file,'wt') as file: + file.write(config_string) + + if os.path.isfile(openssl_user_file): + print(f'User OpenSSL configfile is correctly generated !') + + + + + print(f'Generate private key for {upn}') + print(subprocess.run(f"openssl req -new -newkey rsa:{default_bits_user} -keyout {TisPKI.root_keyout_path()}/{username}.key -out {TisPKI.root_csr_path()}/{username}.csr -config <(cat {openssl_user_file} <(cat <<-EOF\n[ sanuser ]\notherName=msUPN;UTF8:{upn}\nemail=copy\nEOF\n)\n)",shell=True,check=True, executable='/bin/bash')) + + print(f'Sign certificate') + print(subprocess.run(f'openssl ca -extensions usr_cert_scarduser -days 730 -notext -md sha512 -create_serial -in {TisPKI.root_csr_path()}/{username}.csr -out {TisPKI.root_cert_path()}/{username}.crt -config <(cat {openssl_user_file} <(cat <<-EOF\n[ sanuser ]\notherName=msUPN;UTF8:{upn}\nemail=copy\nEOF\n)\n)',shell=True,check=True, executable='/bin/bash')) + + + print('Remove password in rsa key') + print(subprocess.run(f'openssl rsa -in {TisPKI.root_keyout_path()}/{username}.key -out {TisPKI.root_keyout_path()}/{username}-nopasswd.key',shell=True,check=True, executable='/bin/bash')) + + print('Create p12') + print(subprocess.run(f'openssl pkcs12 -export -inkey {TisPKI.root_keyout_path()}/{username}-nopasswd.key -in {TisPKI.root_cert_path()}/{username}.crt -out {TisPKI.root_p12_path()}/{username}.p12', shell=True,check=True, executable='/bin/bash')) +