Merge "Add remote signing for patch"

This commit is contained in:
Zuul 2024-11-19 14:34:49 +00:00 committed by Gerrit Code Review
commit 55eccd6dd7
2 changed files with 84 additions and 17 deletions

View File

@ -10,4 +10,9 @@ PATCH_SCRIPTS = {
"POST_INSTALL": "post-install.sh", "POST_INSTALL": "post-install.sh",
"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(
required=True) '--recipe',
@click.option('--name', help='Allow user to define name of the patch file. e.g.: test-sample-rr.patch. \ help='Patch recipe input XML file, examples are available under EXAMLES directory',
Name will default to patch_id if not defined', required=True)
required=False) @click.option(
def build(recipe, name=None): '--name',
patch_builder = PatchBuilder(recipe, 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',
required=False)
@click.option(
'--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__':