#!/usr/bin/env python3 """ Interactive Certificate Manager Generates CSR on remote host and signs it with UCS CA """ import os import sys import json import subprocess from pathlib import Path # Configuration CONFIG_FILE = Path.home() / '.cert-manager-config.json' DEFAULT_CONFIG = { 'country': 'DE', 'state': 'berlin', 'locality': 'berlin', 'organization': 'egonetix', 'organizational_unit': 'it', 'ca_server': '10.0.0.21', 'validity_days': '3650', 'last_target_host': '', 'last_common_name': '' } def load_config(): """Load configuration from file or return defaults""" if CONFIG_FILE.exists(): try: with open(CONFIG_FILE, 'r') as f: config = json.load(f) # Merge with defaults to add any new fields return {**DEFAULT_CONFIG, **config} except Exception as e: print(f"Warning: Could not load config: {e}") return DEFAULT_CONFIG.copy() def save_config(config): """Save configuration to file""" try: with open(CONFIG_FILE, 'w') as f: json.dump(config, f, indent=2) except Exception as e: print(f"Warning: Could not save config: {e}") def prompt_with_default(prompt, default): """Prompt user with a default value""" if default: user_input = input(f"{prompt} [{default}]: ").strip() return user_input if user_input else default else: return input(f"{prompt}: ").strip() def yes_no_prompt(prompt, default=True): """Ask a yes/no question""" default_str = "Y/n" if default else "y/N" while True: response = input(f"{prompt} [{default_str}]: ").strip().lower() if not response: return default if response in ['y', 'yes']: return True if response in ['n', 'no']: return False print("Please answer 'y' or 'n'") def main(): print("=" * 60) print("Interactive Certificate Manager") print("=" * 60) print() # Load config config = load_config() # Ask if user wants to modify defaults if CONFIG_FILE.exists(): if yes_no_prompt("Do you want to modify default values?", False): print("\n--- Default Values Configuration ---") config['country'] = prompt_with_default("Country (C)", config['country']) config['state'] = prompt_with_default("State/Province (ST)", config['state']) config['locality'] = prompt_with_default("Locality (L)", config['locality']) config['organization'] = prompt_with_default("Organization (O)", config['organization']) config['organizational_unit'] = prompt_with_default("Organizational Unit (OU)", config['organizational_unit']) config['ca_server'] = prompt_with_default("CA Server", config['ca_server']) config['validity_days'] = prompt_with_default("Validity (days)", config['validity_days']) print() # Get certificate details print("--- Certificate Details ---") target_host = prompt_with_default("Target Host (IP or hostname)", config['last_target_host']) if not target_host: print("Error: Target host is required!") sys.exit(1) common_name = prompt_with_default("Common Name (FQDN)", config['last_common_name']) if not common_name: print("Error: Common name is required!") sys.exit(1) # Extract short name for filenames short_name = common_name.split('.')[0] # Ask for custom values for this certificate print("\n--- Certificate Subject (press Enter to use defaults) ---") country = prompt_with_default("Country (C)", config['country']) state = prompt_with_default("State/Province (ST)", config['state']) locality = prompt_with_default("Locality (L)", config['locality']) organization = prompt_with_default("Organization (O)", config['organization']) org_unit = prompt_with_default("Organizational Unit (OU)", config['organizational_unit']) validity_days = prompt_with_default("Validity (days)", config['validity_days']) print("\n" + "=" * 60) print("Summary:") print("=" * 60) print(f"Target Host: {target_host}") print(f"Common Name: {common_name}") print(f"Country: {country}") print(f"State: {state}") print(f"Locality: {locality}") print(f"Organization: {organization}") print(f"Org Unit: {org_unit}") print(f"Validity: {validity_days} days") print(f"CA Server: {config['ca_server']}") print(f"Output files: {short_name}.req, {short_name}-cert.pem") print("=" * 60) print() if not yes_no_prompt("Proceed with certificate generation?", True): print("Cancelled.") sys.exit(0) # Save config for next run config['last_target_host'] = target_host config['last_common_name'] = common_name save_config(config) # Get script directory script_dir = Path(__file__).parent.absolute() print("\n" + "=" * 60) print("Step 1: Generating CSR on target host") print("=" * 60) # Run generate-csr.sh generate_cmd = [ str(script_dir / 'generate-csr.sh'), target_host, common_name, country, state, locality, organization, org_unit ] try: result = subprocess.run(generate_cmd, check=True) except subprocess.CalledProcessError as e: print(f"\nError: CSR generation failed with exit code {e.returncode}") sys.exit(1) except FileNotFoundError: print(f"\nError: generate-csr.sh not found in {script_dir}") sys.exit(1) print("\n" + "=" * 60) print("Step 2: Signing certificate with CA") print("=" * 60) # Run sign-cert.sh req_file = f"{short_name}.req" sign_cmd = [ str(script_dir / 'sign-cert.sh'), req_file, short_name, validity_days ] try: result = subprocess.run(sign_cmd, check=True) except subprocess.CalledProcessError as e: print(f"\nError: Certificate signing failed with exit code {e.returncode}") sys.exit(1) except FileNotFoundError: print(f"\nError: sign-cert.sh not found in {script_dir}") sys.exit(1) print("\n" + "=" * 60) print("Step 3: Deploying certificate to target host") print("=" * 60) cert_file = f"{short_name}-cert.pem" if yes_no_prompt("Do you want to copy the certificate back to the target host?", True): try: # Copy certificate to target subprocess.run(['scp', cert_file, f'root@{target_host}:/tmp/{short_name}.crt'], check=True) print(f"\nāœ“ Certificate copied to target host at /tmp/{short_name}.crt") print(f" Private key is at /tmp/{short_name}.key") except subprocess.CalledProcessError: print("\nWarning: Failed to copy certificate to target host") print("\n" + "=" * 60) print("āœ“ Certificate Management Complete!") print("=" * 60) print(f"\nFiles created:") print(f" - {req_file} (Certificate Request)") print(f" - {cert_file} (Signed Certificate)") print(f"\nOn target host ({target_host}):") print(f" - /tmp/{short_name}.key (Private Key)") print(f" - /tmp/{short_name}.crt (Certificate)") print("\n") if __name__ == '__main__': try: main() except KeyboardInterrupt: print("\n\nCancelled by user.") sys.exit(1) except Exception as e: print(f"\nError: {e}") sys.exit(1)