diff --git a/sysinv/sysinv/sysinv/sysinv/cmd/ipsec_client.py b/sysinv/sysinv/sysinv/sysinv/cmd/ipsec_client.py index e5dd6746a7..9213e5af99 100644 --- a/sysinv/sysinv/sysinv/sysinv/cmd/ipsec_client.py +++ b/sysinv/sysinv/sysinv/sysinv/cmd/ipsec_client.py @@ -46,7 +46,7 @@ def main(): help="If enabled, the logging level will be set " "to DEBUG instead of the default INFO level.") parser.add_argument("-o", "--opcode", metavar='', - type=int, choices=[1, 2, 3], + type=int, choices=[1, 2], help='Operational code (Default: ' + str(opcode) + ')') args = parser.parse_args() diff --git a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/client/client.py b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/client/client.py index 73487b73ee..5399160bf0 100644 --- a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/client/client.py +++ b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/client/client.py @@ -29,10 +29,10 @@ LOG = logging.getLogger(__name__) class Client(object): - def __init__(self, host, port, opcode): + def __init__(self, host, port, op_code): self.host = host self.port = port - self.opcode = opcode + self.op_code = str(op_code) self.state = State.STAGE_1 self.ifname = utils.get_management_interface() self.personality = utils.get_personality() @@ -45,7 +45,7 @@ class Client(object): # Generate message 1 - OP/MAC/HASH def _generate_message_1(self): message = {} - message['op'] = str(self.opcode) + message['op'] = self.op_code message['mac_addr'] = self.mac_addr message['hash'] = utils.hash_payload(message) @@ -159,12 +159,15 @@ class Client(object): if self.state == State.STAGE_4: LOG.info("Received IPSec Auth CSR Response") cert = base64.b64decode(msg['cert']) - network = msg['network'] digest = base64.b64decode(msg['hash']) ca_cert = utils.load_data(constants.TRUSTED_CA_CERT_PATH) - data = msg['cert'].encode('utf-8') + network.encode('utf-8') + data = msg['cert'].encode('utf-8') + if self.op_code == constants.OP_CODE_INITIAL_AUTH: + network = msg['network'] + data = data + network.encode('utf-8') + if not utils.verify_signed_hash(ca_cert, digest, data): msg = "Hash validation failed" LOG.exception("Hash validation failed") @@ -176,23 +179,46 @@ class Client(object): cert_path = constants.CERT_SYSTEM_LOCAL_DIR + cert_file utils.save_data(cert_path, cert) - if self.personality == constants.CONTROLLER: - self.local_addr = self.hostname[constants.UNIT_HOSTNAME] + ', ' \ - + self.hostname[constants.FLOATING_UNIT_HOSTNAME] - else: - self.local_addr = utils.get_ip_addr(self.ifname) + if self.op_code == constants.OP_CODE_INITIAL_AUTH: + if self.personality == constants.CONTROLLER: + self.local_addr = self.hostname[constants.UNIT_HOSTNAME] + ', ' \ + + self.hostname[constants.FLOATING_UNIT_HOSTNAME] + else: + self.local_addr = utils.get_ip_addr(self.ifname) - LOG.info("Generating config files and restart ipsec") - strong = config.StrongswanPuppet(self.hostname[constants.UNIT_HOSTNAME], - self.local_addr, network) - strong.generate_file() - puppet_cf = subprocess.run(['puppet', 'apply', '-e', 'include ::platform::strongswan'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) + LOG.info("Generating config files and restart ipsec") + strong = config.StrongswanPuppet(self.hostname[constants.UNIT_HOSTNAME], + self.local_addr, network) + strong.generate_file() + puppet_cf = subprocess.run(['puppet', 'apply', '-e', + 'include ::platform::strongswan'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + check=False) - if puppet_cf.stderr: - err = "Error: %s" % (puppet_cf.stderr.decode("utf-8")) - LOG.exception("Failed to create strongswan config files: %s" % err) - return False + if puppet_cf.returncode != 0: + err = "Error: %s" % (puppet_cf.stderr.decode("utf-8")) + LOG.exception("Failed to create StrongSwan config files: %s" % err) + return False + + elif self.op_code == constants.OP_CODE_CERT_RENEWAL: + load_creds = subprocess.run(['swanctl', '--load-creds'], stdout=subprocess.PIPE, + stderr=subprocess.PIPE, check=False) + + if load_creds.returncode != 0: + err = "Error: %s" % (load_creds.stderr.decode("utf-8")) + LOG.exception("Failed to load StrongSwan credentials: %s" % err) + return False + + rekey = subprocess.run(['swanctl', '--rekey', '--ike', constants.IKE_SA_NAME, + '--reauth'], stdout=subprocess.PIPE, + stderr=subprocess.PIPE, check=False) + + if rekey.returncode != 0: + err = "Error: %s" % (rekey.stderr.decode("utf-8")) + LOG.exception("Failed to rekey IKE SA with StrongSwan: %s" % err) + return False + + LOG.info('IPsec certificate renewed successfully') return True diff --git a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/client/config.py b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/client/config.py index 9fd908cce9..0242ff962b 100644 --- a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/client/config.py +++ b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/client/config.py @@ -90,11 +90,11 @@ class SwanctlConf(object): self.node[key] = value def get_conf(self): - self.children['node'] = self.node + self.children[constants.CHILD_SA_NAME] = self.node self.system_nodes['local'] = self.local self.system_nodes['remote'] = self.remote self.system_nodes['children'] = self.children - self.connections['system-nodes'] = self.system_nodes + self.connections[constants.IKE_SA_NAME] = self.system_nodes return self.connections diff --git a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/constants.py b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/constants.py index 5141fe73e4..4bde84f949 100644 --- a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/constants.py +++ b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/constants.py @@ -49,10 +49,12 @@ PXECONTROLLER_URL = 'http://pxecontroller:6385' OP_CODE_INITIAL_AUTH = "1" OP_CODE_CERT_RENEWAL = "2" - SUPPORTED_OP_CODES = [OP_CODE_INITIAL_AUTH, OP_CODE_CERT_RENEWAL] MGMT_IPSEC_ENABLING = 'enabling' MGMT_IPSEC_ENABLED = 'enabled' MGMT_IPSEC_DISABLED = 'disabled' + +CHILD_SA_NAME = 'node' +IKE_SA_NAME = 'system-nodes' diff --git a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/utils.py b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/utils.py index 1af2db6d66..ec626c185a 100644 --- a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/utils.py +++ b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/utils.py @@ -282,7 +282,7 @@ def kube_apply_certificate_request(body): elif get_cr.stderr and 'NotFound' not in str(get_cr.stderr): err = "Error: %s" % (get_cr.stderr.decode("utf-8")) LOG.exception("Failed to retrieve CertificateRequest resource info. %s" % (err)) - return + return None # Create CertificateRequest resource in kubernetes cr_body = yaml.safe_dump(body, default_flow_style=False) @@ -292,11 +292,11 @@ def kube_apply_certificate_request(body): stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) - if create_cr.stderr: + if create_cr.returncode != 0: err = "Error: %s" % (create_cr.stderr.decode("utf-8")) LOG.exception("Failed to create CertificateRequest %s/%s. %s" % (constants.NAMESPACE_DEPLOYMENT, name, err)) - return + return None # Get Certificate from recently created resource in kubernetes cmd_get_certificate = ['-o', "jsonpath='{.status.certificate}'"] @@ -305,10 +305,10 @@ def kube_apply_certificate_request(body): stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) - if signed_cert.stderr: + if signed_cert.returncode != 0: err = "Error: %s" % (signed_cert.stderr.decode("utf-8")) LOG.exception("Failed to retrieve %s/%s's Certificate. %s" % (constants.NAMESPACE_DEPLOYMENT, name, err)) - return + return None return signed_cert.stdout.decode("utf-8").strip("'") diff --git a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/server/server.py b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/server/server.py index 39680d8a01..3d50a183c1 100644 --- a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/server/server.py +++ b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/server/server.py @@ -90,6 +90,7 @@ class IPsecConnection(object): CA_CRT = 'tls.crt' def __init__(self): + self.op_code = None self.hostname = None self.mgmt_subnet = None self.signed_cert = None @@ -153,7 +154,6 @@ class IPsecConnection(object): raise ConnectionRefusedError(msg) client_data = utils.get_client_hostname_and_mgmt_subnet(mac_addr) - self.hostname = client_data['hostname'] self.mgmt_subnet = client_data['mgmt_subnet'] @@ -199,12 +199,14 @@ class IPsecConnection(object): if not self.signed_cert: raise ValueError('Unable to sign certificate request') - cert = bytes(self.signed_cert, 'utf-8') - network = bytes(self.mgmt_subnet, 'utf-8') - hash_payload = utils.hash_and_sign_payload(self.ca_key, cert + network) + data = bytes(self.signed_cert, 'utf-8') + if self.op_code == constants.OP_CODE_INITIAL_AUTH: + payload["network"] = self.mgmt_subnet + data = data + bytes(self.mgmt_subnet, 'utf-8') + + hash_payload = utils.hash_and_sign_payload(self.ca_key, data) payload["cert"] = self.signed_cert - payload["network"] = self.mgmt_subnet payload["hash"] = hash_payload.decode("utf-8") LOG.info("Sending IPSec Auth CSR Response")