|
| 1 | +import requests |
| 2 | +import os |
| 3 | +import random |
| 4 | +import string |
| 5 | +import argparse |
| 6 | +from urllib.parse import quote |
| 7 | +from urllib3.exceptions import InsecureRequestWarning |
| 8 | + |
| 9 | +# Disable SSL warnings for self-signed certificates |
| 10 | +requests.packages.urllib3.disable_warnings(InsecureRequestWarning) |
| 11 | + |
| 12 | +def generate_secure_password(length=12): |
| 13 | + """Generate a secure password with at least one of each character type""" |
| 14 | + special_chars = "!@#$%^&*()-_=+" |
| 15 | + password_chars = ( |
| 16 | + random.choice(string.ascii_uppercase) + |
| 17 | + random.choice(string.ascii_lowercase) + |
| 18 | + random.choice(string.digits) + |
| 19 | + random.choice(special_chars) + |
| 20 | + ''.join(random.choices(string.ascii_letters + string.digits + special_chars, k=length - 4)) |
| 21 | + ) |
| 22 | + password_list = list(password_chars) |
| 23 | + random.shuffle(password_list) |
| 24 | + return ''.join(password_list) |
| 25 | + |
| 26 | +# Parse command line arguments |
| 27 | +parser = argparse.ArgumentParser(description='KMIP Client Setup for CipherTrust Manager') |
| 28 | +parser.add_argument('--admin-pass', required=True, help='Password for admin user') |
| 29 | +parser.add_argument('--ip', required=True, help='CTM IP address') |
| 30 | +parser.add_argument('--username', default='testmysql', help='Username to create (default: testmysql)') |
| 31 | +parser.add_argument('--client-name', default='mysql-client', help='KMIP client name (default: mysql-client)') |
| 32 | +parser.add_argument('--cert-dir', default='./ciper-kmip', help='Certificate store directory (default: ./certs)') |
| 33 | +parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose output') |
| 34 | +args = parser.parse_args() |
| 35 | + |
| 36 | +# Configuration |
| 37 | +CTM_IP = args.ip |
| 38 | +ADMIN_PASS = args.admin_pass |
| 39 | +USERNAME = args.username |
| 40 | +USER_PASS = generate_secure_password(12) # Generate secure password |
| 41 | +CLIENT_NAME = args.client_name |
| 42 | + |
| 43 | +# Base URL |
| 44 | +base_url = f"https://{CTM_IP}/api/v1" |
| 45 | + |
| 46 | +def make_request(method, endpoint, headers=None, data=None): |
| 47 | + """Helper function to make HTTP requests""" |
| 48 | + url = f"{base_url}{endpoint}" |
| 49 | + response = requests.request( |
| 50 | + method=method, |
| 51 | + url=url, |
| 52 | + headers=headers, |
| 53 | + json=data, |
| 54 | + verify=False # Skip SSL verification (equivalent to -k flag) |
| 55 | + ) |
| 56 | + return response |
| 57 | + |
| 58 | +# Get authentication token |
| 59 | +print("Getting authentication token...") |
| 60 | +auth_data = { |
| 61 | + "name": "admin", |
| 62 | + "password": ADMIN_PASS, |
| 63 | + "validity_period": 86400 |
| 64 | +} |
| 65 | + |
| 66 | +auth_response = make_request("POST", "/auth/tokens/", data=auth_data) |
| 67 | +if auth_response.status_code != 200: |
| 68 | + raise Exception(f"Authentication failed. Status: {auth_response.status_code}") |
| 69 | + |
| 70 | +jwt_token = auth_response.json()["jwt"] |
| 71 | +print("Authentication successful") |
| 72 | + |
| 73 | +# Set up headers with JWT token |
| 74 | +headers = { |
| 75 | + "Authorization": f"Bearer {jwt_token}", |
| 76 | + "Content-Type": "application/json" |
| 77 | +} |
| 78 | + |
| 79 | +# Get license information |
| 80 | +license_response = make_request("GET", "/licensing/trials/", headers=headers) |
| 81 | +license_data = license_response.json()['resources'][0] |
| 82 | + |
| 83 | +license_data = license_response.json()['resources'][0] |
| 84 | +license_status = license_data['status'] |
| 85 | +license_id = license_data['id'] |
| 86 | + |
| 87 | +# Activate only if new instance or deactivated state |
| 88 | +if license_status in ["deactivated", "available"]: |
| 89 | + activate_response = make_request("POST", f"/licensing/trials/{license_id}/activate", headers=headers) |
| 90 | + activate_response.raise_for_status() |
| 91 | + |
| 92 | +# Create user |
| 93 | +print(f"Creating user: {USERNAME}") |
| 94 | +user_data = { |
| 95 | + "username": USERNAME, |
| 96 | + "password": USER_PASS, |
| 97 | + "email": f"{USERNAME}@example.com" |
| 98 | +} |
| 99 | + |
| 100 | +user_response = make_request("POST", "/usermgmt/users/", headers=headers, data=user_data) |
| 101 | + |
| 102 | +if user_response.status_code == 201 or user_response.status_code == 200: |
| 103 | + user_response_json = user_response.json() |
| 104 | + user_id = user_response_json["user_id"] |
| 105 | + print("User created successfully") |
| 106 | +elif user_response.status_code == 409: # Conflict - user already exists |
| 107 | + print("User already exists, fetching user ID...") |
| 108 | + # Get user list to find the user ID |
| 109 | + users_response = make_request("GET", "/usermgmt/users/", headers=headers) |
| 110 | + users_list = users_response.json() |
| 111 | + for user in users_list.get("resources", []): |
| 112 | + if user.get("username") == USERNAME: |
| 113 | + user_id = user.get("user_id") |
| 114 | + print("Found existing user") |
| 115 | + break |
| 116 | + else: |
| 117 | + raise Exception(f"Could not find user ID for {USERNAME}") |
| 118 | +else: |
| 119 | + raise Exception(f"Failed to create user. Status: {user_response.status_code}") |
| 120 | + |
| 121 | +# Assign to Key Admins group |
| 122 | +print("Assigning user to Key Admins group...") |
| 123 | +# URL encode the user_id since it contains special characters like '|' |
| 124 | +encoded_user_id = quote(user_id, safe='') |
| 125 | +group_response = make_request("POST", f"/usermgmt/groups/Key%20Admins/users/{encoded_user_id}", headers=headers) |
| 126 | + |
| 127 | +if group_response.status_code not in [200, 201, 204]: |
| 128 | + print(f"Warning: Failed to add user to Key Admins group. Status: {group_response.status_code}") |
| 129 | +else: |
| 130 | + print("Successfully added user to Key Admins group") |
| 131 | + |
| 132 | +# Create profile for KMIP |
| 133 | +print("Creating KMIP profile...") |
| 134 | +profile_data = { |
| 135 | + "name": "mysql", |
| 136 | + "subject_dn_field_to_modify": "UID", |
| 137 | + "properties": { |
| 138 | + "cert_user_field": "CN" |
| 139 | + }, |
| 140 | + "device_credential": {} |
| 141 | +} |
| 142 | + |
| 143 | +profile_response = make_request("POST", "/kmip/kmip-profiles", headers=headers, data=profile_data) |
| 144 | + |
| 145 | +# Register the token for KMIP |
| 146 | +print("Registering KMIP token...") |
| 147 | +token_data = { |
| 148 | + "name_prefix": "mysql", |
| 149 | + "profile_name": "mysql", |
| 150 | + "max_clients": 100 |
| 151 | +} |
| 152 | + |
| 153 | +token_response = make_request("POST", "/kmip/regtokens/", headers=headers, data=token_data) |
| 154 | +if token_response.status_code not in [200, 201]: |
| 155 | + raise Exception(f"Token registration failed. Status: {token_response.status_code}") |
| 156 | + |
| 157 | +kmip_token = token_response.json()["token"] |
| 158 | +print("KMIP token registered successfully") |
| 159 | + |
| 160 | +# Adjust Interface for MySQL Auth/Cert mode |
| 161 | +print("Adjusting KMIP interface mode...") |
| 162 | +interface_data = {"mode": "tls-pw-opt"} |
| 163 | +interface_response = make_request("PATCH", "/configs/interfaces/kmip", headers=headers, data=interface_data) |
| 164 | + |
| 165 | +# Create the KMIP Client setup using the token |
| 166 | +print(f"Creating KMIP client: {CLIENT_NAME}") |
| 167 | +client_data = { |
| 168 | + "name": CLIENT_NAME, |
| 169 | + "reg_token": kmip_token |
| 170 | +} |
| 171 | + |
| 172 | +client_response = make_request("POST", "/kmip/kmip-clients", headers=headers, data=client_data) |
| 173 | + |
| 174 | +if client_response.status_code in [200, 201]: |
| 175 | + client_resp_json = client_response.json() |
| 176 | + print("KMIP client created successfully") |
| 177 | +elif client_response.status_code == 409: # Conflict - client already exists |
| 178 | + raise Exception(f"KMIP client '{CLIENT_NAME}' already exists. Please change the CLIENT_NAME variable in the script and try again.") |
| 179 | +else: |
| 180 | + raise Exception(f"KMIP client creation failed. Status: {client_response.status_code}") |
| 181 | + |
| 182 | +# Save certificate files |
| 183 | +print("Saving certificate files...") |
| 184 | + |
| 185 | +# Create the certificate directory if it doesn't exist |
| 186 | +os.makedirs(args.cert_dir, exist_ok=True) |
| 187 | + |
| 188 | +# Save private key to client_key.pem |
| 189 | +key_file_path = os.path.join(args.cert_dir, "client_key.pem") |
| 190 | +with open(key_file_path, "w") as key_file: |
| 191 | + key_file.write(client_resp_json["key"]) |
| 192 | + |
| 193 | +# Save certificate to client_certificate.pem |
| 194 | +cert_file_path = os.path.join(args.cert_dir, "client_certificate.pem") |
| 195 | +with open(cert_file_path, "w") as cert_file: |
| 196 | + cert_file.write(client_resp_json["cert"]) |
| 197 | + |
| 198 | +# Save CA certificate to root_certificate.pem |
| 199 | +ca_file_path = os.path.join(args.cert_dir, "root_certificate.pem") |
| 200 | +with open(ca_file_path, "w") as ca_file: |
| 201 | + ca_file.write(client_resp_json["client_ca"]) |
| 202 | + |
| 203 | +# Set restrictive permissions on private key file (equivalent to chmod 600) |
| 204 | +os.chmod(key_file_path, 0o600) |
| 205 | + |
| 206 | +if args.verbose: |
| 207 | + print("KMIP setup completed successfully!") |
| 208 | + print(f"Files created within {args.cert_dir}:") |
| 209 | + print(f" - Private key: {key_file_path} ") |
| 210 | + print(f" - Certificate: {cert_file_path}") |
| 211 | + print(f" - CA certificate: {ca_file_path}") |
0 commit comments