From 8dd45a3d0f0a3b8f8818203cb189e833cdb9c4d7 Mon Sep 17 00:00:00 2001
From: Leonardo Mendes <Leonardo.MendesSantana@windriver.com>
Date: Thu, 9 May 2024 10:34:03 -0300
Subject: [PATCH] Store IPSec cert and keys in LUKS filesystem

This commit updated ipsec-client to store certs and keys in LUKS
filesystem. The encrypted filesystem take some time to mount and
decrypted, so a script was created to check if the filesystem is
ready before start ipsec.

Test Plan:
PASS: Bootstrap, install and unlock a DX system with a worker. Wait
      until system reboots and verify unlocked enable available status.
      Then, observe IPSec is enabled, SAs are established and the
      symbolic links were created in the directories /etc/swanctl/x509
      and /etc/swanctl/private pointing to file in the directory
      /var/luks/stx/luks_fs/ipsec/{certs,keys}.

Story: 2010940
Task: 50086

Change-Id: Ifccd747bd1db8f565d4744d99d94a61a22d5890e
Signed-off-by: Leonardo Mendes <Leonardo.MendesSantana@windriver.com>
---
 sysinv/sysinv/debian/deb_folder/rules         |  2 ++
 .../sysinv/debian/deb_folder/sysinv.install   |  1 +
 .../sysinv/scripts/check-ipsec-luks-dir.sh    | 14 +++++++++++
 .../sysinv/sysinv/sysinv/cmd/ipsec_client.py  |  5 ++--
 .../sysinv/sysinv/ipsec_auth/client/client.py | 23 ++++++++++++-------
 .../sysinv/ipsec_auth/common/constants.py     | 13 ++++++-----
 .../sysinv/sysinv/ipsec_auth/common/utils.py  | 14 +++++++++++
 ...rkerconfig-standalone.workerconfig.service |  1 +
 .../workerconfig/workerconfig.service         |  1 +
 9 files changed, 58 insertions(+), 16 deletions(-)
 create mode 100644 sysinv/sysinv/sysinv/scripts/check-ipsec-luks-dir.sh

diff --git a/sysinv/sysinv/debian/deb_folder/rules b/sysinv/sysinv/debian/deb_folder/rules
index c5cd8ef353..6f2198207a 100755
--- a/sysinv/sysinv/debian/deb_folder/rules
+++ b/sysinv/sysinv/debian/deb_folder/rules
@@ -27,6 +27,7 @@ override_dh_install:
 	install -p -D -m 700 $(CURDIR)/scripts/kube-cert-rotation.sh $(CURDIR)/debian/tmp/usr/bin/kube-cert-rotation.sh
 	install -p -D -m 700 $(CURDIR)/scripts/ipsec-cert-renew.sh $(CURDIR)/debian/tmp/usr/bin/ipsec-cert-renew.sh
 	install -p -D -m 700 $(CURDIR)/scripts/ipsec-swap-certificates.py $(CURDIR)/debian/tmp/usr/bin/ipsec-swap-certificates
+	install -p -D -m 700 $(CURDIR)/scripts/check-ipsec-luks-dir.sh $(CURDIR)/debian/tmp/usr/bin/check-ipsec-luks-dir.sh
 	dh_install
 
 override_dh_python3:
@@ -39,3 +40,4 @@ override_dh_installsystemd:
 override_dh_fixperms:
 	dh_fixperms -Xkube-cert-rotation.sh
 	dh_fixperms -Xipsec-cert-renew.sh
+	dh_fixperms -Xcheck-ipsec-luks-dir.sh
diff --git a/sysinv/sysinv/debian/deb_folder/sysinv.install b/sysinv/sysinv/debian/deb_folder/sysinv.install
index 1b6508b546..82d7504298 100644
--- a/sysinv/sysinv/debian/deb_folder/sysinv.install
+++ b/sysinv/sysinv/debian/deb_folder/sysinv.install
@@ -15,6 +15,7 @@ etc/sysinv/upgrades/delete_load.sh
 etc/update-motd.d/10-system
 usr/bin/cert-alarm
 usr/bin/cert-mon
+usr/bin/check-ipsec-luks-dir.sh
 usr/bin/ipsec-server
 usr/bin/ipsec-client
 usr/bin/kube-cert-rotation.sh
diff --git a/sysinv/sysinv/sysinv/scripts/check-ipsec-luks-dir.sh b/sysinv/sysinv/sysinv/scripts/check-ipsec-luks-dir.sh
new file mode 100644
index 0000000000..22c8f598bc
--- /dev/null
+++ b/sysinv/sysinv/sysinv/scripts/check-ipsec-luks-dir.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+FIRST_BOOT="/etc/platform/.first_boot"
+LUKS_DIR="/var/luks/stx/luks_fs/ipsec"
+
+if [ -e ${FIRST_BOOT} ]; then
+    exit 0
+fi
+
+test -d $LUKS_DIR
+while [ $? != 0 ]; do
+    sleep 1
+    test -d $LUKS_DIR
+done
diff --git a/sysinv/sysinv/sysinv/sysinv/cmd/ipsec_client.py b/sysinv/sysinv/sysinv/sysinv/cmd/ipsec_client.py
index 8862580c7a..70aa5ee0b0 100644
--- a/sysinv/sysinv/sysinv/sysinv/cmd/ipsec_client.py
+++ b/sysinv/sysinv/sysinv/sysinv/cmd/ipsec_client.py
@@ -80,8 +80,9 @@ def main():
 
     logging.setup(CONF, 'ipsec-client')
 
-    if not os.path.exists(constants.TMP_DIR_IPSEC_KEYS):
-        os.makedirs(constants.TMP_DIR_IPSEC_KEYS)
+    if not os.path.exists(constants.LUKS_DIR_IPSEC_KEYS):
+        os.makedirs(constants.LUKS_DIR_IPSEC_KEYS)
+        os.makedirs(constants.LUKS_DIR_IPSEC_CERTS)
 
     client = Client(host, port, opcode, force_reload)
     client.run()
diff --git a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/client/client.py b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/client/client.py
index 8891c175fc..10e566a70f 100644
--- a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/client/client.py
+++ b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/client/client.py
@@ -69,11 +69,14 @@ class Client(object):
             encryption_algorithm=serialization.NoEncryption()
         )
 
-        # TODO: Save PRK2 in LUKS Filesystem
         prk2_file = constants.CERT_NAME_PREFIX + \
                         self.hostname[constants.UNIT_HOSTNAME] + '.key'
+
+        prk2_luks_path = constants.LUKS_DIR_IPSEC_KEYS + prk2_file
         prk2_path = constants.CERT_SYSTEM_LOCAL_PRIVATE_DIR + prk2_file
-        utils.save_data(prk2_path, prk2_bytes)
+
+        utils.save_data(prk2_luks_path, prk2_bytes)
+        utils.create_symlink(prk2_luks_path, prk2_path)
 
         return prk2
 
@@ -81,8 +84,8 @@ class Client(object):
     def _generate_ak1(self, puk1_data):
         ak1 = os.urandom(32)
 
-        # TODO: Save AK1 in LUKS Filesystem
-        utils.save_data(constants.TMP_AK1_FILE, ak1)
+        # Save AK1 in LUKS Filesystem
+        utils.save_data(constants.LUKS_AK1_FILE, ak1)
 
         return ak1
 
@@ -101,7 +104,7 @@ class Client(object):
     def _generate_message_3(self):
         message = {}
 
-        puk1_data = utils.load_data(constants.TMP_PUK1_FILE)
+        puk1_data = utils.load_data(constants.LUKS_PUK1_FILE)
         puc_data = utils.load_data(constants.TRUSTED_CA_CERT_1_PATH)
 
         LOG.info("Generate RSA Private Key (PRK2).")
@@ -177,8 +180,9 @@ class Client(object):
                 LOG.exception("%s" % msg)
                 return False
 
-            utils.save_data(constants.TMP_PUK1_FILE, key)
-
+            utils.save_data(constants.LUKS_PUK1_FILE, key)
+            utils.save_data(constants.TRUSTED_ROOT_CA_CERT_1_PATH, root_ca_cert)
+            utils.save_data(constants.TRUSTED_CA_CERT_1_PATH, ca_cert)
             if self.op_code == constants.OP_CODE_INITIAL_AUTH:
                 utils.save_data(constants.TRUSTED_ROOT_CA_CERT_0_PATH, root_ca_cert)
                 utils.save_cert_bundle(ca_cert, constants.TRUSTED_CA_CERT_0_PREFIX)
@@ -211,8 +215,11 @@ class Client(object):
             cert_file = constants.CERT_NAME_PREFIX + \
                 self.hostname[constants.UNIT_HOSTNAME] + '.crt'
 
+            cert_luks_path = constants.LUKS_DIR_IPSEC_CERTS + cert_file
             cert_path = constants.CERT_SYSTEM_LOCAL_DIR + cert_file
-            utils.save_data(cert_path, cert)
+
+            utils.save_data(cert_luks_path, cert)
+            utils.create_symlink(cert_luks_path, cert_path)
 
             if self.op_code == constants.OP_CODE_INITIAL_AUTH:
                 if self.personality == constants.CONTROLLER:
diff --git a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/constants.py b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/constants.py
index c34c9bbff5..d71976c7e4 100644
--- a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/constants.py
+++ b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/constants.py
@@ -45,12 +45,13 @@ CERT_SYSTEM_LOCAL_DIR = '/etc/swanctl/x509/'
 CERT_SYSTEM_LOCAL_PRIVATE_DIR = '/etc/swanctl/private/'
 CERT_NAME_PREFIX = 'system-ipsec-certificate-'
 
-TMP_DIR_IPSEC = '/tmp/ipsec/'
-TMP_DIR_IPSEC_KEYS = TMP_DIR_IPSEC + 'keys/'
-TMP_FILE_IPSEC_PUK1 = 'puk1.crt'
-TMP_FILE_IPSEC_AK1_KEY = 'ak1.key'
-TMP_PUK1_FILE = TMP_DIR_IPSEC + TMP_FILE_IPSEC_PUK1
-TMP_AK1_FILE = TMP_DIR_IPSEC_KEYS + TMP_FILE_IPSEC_AK1_KEY
+LUKS_DIR_IPSEC = '/var/luks/stx/luks_fs/ipsec/'
+LUKS_DIR_IPSEC_KEYS = LUKS_DIR_IPSEC + 'keys/'
+LUKS_DIR_IPSEC_CERTS = LUKS_DIR_IPSEC + 'certs/'
+LUKS_FILE_IPSEC_PUK1 = 'puk1.key'
+LUKS_FILE_IPSEC_AK1_KEY = 'ak1.key'
+LUKS_PUK1_FILE = LUKS_DIR_IPSEC_KEYS + LUKS_FILE_IPSEC_PUK1
+LUKS_AK1_FILE = LUKS_DIR_IPSEC_KEYS + LUKS_FILE_IPSEC_AK1_KEY
 
 UNIT_HOSTNAME = 'unit_hostname'
 FLOATING_UNIT_HOSTNAME = 'floating_unit_hostname'
diff --git a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/utils.py b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/utils.py
index 91454442b0..a7cc581344 100644
--- a/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/utils.py
+++ b/sysinv/sysinv/sysinv/sysinv/ipsec_auth/common/utils.py
@@ -351,3 +351,17 @@ def save_cert_bundle(cert_data, cert_prefix):
         cert_path = get_ca_certificate_path(cert_prefix, index)
         save_data(cert_path, cert.public_bytes(encoding=serialization.Encoding.PEM))
         index += 1
+
+
+def create_symlink(src, dst):
+    if not os.path.exists(src):
+        return False
+
+    if os.path.exists(dst):
+        if src == dst:
+            return False
+        os.unlink(dst)
+
+    os.symlink(src, dst)
+
+    return os.path.exists(dst)
diff --git a/workerconfig/debian/deb_folder/workerconfig-standalone.workerconfig.service b/workerconfig/debian/deb_folder/workerconfig-standalone.workerconfig.service
index 97e6a1af38..afb6682827 100644
--- a/workerconfig/debian/deb_folder/workerconfig-standalone.workerconfig.service
+++ b/workerconfig/debian/deb_folder/workerconfig-standalone.workerconfig.service
@@ -6,6 +6,7 @@ After=affine-platform.sh.service
 After=opt-platform.service
 After=sysinv-agent.service
 After=network-online.target
+After=ipsec.service
 Before=config.service worker-config-gate.service
 Before=goenabled.service
 
diff --git a/workerconfig/workerconfig/workerconfig.service b/workerconfig/workerconfig/workerconfig.service
index 97e6a1af38..afb6682827 100644
--- a/workerconfig/workerconfig/workerconfig.service
+++ b/workerconfig/workerconfig/workerconfig.service
@@ -6,6 +6,7 @@ After=affine-platform.sh.service
 After=opt-platform.service
 After=sysinv-agent.service
 After=network-online.target
+After=ipsec.service
 Before=config.service worker-config-gate.service
 Before=goenabled.service