|
| 1 | +import argparse |
| 2 | +import os |
| 3 | +import ssl |
| 4 | +import subprocess |
| 5 | +import time |
| 6 | + |
| 7 | +from cpapi import APIClient, APIClientArgs |
| 8 | +import http.client |
| 9 | +import json |
| 10 | +import logging |
| 11 | +import sys |
| 12 | +from logging.handlers import RotatingFileHandler |
| 13 | + |
| 14 | +CURL_CLI = '$MDS_FWDIR/bin/curl_cli' |
| 15 | + |
| 16 | +CONSENT_MESSAGE = "I wish to connect my Self-Hosted Security Management environment and Security Gateways to the" \ |
| 17 | + " Infinity Portal. These will share with Check Point information which may include personal data and" \ |
| 18 | + " which will be processed per Check Point’s Privacy Policy.\n" \ |
| 19 | + "I understand that when connecting multiple Security Managements, data may be shared between them." |
| 20 | + |
| 21 | +DEFAULT_API_PORT = 443 |
| 22 | +LOCAL_HOST_IP = "127.0.0.1" |
| 23 | +LOCAL_HOST = "localhost" |
| 24 | +SYSTEM_DATA = "System Data" |
| 25 | + |
| 26 | + |
| 27 | +CI_URL_MAP = { |
| 28 | + "ap": "cloudinfra-gw.ap.portal.checkpoint.com", |
| 29 | + "us": "cloudinfra-gw-us.portal.checkpoint.com", |
| 30 | + "eu": "cloudinfra-gw.portal.checkpoint.com", |
| 31 | + "uae": "cloudinfra-gw.ae.portal.checkpoint.com", |
| 32 | + "in": "cloudinfra-gw.in.portal.checkpoint.com" |
| 33 | +} |
| 34 | + |
| 35 | + |
| 36 | +def make_http_request(method, endpoint, headers, data=None): |
| 37 | + if server == LOCAL_HOST_IP or server == LOCAL_HOST: |
| 38 | + return make_http_request_from_server(method, endpoint, headers, data) |
| 39 | + return make_http_request_from_remote(method, endpoint, headers, data) |
| 40 | + |
| 41 | + |
| 42 | +def make_http_request_from_remote(method, endpoint, headers, data=None): |
| 43 | + conn = http.client.HTTPSConnection(ci_url) |
| 44 | + conn.request(method, endpoint, body=data, headers=headers) |
| 45 | + response = conn.getresponse() |
| 46 | + status_code = response.status |
| 47 | + content = response.read().decode() |
| 48 | + conn.close() |
| 49 | + |
| 50 | + return status_code, content |
| 51 | + |
| 52 | + |
| 53 | +def make_http_request_from_server(method, endpoint, headers, data=None): |
| 54 | + command = CURL_CLI + " --cacert $CPDIR/conf/ca-bundle-public-cloud.crt -w \"\\nCODE:%{http_code}\\n\"" |
| 55 | + command += f" -X {method} https://{ci_url}{endpoint}" |
| 56 | + for key, value in headers.items(): |
| 57 | + command += f" -H '{key}: {value}'" |
| 58 | + command += f" -d '{data}'" |
| 59 | + if proxy and proxy_port: |
| 60 | + command += f" -x http://{proxy}:{proxy_port}" |
| 61 | + |
| 62 | + p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 63 | + output, err = p.communicate() |
| 64 | + p.wait() |
| 65 | + if p.returncode != 0: |
| 66 | + logger.error("Failed to run command {}, failure reason is {} ".format(command, err)) |
| 67 | + logger.debug("json_string is {} ".format(output)) |
| 68 | + try: |
| 69 | + json_temp = output.decode("utf-8") |
| 70 | + code = json_temp.split("CODE:")[1] |
| 71 | + logger.info("Status code: " + code) |
| 72 | + json_temp = json_temp.strip("\\nCODE:"+code+"\\n") |
| 73 | + return int(code.strip()), json_temp |
| 74 | + |
| 75 | + except ValueError as ex: |
| 76 | + logger.error(output) |
| 77 | + |
| 78 | + |
| 79 | +def add_om_prem(jwt, domain): |
| 80 | + headers = { |
| 81 | + 'Content-Type': 'application/json', |
| 82 | + 'Authorization': 'Bearer {}'.format(jwt), |
| 83 | + 'User-Agent': 'connect-all-domains' |
| 84 | + } |
| 85 | + |
| 86 | + data = json.dumps({ |
| 87 | + "name": domain, |
| 88 | + "isCloudMgmtService": False, |
| 89 | + "consent": True |
| 90 | + }) |
| 91 | + |
| 92 | + code, content = make_http_request("POST", "/app/maas/api/v2/environments/", headers, data) |
| 93 | + |
| 94 | + if code == 200: |
| 95 | + logger.info(f'Successfully added MGMT instance of domain: {domain}') |
| 96 | + content = json.loads(content) |
| 97 | + return content['data']['authToken']['token'] |
| 98 | + else: |
| 99 | + logger.error(f'Failed to add MGMT instance for domain {domain}') |
| 100 | + return None |
| 101 | + |
| 102 | + |
| 103 | +def get_jwt(): |
| 104 | + headers = { |
| 105 | + 'Content-Type': 'application/json', |
| 106 | + 'User-Agent': 'connect-all-domains' |
| 107 | + } |
| 108 | + |
| 109 | + data = json.dumps({ |
| 110 | + "clientId": client_id, |
| 111 | + "accessKey": access_key |
| 112 | + }) |
| 113 | + |
| 114 | + code, content = make_http_request("POST", "/auth/external", headers, data) |
| 115 | + if code == 200 or (code == 302 and proxy and proxy_port): |
| 116 | + logger.info("Successfully created external jwt from given keys") |
| 117 | + content = json.loads(content) |
| 118 | + return content['data']['token'] |
| 119 | + else: |
| 120 | + logger.error("Failed to create external jwt from given keys with the following error: {}".format(content)) |
| 121 | + exit(1) |
| 122 | + |
| 123 | + |
| 124 | +def logger_configuration(): |
| 125 | + log_backup_count = 3 |
| 126 | + script_dir = os.path.dirname(os.path.abspath(__file__)) |
| 127 | + log_file = os.path.join(script_dir, 'connect_all_domains.log') |
| 128 | + log_max_bytes = 10_000_000_000 |
| 129 | + log_formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s") |
| 130 | + logging_handler = RotatingFileHandler(log_file, maxBytes=log_max_bytes, backupCount=log_backup_count) |
| 131 | + logging_handler.setFormatter(log_formatter) |
| 132 | + logging_handler.setLevel(logging.DEBUG) |
| 133 | + logger = logging.getLogger('root') |
| 134 | + logger.setLevel(logging.INFO) |
| 135 | + logger.addHandler(logging_handler) |
| 136 | + |
| 137 | + return logger |
| 138 | + |
| 139 | + |
| 140 | +def user_consent(): |
| 141 | + print("Connect my Self-hosted Security Management environment and Security Gateways to Infinity") |
| 142 | + print(CONSENT_MESSAGE) |
| 143 | + consent = input("Do you agree? y/n: ") |
| 144 | + logger.info("User must agree to the following message:\n {}".format(CONSENT_MESSAGE)) |
| 145 | + logger.info("User's response: {}".format(consent)) |
| 146 | + if consent.lower() != "y": |
| 147 | + raise Exception("Consent must be given in order to run this script") |
| 148 | + |
| 149 | + |
| 150 | +def get_args(args): |
| 151 | + if args: |
| 152 | + parser = argparse.ArgumentParser() |
| 153 | + parser.add_argument("--client_id", type=str, action="store", help="Client Id", dest="client_id", required=True) |
| 154 | + parser.add_argument("--access_key", type=str, action="store", help="Access Key", dest="access_key", |
| 155 | + required=True) |
| 156 | + parser.add_argument("--region", type=str, action="store", help="Region", dest="region", |
| 157 | + choices=['ap', 'us', 'eu', 'uae', 'in'], required=True) |
| 158 | + parser.add_argument("--server", type=str, action="store", help="Server IP address or hostname, must be given " |
| 159 | + "when running from remote", dest="server") |
| 160 | + parser.add_argument("--api_key", type=str, action="store", |
| 161 | + help="Api-Key must be given when running from remote", dest="api_key") |
| 162 | + parser.add_argument("--api_port", type=str, action="store", |
| 163 | + help="Api Port must be given if running from remote and it isn't the default value", |
| 164 | + dest="api_port") |
| 165 | + parser.add_argument("--debug_file", type=str, action="store", help="Api calls debug file name", dest="debug_file") |
| 166 | + |
| 167 | + args = parser.parse_args() |
| 168 | + client_id = args.client_id |
| 169 | + access_key = args.access_key |
| 170 | + region = args.region |
| 171 | + server = args.server if args.server else LOCAL_HOST_IP |
| 172 | + api_key = args.api_key |
| 173 | + api_port = args.api_port |
| 174 | + debug_file = args.debug_file if args.debug_file else "" |
| 175 | + |
| 176 | + return client_id, access_key, region, server, api_key, api_port, debug_file |
| 177 | + |
| 178 | + else: |
| 179 | + logger.error("No arguments given") |
| 180 | + exit(1) |
| 181 | + |
| 182 | + |
| 183 | +def login(domain): |
| 184 | + logger.info("Logging in to domain {} of server {}...".format(domain, server)) |
| 185 | + payload = {"session-name": "connect all domains to infinity portal", |
| 186 | + "session-description": "connect all domains to infinity portal"} |
| 187 | + if api_key: |
| 188 | + login_res = api_client.login_with_api_key(api_key=api_key, domain=domain, payload=payload) |
| 189 | + else: |
| 190 | + login_res = api_client.login_as_root(domain=domain, payload=payload) |
| 191 | + if login_res.success is False: |
| 192 | + logger.error("Login failed:\n{}".format(login_res.error_message)) |
| 193 | + if domain == SYSTEM_DATA: |
| 194 | + exit(1) |
| 195 | + return login_res.success |
| 196 | + |
| 197 | + |
| 198 | +def get_proxy(): |
| 199 | + proxy_reply = api_client.api_call("show-proxy") |
| 200 | + if proxy_reply.success is False: |
| 201 | + logger.error("Failed to get proxy data:\n{}".format(proxy_reply.error_message)) |
| 202 | + api_client.api_call("logout", {}) |
| 203 | + exit(1) |
| 204 | + if proxy_reply.data["enabled"]: |
| 205 | + return proxy_reply.data["address"], proxy_reply.data["port"] |
| 206 | + else: |
| 207 | + return None, None |
| 208 | + |
| 209 | + |
| 210 | +def get_port(): |
| 211 | + script_path = os.path.expandvars("$MDS_FWDIR/scripts/api_get_port.py") |
| 212 | + try: |
| 213 | + result = subprocess.run(['python3', script_path, '-f', 'json'], capture_output=True, text=True, check=True) |
| 214 | + json_data = json.loads(result.stdout) |
| 215 | + return int(json_data["external_port"]) |
| 216 | + except subprocess.CalledProcessError as e: |
| 217 | + print(f"Error running script: {e}") |
| 218 | + except json.JSONDecodeError as e: |
| 219 | + print(f"Error parsing JSON: {e}") |
| 220 | + return DEFAULT_API_PORT |
| 221 | + |
| 222 | + |
| 223 | +def get_domains(): |
| 224 | + domains = api_client.api_query("show-domains") |
| 225 | + if domains.success is False: |
| 226 | + logger.error("Failed to get the domains data:\n{}".format(domains.error_message)) |
| 227 | + api_client.api_call("logout", {}) |
| 228 | + exit(1) |
| 229 | + return domains.data |
| 230 | + |
| 231 | + |
| 232 | +def connect_cloud_services(domain, auth_token): |
| 233 | + res = login(domain) |
| 234 | + if res is False: |
| 235 | + return res |
| 236 | + connect_reply = api_client.api_call("connect-cloud-services", {"auth-token": auth_token}) |
| 237 | + connected = False |
| 238 | + if connect_reply.success is False: |
| 239 | + logger.error( |
| 240 | + "Failed to run connect-cloud-services on domain {}:\n{}".format(domain, connect_reply.error_message)) |
| 241 | + print("Failed to connect domain: {}".format(domain)) |
| 242 | + else: |
| 243 | + logger.info("Successfully connected cloud services on domain {}".format(domain)) |
| 244 | + print("Successfully connected domain: {}".format(domain)) |
| 245 | + connected = True |
| 246 | + api_client.api_call("logout", {}) |
| 247 | + return connected |
| 248 | + |
| 249 | + |
| 250 | +if __name__ == "__main__": |
| 251 | + logger = logger_configuration() |
| 252 | + logger.info("=====================================================================================================") |
| 253 | + print("=====================================================================================================") |
| 254 | + user_consent() |
| 255 | + client_id, access_key, region, server, api_key, api_port, debug_file = get_args(sys.argv[1:]) |
| 256 | + ci_url = CI_URL_MAP.get(region) |
| 257 | + api_client = APIClient(APIClientArgs(server=server, debug_file=debug_file)) |
| 258 | + if server != LOCAL_HOST and server != LOCAL_HOST_IP: # if running from remote |
| 259 | + api_port = int(api_port) if api_port else DEFAULT_API_PORT |
| 260 | + else: |
| 261 | + api_port = int(api_port) if api_port else get_port() |
| 262 | + api_client.set_port(api_port) |
| 263 | + api_client.user_agent = "connect-all-domains" |
| 264 | + logger.info("=====================================================================================================") |
| 265 | + |
| 266 | + login(SYSTEM_DATA) |
| 267 | + if server == LOCAL_HOST_IP or server == LOCAL_HOST: |
| 268 | + proxy, proxy_port = get_proxy() |
| 269 | + else: |
| 270 | + proxy = None |
| 271 | + proxy_port = None |
| 272 | + domains = get_domains() |
| 273 | + api_client.api_call("logout", {}) |
| 274 | + |
| 275 | + jwt = get_jwt() |
| 276 | + |
| 277 | + successful_connections = [] |
| 278 | + failed_connections = [] |
| 279 | + |
| 280 | + for domain in domains: |
| 281 | + domain_name = domain["name"] |
| 282 | + logger.info("==========================================================") |
| 283 | + logger.info("Starting to connect-cloud-services on domain {}".format(domain_name)) |
| 284 | + auth_token = add_om_prem(jwt, domain_name) |
| 285 | + if auth_token is None: |
| 286 | + failed_connections.append(domain_name) |
| 287 | + continue |
| 288 | + connect_success = connect_cloud_services(domain_name, auth_token) |
| 289 | + if connect_success: |
| 290 | + successful_connections.append(domain_name) |
| 291 | + else: |
| 292 | + failed_connections.append(domain_name) |
| 293 | + time.sleep(3) |
| 294 | + api_client.save_debug_data() |
| 295 | + logger.info("==========================================================\nScript finished") |
| 296 | + print("=====================================================================================================") |
| 297 | + if len(successful_connections) == len(domains): |
| 298 | + print("Successfully connected all domains") |
| 299 | + exit(0) |
| 300 | + if len(successful_connections) > 0: |
| 301 | + print("Successfully connected the following domains:") |
| 302 | + for name in successful_connections: |
| 303 | + print(name) |
| 304 | + if len(failed_connections) > 0: |
| 305 | + print("Failed to connect the following domains:") |
| 306 | + for name in failed_connections: |
| 307 | + print(name) |
| 308 | + exit(1) |
0 commit comments