Implement IPsec Cert-Renewal Operation
This commit adds IPsec Cert-Renewal implementation to work properly when specified by "--opcode" parameter in IPsec client execution. This implementation adds to IPsec client a rekey step after the generated keys and cert are stored and exchanged during cert-renewal operation. The main goal of this implementation is to provide new certificates and keys for an IPsec client host that has already been authenticated by IPsec server host. Test Plan: PASS: Full build, system install, bootstrap and unlock DX system w/ unlocked enabled available status. PASS: Execute "ipsec-client pxecontroller --opcode 2" in controller-1. Observe the previously created CertificateRequest was deleted and generated a new one for controller-1's node. The new certificate is sent to IPsec Client and stored with the swanctl rekey command executed sucessfully. PASS: In a DC system with available enabled active status with IPsec server being executed from controller-0. Change c0 and c1 dates to expire IPsec certificates. If needed, recover kubernetes certificates or pods. Execute "sudo ipsec-client pxecontroller -o 2" command from controller-0 and controller-1. Observe that certificates and keys were generated and stored in /etc/swanctl/ directory. Observe new SAs have been created between controllers by executing "sudo swanctl --list-sas" command. Story: 2010940 Task: 49656 Change-Id: I69383005c2e204fe0a6401b2efaf05e8754f2bc3 Signed-off-by: Manoel Benedito Neto <Manoel.BeneditoNeto@windriver.com>
This commit is contained in:
parent
eb27f7bbf3
commit
937ce744b0
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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("'")
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue