Add remote signing for patch

This change add the option --remote-sign for the patch-builder script
allowing the user to send the patch to a remote signing server, get the
it signed and download it signed.

Test plan:
    PASS: Build patch without signing it remotely
    PASS: Build patch signing it remotely

Story: 2010676
Task: 51341

Change-Id: I59631ea81f05133f47aa3036f6c9f29e1a02b9c2
Signed-off-by: Dostoievski Batista <dostoievski.albinobatista@windriver.com>
This commit is contained in:
Dostoievski Batista 2024-11-13 17:01:20 -03:00
parent 15cc010032
commit 6633f50248
2 changed files with 84 additions and 17 deletions

View File

@ -11,3 +11,8 @@ PATCH_SCRIPTS = {
"DEPLOY_PRECHECK": "deploy-precheck", "DEPLOY_PRECHECK": "deploy-precheck",
"UPGRADE_UTILS": "upgrade_utils.py", "UPGRADE_UTILS": "upgrade_utils.py",
} }
# Default path to the script that generates the upload path
GET_UPLOAD_PATH = "/opt/signing/sign.sh"
# Default path to the script that sign the patch
REQUEST_SIGN = "/opt/signing/sign_patch.sh"

View File

@ -34,18 +34,23 @@ utils.set_logger(logger)
detached_signature_file = "signature.v2" detached_signature_file = "signature.v2"
mdsum_signature_file = "signature" mdsum_signature_file = "signature"
# Signing server variables
signing_server = os.getenv('SIGNING_SERVER', '')
signing_user = os.getenv('SIGNING_USER', '')
# Patch output directory # Patch output directory
DEPLOY_DIR = "/localdisk/deploy" DEPLOY_DIR = "/localdisk/deploy"
PATCH_OUTPUT = os.path.join(DEPLOY_DIR, "patch_output") PATCH_OUTPUT = os.path.join(DEPLOY_DIR, "patch_output")
class PatchBuilder(object): class PatchBuilder(object):
def __init__(self, patch_recipe_file, file_name=None): def __init__(self, patch_recipe_file, file_name=None, sign_remote=False):
self.metadata = metadata.PatchMetadata(patch_recipe_file) self.metadata = metadata.PatchMetadata(patch_recipe_file)
self.metadata.parse_input_xml_data() self.metadata.parse_input_xml_data()
self.fetch_debs = fetch_debs.FetchDebs() self.fetch_debs = fetch_debs.FetchDebs()
self.fetch_debs.need_dl_stx_pkgs = self.metadata.stx_packages self.fetch_debs.need_dl_stx_pkgs = self.metadata.stx_packages
self.fetch_debs.need_dl_binary_pkgs = self.metadata.binary_packages self.fetch_debs.need_dl_binary_pkgs = self.metadata.binary_packages
self.patch_name = f'{self.metadata.patch_id}.patch' if file_name == None else file_name self.patch_name = f'{self.metadata.patch_id}.patch' if file_name == None else file_name
self.sign_remote = sign_remote
def get_md5(self, path): def get_md5(self, path):
''' '''
@ -267,30 +272,87 @@ class PatchBuilder(object):
tar.add(file) tar.add(file)
tar.close() tar.close()
logger.info(f"Patch file created {patch_full_path}") logger.info(f"Patch file created {patch_full_path}")
if self.sign_remote:
self.__sign_patch_remotely(patch_full_path)
def __sign_official_patches(self, patch_file): def __sign_patch_remotely(self, patch_file):
""" """
Sign formal patch Send the patch file to be signed remotely by a signing server
Called internally once a patch is created and formal flag is set to true
:param patch_file full path to the patch file :param patch_file full path to the patch file
""" """
logger.info("Signing patch %s", patch_file) logger.info("Starting remote signing for: %s", patch_file)
if not signing_server:
logger.exception("SIGNING_SERVER variable not set, unable to continue.")
sys.exit(1)
if not signing_user:
logger.exception("SIGNING_USER variable not set, unable to continue.")
sys.exit(1)
try: try:
subprocess.check_call(["sign_patch_formal.sh", patch_file]) conn_string = f"{signing_user}@{signing_server}"
patch_basename = os.path.basename(patch_file)
# First we get the upload path from the signing server, it should return something
# similar to: "Upload: /tmp/sign_upload.5jR11pS0"
call_path = subprocess.check_output([
"ssh",
"-o StrictHostKeyChecking=no",
conn_string,
f"sudo {constants.GET_UPLOAD_PATH} -r"]).decode(sys.stdout.encoding).strip()
upload_path = call_path.split()[1]
logger.info("Upload path receive from signing server: %s", upload_path)
# We send the patch to the signing server
logger.info("Sending patch to signing server...")
subprocess.check_output([
"scp",
"-q",
patch_file,
f"{conn_string}:{upload_path}"])
# Request the signing server to sign the patch, it should return the full path
# of the patch inside the signing server
logger.info("Signing patch...")
signed_patch_path = subprocess.check_output([
"ssh",
conn_string,
f"sudo {constants.REQUEST_SIGN}",
f"{upload_path}/{patch_basename}",
"usm"]).decode(sys.stdout.encoding).strip()
logger.info("Signing successful, path returned: %s", signed_patch_path)
logger.info("Downloading signed patch...")
subprocess.check_output([
"scp",
"-q",
f"{conn_string}:{signed_patch_path}",
patch_file])
logger.info("Patch successfully signed: %s", patch_file)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
logger.exception("Failed to sign official patch. Call to sign_patch_formal.sh process returned non-zero exit status %i", e.returncode) logger.exception("Failure to sign patch: %s", e)
except FileNotFoundError: except Exception as e:
logger.exception("sign_patch_formal.sh not found, make sure $STX_BUILD_HOME/repo/cgcs-root/build-tools is in the $PATH") logger.exception("An unexpected error has occurred when signing the patch: %s", e)
@click.command() @click.command()
@click.option('--recipe', help='Patch recipe input XML file, examples are available under EXAMLES directory', @click.option(
'--recipe',
help='Patch recipe input XML file, examples are available under EXAMLES directory',
required=True) required=True)
@click.option('--name', help='Allow user to define name of the patch file. e.g.: test-sample-rr.patch. \ @click.option(
'--name',
help='Allow user to define name of the patch file. e.g.: test-sample-rr.patch. \
Name will default to patch_id if not defined', Name will default to patch_id if not defined',
required=False) required=False)
def build(recipe, name=None): @click.option(
patch_builder = PatchBuilder(recipe, name) '--remote-sign',
help='Send the patch to defined SIGNING SERVER to be sign with an different key.',
is_flag=True,
required=False)
def build(recipe, name=None, remote_sign=False):
patch_builder = PatchBuilder(recipe, name, remote_sign)
patch_builder.build_patch() patch_builder.build_patch()
if __name__ == '__main__': if __name__ == '__main__':