Merge "Implement IPsec Cert-Renewal Operation"

This commit is contained in:
Zuul 2024-03-11 20:03:25 +00:00 committed by Gerrit Code Review
commit 6945a0fd6b
6 changed files with 64 additions and 34 deletions

View File

@ -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='<opcode>',
type=int, choices=[1, 2, 3],
type=int, choices=[1, 2],
help='Operational code (Default: ' + str(opcode) + ')')
args = parser.parse_args()

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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("'")

View File

@ -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")