diff --git a/.github/platform-tg-infra.code-workspace b/.github/platform-tg-infra.code-workspace index 303b8f7..d51c331 100644 --- a/.github/platform-tg-infra.code-workspace +++ b/.github/platform-tg-infra.code-workspace @@ -98,12 +98,6 @@ }, { "path": "../../243219719746-lab-gov-management-nonprod" - }, - { - "path": "../../../terraform-modules/aws-s3" - }, - { - "path": "../../karpenter-provider-aws" } ] } diff --git a/Makefile b/Makefile index 52d1e72..1be2527 100644 --- a/Makefile +++ b/Makefile @@ -73,6 +73,9 @@ deploy-to-pipeline: exit 1; \ fi + @echo "Copy buildspecs from tfmod-pipeline" + cp -r ../tfmod-pipeline/buildspecs/* ./buildspecs + @echo "Creating zip file..." zip -r platform-tg-infra.zip . -x "*.git*" "*.github*" "*.terragrunt-cache*" "*.terraform*" @@ -82,7 +85,7 @@ deploy-to-pipeline: $(eval OBJECT_KEY=clusters/$(CLUSTER_NAME)/platform-tg-infra.zip) @echo "Uploading to S3 bucket $(S3_BUCKET)..." - aws s3 cp platform-tg-infra.zip s3://$(S3_BUCKET)/$(OBJECT_KEY) --profile $(AWS_PROFILE) + aws s3 cp platform-tg-infra.zip s3://$(S3_BUCKET)/$(OBJECT_KEY) --profile $(AWS_PROFILE) --sse aws:kms @echo "Upload complete. Pipeline should trigger automatically." @echo "Calculating pipeline URL..." diff --git a/buildspecs/scripts/manage_tools.sh b/buildspecs/scripts/manage_tools.sh new file mode 100644 index 0000000..19d2c4a --- /dev/null +++ b/buildspecs/scripts/manage_tools.sh @@ -0,0 +1,220 @@ +#!/bin/bash +set -e # Exit immediately if a command exits with a non-zero status. + +echo "--- Starting Tool Management Script (manage_tools.sh) ---" + +# --- Configuration & Environment Variables --- +# Required environment variables: +# - TOOL_DEFINITIONS: JSON string defining tools (name, s3_key, checksum, archive_format, executable_path_in_archive) +# - ARTIFACTS_BUCKET: S3 bucket name for downloading tool archives +# - REQUIRED_TOOLS: Space-separated list of tool names to install (e.g., "terraform terragrunt") +# - CODEBUILD_SRC_DIR: Base directory for caching (CodeBuild specific, but adaptable) + +TOOL_CACHE_DIR="${CODEBUILD_SRC_DIR}/.tool_cache" +INSTALL_DIR="/usr/local/bin" # Standard installation directory for executables + +# --- Sanity Checks --- +if [ -z "$TOOL_DEFINITIONS" ]; then + echo "ERROR: TOOL_DEFINITIONS environment variable is not set or is empty." >&2 + exit 1 +fi + +if ! echo "$TOOL_DEFINITIONS" | jq empty > /dev/null 2>&1; then + echo "ERROR: TOOL_DEFINITIONS does not contain valid JSON." >&2 + echo "TOOL_DEFINITIONS content: $TOOL_DEFINITIONS" >&2 + exit 1 +fi + +if [ -z "$ARTIFACTS_BUCKET" ]; then + echo "ERROR: ARTIFACTS_BUCKET environment variable is not set or is empty." >&2 + exit 1 +fi + +if [ -z "$REQUIRED_TOOLS" ]; then + echo "WARNING: REQUIRED_TOOLS environment variable is not set or is empty. No tools will be installed by this script." >&2 + # exit 0 # or exit 1 depending on desired strictness +fi + +if [ -z "$CODEBUILD_SRC_DIR" ]; then + echo "ERROR: CODEBUILD_SRC_DIR environment variable is not set or is empty." >&2 + exit 1 +fi + +# --- Ensure jq is available --- +if ! command -v jq &> /dev/null; then + echo "jq not found, attempting to install..." + if apt-get update -y && apt-get install -y jq; then + echo "jq installed successfully via apt-get." + elif yum install -y jq; then + echo "jq installed successfully via yum." + else + echo "ERROR: Failed to install jq. Please ensure jq is available in the CodeBuild image or install it manually." >&2 + exit 1 + fi +fi +echo "jq is available." + +# --- Create necessary directories --- +mkdir -p "$TOOL_CACHE_DIR" +mkdir -p "$INSTALL_DIR" +export PATH="$INSTALL_DIR:$PATH" # Add install dir to PATH for this script's session + +echo "Tool Cache Directory: $TOOL_CACHE_DIR" +echo "Installation Directory: $INSTALL_DIR" +echo "Updated PATH: $PATH" +echo "Required tools to process: $REQUIRED_TOOLS" +echo "TOOL_DEFINITIONS (first 200 chars): $(echo "$TOOL_DEFINITIONS" | cut -c 1-200)..." + +# --- Tool Installation Loop --- +for tool_name_var in $REQUIRED_TOOLS; do + # Use a subshell for per-tool variables to avoid conflicts and ensure clean state + ( + tool_name="$tool_name_var" + echo "--------------------------------------------------" + echo "Processing tool: $tool_name" + + tool_info=$(echo "$TOOL_DEFINITIONS" | jq -r --arg tn "$tool_name" '.[$tn]') + + if [ -z "$tool_info" ] || [ "$tool_info" == "null" ] || [ "$tool_info" == "{}" ] ; then + echo "ERROR: Tool '$tool_name' not found or has null/empty definition in TOOL_DEFINITIONS." >&2 + exit 1 # Exit subshell, which will cause the main script to exit due to \`set -e\` if subshell fails + fi + + # Extract tool details + # version=$(echo "$tool_info" | jq -r '.version // empty') # Version not strictly needed by script but good for logging + s3_key=$(echo "$tool_info" | jq -r '.s3_key // empty') + expected_checksum=$(echo "$tool_info" | jq -r '.checksum // empty') # SHA256 + archive_format=$(echo "$tool_info" | jq -r '.archive_format // empty') + # executable_path_in_archive is the path *inside* the archive to the executable file itself. + # If archive_format is 'binary', this is ignored. + # If archive_format is 'zip' or 'tar.gz' and this is empty/null, script defaults to tool_name. + executable_path_in_archive=$(echo "$tool_info" | jq -r '.executable_path_in_archive // empty') + + # Validate extracted details + if [ -z "$s3_key" ] || [ -z "$expected_checksum" ] || [ -z "$archive_format" ]; then + echo "ERROR: Missing one or more critical fields (s3_key, checksum, archive_format) for tool '$tool_name'." >&2 + echo "Tool Info Found: $tool_info" >&2 + exit 1 + fi + + # Determine the actual executable name within the archive if not specified + effective_executable_path_in_archive="$executable_path_in_archive" + if [[ "$archive_format" == "zip" || "$archive_format" == "tar.gz" ]] && \ + [[ -z "$executable_path_in_archive" || "$executable_path_in_archive" == "null" ]]; then + effective_executable_path_in_archive="$tool_name" + fi + + archive_filename=$(basename "$s3_key") + cached_archive_path="$TOOL_CACHE_DIR/$archive_filename" + s3_source_path="s3://${ARTIFACTS_BUCKET}/${s3_key}" + target_executable_path="$INSTALL_DIR/$tool_name" # Final destination of the executable + + echo "Details for $tool_name:" + # echo " Version: $version" + echo " S3 Key: $s3_key" + echo " Expected SHA256: $expected_checksum" + echo " Archive Format: $archive_format" + echo " Executable path in archive (effective): $effective_executable_path_in_archive" + echo " Archive filename: $archive_filename" + echo " Cached archive path: $cached_archive_path" + echo " Target executable path: $target_executable_path" + + # --- Cache Check & Download --- + needs_download=true + if [ -f "$cached_archive_path" ]; then + echo "Cached archive $cached_archive_path found. Verifying checksum..." + actual_checksum=$(sha256sum "$cached_archive_path" | awk '{print $1}') + if [ "$actual_checksum" == "$expected_checksum" ]; then + echo "Checksum for cached $archive_filename is VALID." + needs_download=false + else + echo "Checksum MISMATCH for cached $archive_filename. Expected: $expected_checksum, Got: $actual_checksum. Re-downloading." + rm -f "$cached_archive_path" + fi + else + echo "Archive $archive_filename not found in cache: $cached_archive_path. Downloading." + fi + + if [ "$needs_download" == true ]; then + echo "Downloading $tool_name from $s3_source_path to $cached_archive_path..." + if ! aws s3 cp "$s3_source_path" "$cached_archive_path"; then + echo "ERROR: Failed to download $tool_name from S3." >&2 + exit 1 + fi + echo "Download complete. Verifying checksum of downloaded file..." + actual_checksum=$(sha256sum "$cached_archive_path" | awk '{print $1}') + if [ "$actual_checksum" != "$expected_checksum" ]; then + echo "ERROR: Checksum MISMATCH for downloaded $archive_filename. Expected: $expected_checksum, Got: $actual_checksum." >&2 + rm -f "$cached_archive_path" + exit 1 + fi + echo "Checksum for downloaded $archive_filename is VALID." + fi + + # --- Extraction & Installation --- + echo "Installing $tool_name from $cached_archive_path to $target_executable_path..." + # Ensure target is clean for binary moves/copies + rm -f "$target_executable_path" + # Create a temporary directory for extraction to keep $TOOL_CACHE_DIR clean from extracted files + temp_extract_dir=$(mktemp -d -p "$TOOL_CACHE_DIR" "tmp_extract_${tool_name}_XXXXXX") + + extracted_executable_source_path="" # Path to the executable *after* extraction + + if [ "$archive_format" == "zip" ]; then + unzip -o "$cached_archive_path" -d "$temp_extract_dir" > /dev/null + extracted_executable_source_path="$temp_extract_dir/$effective_executable_path_in_archive" + elif [ "$archive_format" == "tar.gz" ]; then + # tar -xzf "$cached_archive_path" -C "$temp_extract_dir" "$effective_executable_path_in_archive" # This only extracts the specific file + tar -xzf "$cached_archive_path" -C "$temp_extract_dir" > /dev/null # Extract all + extracted_executable_source_path="$temp_extract_dir/$effective_executable_path_in_archive" + elif [ "$archive_format" == "binary" ]; then + # For binary, the "archive" is the executable itself. Copy it to the temp dir first for consistency. + cp "$cached_archive_path" "$temp_extract_dir/$tool_name" + extracted_executable_source_path="$temp_extract_dir/$tool_name" + else + echo "ERROR: Unknown archive format '$archive_format' for $tool_name." >&2 + rm -rf "$temp_extract_dir" + exit 1 + fi + + if [ ! -f "$extracted_executable_source_path" ]; then + echo "ERROR: Executable for $tool_name not found at '$extracted_executable_source_path' after extraction." >&2 + echo "Contents of $temp_extract_dir:" >&2 + ls -lR "$temp_extract_dir" >&2 + rm -rf "$temp_extract_dir" + exit 1 + fi + + echo "Moving '$extracted_executable_source_path' to '$target_executable_path'" + mv "$extracted_executable_source_path" "$target_executable_path" + chmod +x "$target_executable_path" + + # Clean up temporary extraction directory + rm -rf "$temp_extract_dir" + + echo "$tool_name installed successfully to $target_executable_path." + + # --- Verification (Optional but Recommended) --- + echo "Verifying $tool_name installation..." + if command -v $tool_name &> /dev/null; then + echo "Attempting to get version for $tool_name..." + # Try common version flags, redirect stderr to stdout for capture, take first line + if $tool_name --version &> /dev/null; then + echo "$($tool_name --version 2>&1 | head -n 1)" + elif $tool_name version &> /dev/null; then + echo "$($tool_name version 2>&1 | head -n 1)" + elif $tool_name -version &> /dev/null; then # e.g. Java + echo "$($tool_name -version 2>&1 | head -n 1)" + elif $tool_name -v &> /dev/null; then # e.g. Go + echo "$($tool_name -v 2>&1 | head -n 1)" + else + echo "$tool_name is callable, but version command is unknown or failed. Assuming successful installation." + fi + else + echo "ERROR: $tool_name command not found in PATH after installation attempt to $target_executable_path." >&2 + exit 1 + fi + ) || exit 1 # If subshell fails, exit the main script +done + +echo "--- Tool Management Script (manage_tools.sh) Finished Successfully ---" diff --git a/buildspecs/scripts/pip-cert.pem b/buildspecs/scripts/pip-cert.pem new file mode 100644 index 0000000..319b764 --- /dev/null +++ b/buildspecs/scripts/pip-cert.pem @@ -0,0 +1,323 @@ +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIQGVCQdFyalIVHZ1OchWiMYDANBgkqhkiG9w0BAQwFADBs +MRMwEQYKCZImiZPyLGQBGRYDR292MRYwFAYKCZImiZPyLGQBGRYGQ2Vuc3VzMQww +CgYDVQQLEwNUQ08xDDAKBgNVBAsTA1BLSTEhMB8GA1UEAxMYVVMgQ2Vuc3VzIEJ1 +cmVhdSBSb290IENBMB4XDTE5MDcyNTE4MTAyOVoXDTI5MDcyNTE4MjAyN1owbDET +MBEGCgmSJomT8ixkARkWA0dvdjEWMBQGCgmSJomT8ixkARkWBkNlbnN1czEMMAoG +A1UECxMDVENPMQwwCgYDVQQLEwNQS0kxITAfBgNVBAMTGFVTIENlbnN1cyBCdXJl +YXUgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMWX8I9p +slFaUueuPpEFExgqKcGgoyTOBxFUCXNBnucL3cKRx9MC47kWOwQ94WYvI3LMcehC +6pOwIf5AuhrIdVrJaHSz317ENuDaiur9/qN3fBRidijHphynR/rwJSxiI3VQtj8G +SO4JmCA8dMsKayIl1RiKlQHPoNnSWyDEspAfenr0qq7PzbjKOEPXoO4eXO0plfB3 +aYd+qMRwHKQre4gRGpMfWu1w5JZqFItbXE/RSC38SoZWjkcMcjyTCDTSGY+j/aJw +SHx98riQ8SLQszL5Be0AmF0KHwMZNOsoaa5u/bF++g207W9guLVgO2Ak5D4Unyo3 +D7kcFSuBOVYdeT0XRi3iD0AwEkoCsVzeEOIqjAasj6hYD43O8GjfHpwGpAeASqTT +nbDajtuTsJrrBlLwpz49J5dihJ3Ah7jTirzQciEUZTXv3L7XpdBlt3/sv73Gn0F6 +jZPDANmHIfNHz0xWa9iES9sLPKln9cjnkJs/QlpooTJSrVuovGyzsbu1mb7PfBji +IMF8lVptjQYaWvvMXqXNx2+L6+uBVkEfmuZIs7Xen4ZNz4NP5MixTs3Tq2h81Hym +TbIlJUtSdwZ98jsX6YLerBYYMPawtSIH4Yfdq/Wpt7IHED47dTWdFfC0peqYfHIN +PoRG+eFYq5nHxadkGaifElPnNdvGblRLDj27AgMBAAGjUTBPMAsGA1UdDwQEAwIB +hjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTHXiB3QZv2GiBSkErqGoOT8cOr +HjAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQwFAAOCAgEAdXsv6igAKGnq +VS79nePbjGj2Z+SFdM2jRVibv06mWR3uVqFNCz2zqlIXzX7PJmK7HycWDK82UWMh +8J0cn1O+PYWFalzhPWk7t1c6EK8wV63/iKj+voqNwZWL7L1/EQiQ8B4OPIyf7v5Y +j3/jqrvufLgGCyz+0JhBY8CBEGZ1knijrHxTv0DOV0ykKI0OpUIes+8SOTdszTDb +XujzE4ekSRTDqWJOCbsQb3KbBUr/k8APVq/Ir/xmS1WmauyP3zBIxMlPMmu9XTw/ +5nRUKKQe8FrVHELLO32iS+6bqdTNmkD7z/VyzWmBA0FVt8upD6Bs8U/bHjoiL/Jk +W3BQ6owq7u+B5w/Cl+WsgQcgVlDLlBZWMKnEng1n2MhqUnzf0dDGA99vrzLPVcPT +yoexQe1E1Y2EoORgaGbsnjkRTwppUnpnxkWrzObBieYB1ir0rRTbKS5hgwXu55Uc +6ypmCLUnQaDVWIZyKKwtmr4n/rX5KJPxj/zT0F+jH1WDyMDVg6jYyu1HIPcABkAU +OlsSr7Tfct75/JGf18oPSFMkV1kzeLUK21vflcMp+ZK0m2TRZyCLvMB/lEsRjsSM +wrgYk7cR14RqJ+RTA7IJqFQfNAXqV1ra+stZYYoLI83oK4shOhHLiO9lR6hSi43f +0w7ALm+8qd1Ih+E5BjmKBJAEFB5Zyzs= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF1jCCA76gAwIBAgITLgAAAAmcP+bslIv04AAAAAAACTANBgkqhkiG9w0BAQwF +ADBsMRMwEQYKCZImiZPyLGQBGRYDR292MRYwFAYKCZImiZPyLGQBGRYGQ2Vuc3Vz +MQwwCgYDVQQLEwNUQ08xDDAKBgNVBAsTA1BLSTEhMB8GA1UEAxMYVVMgQ2Vuc3Vz +IEJ1cmVhdSBSb290IENBMB4XDTE5MDgwNjE1MDc0NVoXDTI0MDgwNjE1MTc0NVow +YTETMBEGCgmSJomT8ixkARkWA2dvdjEWMBQGCgmSJomT8ixkARkWBmNlbnN1czES +MBAGCgmSJomT8ixkARkWAmFkMR4wHAYDVQQDExVVUyBDZW5zdXMgQnVyZWF1IENB +IDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCiUqJa4e90dNdAFC0W +ju9arRst3FchtNxT0ZPdg/2UpDFN35PFBQ4G1RJxGVGuhpkRmqLdtI9t9BQHZ/tk +QZ6ELJRJVxQMPONBuoXlUbnS3CHwDT5+YIvVZr3jHjv96tq6C2SYJ1BNeqDYjhdK +gF3WXUJpb6lbAwZtv7aHZUSVXcnW/hCkfI2aRZoGXCcgi6hbcJRC74HCGW0eLtCZ +M0Y5+lEGdKLAOiIsl4kea+34Uh5eHjIp9LHCicIfx+5RT5xor4hOJldu2pOmjzrg +FBCz59/5wZHIyQCHOu92p/VGO9eeCxCDlT8DWa78c2HjCnf0FvymlxoHPdH89Rhv +idPFAgMBAAGjggF6MIIBdjAQBgkrBgEEAYI3FQEEAwIBATAjBgkrBgEEAYI3FQIE +FgQUNDptGIuzWncMER7QFKnL+JZPMwswHQYDVR0OBBYEFMSLwaPcjo2CqYcxhzj8 +U1q1Px/KMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAS +BgNVHRMBAf8ECDAGAQH/AgEBMB8GA1UdIwQYMBaAFMdeIHdBm/YaIFKQSuoag5Px +w6seMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9wa2kudGNvLmNlbnN1cy5nb3Yv +Q2VydEVucm9sbC9VUyUyMENlbnN1cyUyMEJ1cmVhdSUyMFJvb3QlMjBDQS5jcmww +ZQYIKwYBBQUHAQEEWTBXMFUGCCsGAQUFBzAChklodHRwOi8vcGtpLnRjby5jZW5z +dXMuZ292L0NlcnRFbnJvbGwvVVMlMjBDZW5zdXMlMjBCdXJlYXUlMjBSb290JTIw +Q0EuY3J0MA0GCSqGSIb3DQEBDAUAA4ICAQAvLJiXBncvqEq2WjU4CtvB+g9GKgna +MIeu8D41/BdkhTpLR/Cus6Oq+N18cCyyBHNCPS4pz/cDzyzQvNMIDTP7tpcTwEfc +QW/WgPvfJtEmzOaRtNeSBBci1bySX4OMKnzB9ZQbGphaqYaVAG6n+NLCkg1MSvqK +cexAf8wkAJyjx2YOUh+xqwhXRE6UKlc9TVK0b2anVtg4FLNiUznZ6KerEKXx/wxv +XvOZRAY902P2FIRY9qbkEdAshNSA5HlY27pbdH4eZCTyk5uSTlIZQRtngL6w1Gy8 +Xh70AIv+kj38iKp8N4VgksHWS0Viw3Cg4h+3/hY08E/uLCzUKjdZt9I46bM1YKMv +K2LUA8xrWp0IN+wcdp2UUrAlVSHEp6LW+NR+VHtl0QiMYjXA+AvkoRvcoEotgeZP +mqfK9auR+3WiDUrkVLzPoPMQHWE9QXt+eErzBh+YXqqvPgPBGqA25CGwzyrs8iBT +jlhbJArFNO6KzQUwyf/Vw3dwX5oOebGuoh+KX9yRaN+q1ZqqWL1Jn40NXF8KQyLk +Ro4c9m+fpkTWhuxW6zW8YIbnmtNDk2X3YfAY1dIKAUIW24Si0SMka8pC2d9qaL2m +fyD0JoF+49cPDtTNHsUP5QR3a+JjqAT8haladoSyiNmO24ysueI7sg9A+zY8oJrM +Gi2tB39Jg7J6/w== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF1jCCA76gAwIBAgITLgAAAApfi2u0+zjcuQAAAAAACjANBgkqhkiG9w0BAQwF +ADBsMRMwEQYKCZImiZPyLGQBGRYDR292MRYwFAYKCZImiZPyLGQBGRYGQ2Vuc3Vz +MQwwCgYDVQQLEwNUQ08xDDAKBgNVBAsTA1BLSTEhMB8GA1UEAxMYVVMgQ2Vuc3Vz +IEJ1cmVhdSBSb290IENBMB4XDTE5MDgwNjE1MDc0M1oXDTI0MDgwNjE1MTc0M1ow +YTETMBEGCgmSJomT8ixkARkWA2dvdjEWMBQGCgmSJomT8ixkARkWBmNlbnN1czES +MBAGCgmSJomT8ixkARkWAmFkMR4wHAYDVQQDExVVUyBDZW5zdXMgQnVyZWF1IENB +IDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFLt4b/8hnKu0yk7IC +C0qY8gAF20DZrbE6rILe2quYeSQcztIw3H6K2+uAsvpCRjRc4+ra+bKQWLpTv5gP +6l6iDMlun3po1+Qqlga4S4/kJMoYP52AbcdHog33vdvpmtRhL2WLBdHfXLfahVx3 +OB1WkrZMFP4T3L4mTo8SW4abdIf5Q7SmClrHzy+znv4jhKEU9tiY7NXJBCINETx3 +5B8PE8F0r1s0Mv+yhoDHWk2Poa/rC+CrXZ+NdzWfI2ajUc1Nb2b+6f4Wrpc9qC+a +kxYywDcrUoGnwqJYDoIFZY2ErqTQUw7JGQkG/i+7gYs+VaHPcD3DNQq3iFzab26I +0vG5AgMBAAGjggF6MIIBdjAQBgkrBgEEAYI3FQEEAwIBATAjBgkrBgEEAYI3FQIE +FgQU6ZLQoy5LJaVqTI5Em9TBptKdLmAwHQYDVR0OBBYEFOpnUT2Oc868n6qxmUrj +FdfUn3tOMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAS +BgNVHRMBAf8ECDAGAQH/AgEBMB8GA1UdIwQYMBaAFMdeIHdBm/YaIFKQSuoag5Px +w6seMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9wa2kudGNvLmNlbnN1cy5nb3Yv +Q2VydEVucm9sbC9VUyUyMENlbnN1cyUyMEJ1cmVhdSUyMFJvb3QlMjBDQS5jcmww +ZQYIKwYBBQUHAQEEWTBXMFUGCCsGAQUFBzAChklodHRwOi8vcGtpLnRjby5jZW5z +dXMuZ292L0NlcnRFbnJvbGwvVVMlMjBDZW5zdXMlMjBCdXJlYXUlMjBSb290JTIw +Q0EuY3J0MA0GCSqGSIb3DQEBDAUAA4ICAQCYQm6VusLYzHy9PM0P4dSkHSUVGug+ +8Q/Gn1qQ+pejTpx0fR+pxq8DP8Ua3qgWsIz3scrONairxWVUW5AA4E0VXU0fO6n+ +4DLdJnwwIEIkV410p5w79l9Dl2NiI31Ijv0Y8PwEzXmcSvcz1Qc05TyRV+1yv6Uh +nHfnu4kHXj26NOOsPjrEJ60l0tcOT4p3edkwYRf3XzQ19k4ITEBeYF76y1FX8H+W +RTIjQNr8BXUVt+afJZXgUgSB0xHfSRBhTUXiFvKbs1BpICNQmhbFIaz7GJZkvx9r +b+7Um2EQNIQKxoe4rG4mar62Ux3k0i9o8O9nccQSl9VCuSvTyCmtpKpsKRRitMf2 +vBQ9D14p5pzDdFZQC75B8lkibXpuk8fQ3/CIMqK4547wIO8tgz4wqN8ID4tEBgqZ +Fot9XSJpDAZHYKx5GWVwKmhqwefACqqASjHR8NVakAd3EkcQ06SEzGYTTq2duWhi +fOxpJKtMtw9JTfbOG9Az28rRWGCk1vVHmtkVHApD3XdAV3RG6w/AqjNu/IY70fmd +wULhegJxbVdQucgwR4WyNbx7hCJYvoEyL5L7ZQwBpFXHnOI7wJFGw2eo5xIUehUS +4jPpb2OolWHEOjMkEkRfgfrJsnt/blpKXRmYRFUd1+c5VBOtsaYv3iYArxZziQxf +pR508zEDCd9cRQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFsjCCA5qgAwIBAgITLgAAAAvaREPe3QGJiAAAAAAACzANBgkqhkiG9w0BAQwF +ADBsMRMwEQYKCZImiZPyLGQBGRYDR292MRYwFAYKCZImiZPyLGQBGRYGQ2Vuc3Vz +MQwwCgYDVQQLEwNUQ08xDDAKBgNVBAsTA1BLSTEhMB8GA1UEAxMYVVMgQ2Vuc3Vz +IEJ1cmVhdSBSb290IENBMB4XDTE5MDgwNjE1MDc0MVoXDTI0MDgwNjE1MTc0MVow +YjETMBEGCgmSJomT8ixkARkWA2dvdjEWMBQGCgmSJomT8ixkARkWBmNlbnN1czET +MBEGCgmSJomT8ixkARkWA2VhZDEeMBwGA1UEAxMVVVMgQ2Vuc3VzIEJ1cmVhdSBD +QSAzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxA+7bWM9ZExFO/ZN +uFodd+ktg0TWojeV8QJTYAdtwzMquqDl/zMLgkHPD8xC730qMdKB6Df74i3moN5c +6h9S087T0tdf02U0J95AfO06oZiaGNzq/zacINhfbxWf2ZAyZCiwpcQL3w3uAjS1 +MK++iC8ZWDBnd5z64ewCDFS8d9FD5RrJ0GxGCcC4IJ8DyhOq7i3a/Td29wLTP1wz +QuFLVD/5JFWirqnJwgqVVEUdzf8ZK3MSk9DAZcIjY/mIZgnnZ+ukcD0TtYkOnPU7 +j7EGeqo6Jby3T75p4x3uRlNaEKAqXBqiu7bVx+T0cTtuJEjtw4l/8WEGEFGI6Jfs +0Du9+QIDAQABo4IBVTCCAVEwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFCB4 +OetP7QLwgNqbXIDospFC1inEMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsG +A1UdDwQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEBMB8GA1UdIwQYMBaAFMdeIHdB +m/YaIFKQSuoag5Pxw6seMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9wa2kudGNv +LmNlbnN1cy5nb3YvQ2VydEVucm9sbC9VUyUyMENlbnN1cyUyMEJ1cmVhdSUyMFJv +b3QlMjBDQS5jcmwwZQYIKwYBBQUHAQEEWTBXMFUGCCsGAQUFBzAChklodHRwOi8v +cGtpLnRjby5jZW5zdXMuZ292L0NlcnRFbnJvbGwvVVMlMjBDZW5zdXMlMjBCdXJl +YXUlMjBSb290JTIwQ0EuY3J0MA0GCSqGSIb3DQEBDAUAA4ICAQCGmm3uxuTvZcWm +ihlWtSa/0H88MM3ubcOAqYmNHWCzynemR9CxUZfuR/qi8HvRKHm5HwDVT1LtL3Wf +K+9Lc7mcBHStZUdNgINVsqZzNi1L54v/UD3lAu79M/yh16DREvEnWLlc1CUhti+Q +P6aooRfF1VIAzoNZz3iUBj43uRJLewYhlFYRy8GFzRhoKJ/HNZI9nqlV7notKtvV +P2Ae++stlTGzrUEYi91tgJdoSOKweDg4EDjEr4y51yY2l8eJJTXtRRIMDdtv1wbF +XVpxcbWDvAFmYKFjpspaEiD3gAEdSDGcCv23KGFxZCMw5Chblg2drWCSCbJQ2VE/ +XiHcHGxrTQVru+ocZgEqH600BDAC+/nrVP1lJyfKsY2KUh9X/vzbAbx7r45l7LJh +Q173miuG1Hjm60OEtUsNobtVOG/TCxqHflRuMgVK5mGb00Hu5SxMel/ma5bhvWCS +ZQIYEIwo2b6GBicTuhHhBo0e4BdA3vvz8WroUTiezmMo8BveyYViqyWFCB26Wvhy +NB4pfg+GFfTl0wiHSpc1RfBFuoohkGgUMt0ci0jJp1ofb6MeK+p3DqBfKyhQiz+7 +EsgudLUeALpj38b5mWjvN17YBby5suRJnH8lv7+Z1nooo+MqapZZyrRu56PtEBJM +3m7NDAL9JACMk8yF5WDToKtcPuTgpg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF0zCCA7ugAwIBAgITLgAAAA+ydH8TcbjZAgAAAAAADzANBgkqhkiG9w0BAQwF +ADBsMRMwEQYKCZImiZPyLGQBGRYDR292MRYwFAYKCZImiZPyLGQBGRYGQ2Vuc3Vz +MQwwCgYDVQQLEwNUQ08xDDAKBgNVBAsTA1BLSTEhMB8GA1UEAxMYVVMgQ2Vuc3Vz +IEJ1cmVhdSBSb290IENBMB4XDTIyMDkyMjE0NDQwOFoXDTI3MDkyMjE0NTQwOFow +YTETMBEGCgmSJomT8ixkARkWA2dvdjEWMBQGCgmSJomT8ixkARkWBmNlbnN1czES +MBAGCgmSJomT8ixkARkWAmFkMR4wHAYDVQQDExVVUyBDZW5zdXMgQnVyZWF1IENB +IDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCiUqJa4e90dNdAFC0W +ju9arRst3FchtNxT0ZPdg/2UpDFN35PFBQ4G1RJxGVGuhpkRmqLdtI9t9BQHZ/tk +QZ6ELJRJVxQMPONBuoXlUbnS3CHwDT5+YIvVZr3jHjv96tq6C2SYJ1BNeqDYjhdK +gF3WXUJpb6lbAwZtv7aHZUSVXcnW/hCkfI2aRZoGXCcgi6hbcJRC74HCGW0eLtCZ +M0Y5+lEGdKLAOiIsl4kea+34Uh5eHjIp9LHCicIfx+5RT5xor4hOJldu2pOmjzrg +FBCz59/5wZHIyQCHOu92p/VGO9eeCxCDlT8DWa78c2HjCnf0FvymlxoHPdH89Rhv +idPFAgMBAAGjggF3MIIBczAQBgkrBgEEAYI3FQEEAwIBAjAjBgkrBgEEAYI3FQIE +FgQUFE9/OhOsohsjHyLcCd1NqTNkdQYwHQYDVR0OBBYEFMSLwaPcjo2CqYcxhzj8 +U1q1Px/KMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAP +BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFMdeIHdBm/YaIFKQSuoag5Pxw6se +MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9wa2kudGNvLmNlbnN1cy5nb3YvQ2Vy +dEVucm9sbC9VUyUyMENlbnN1cyUyMEJ1cmVhdSUyMFJvb3QlMjBDQS5jcmwwZQYI +KwYBBQUHAQEEWTBXMFUGCCsGAQUFBzAChklodHRwOi8vcGtpLnRjby5jZW5zdXMu +Z292L0NlcnRFbnJvbGwvVVMlMjBDZW5zdXMlMjBCdXJlYXUlMjBSb290JTIwQ0Eu +Y3J0MA0GCSqGSIb3DQEBDAUAA4ICAQCdYsU2TVWTAzVjqPqlO+PtxTcoDxBjlvo+ +L519/iTxzlcz0Kiao83fGhsSitzNf0LsSTOWrAuCprX0sn5If4pasZKqVp+ZJnjF +H9Wpi/4gsaCtvY3V4Hm5ZS1BffUHrre/kR//pn9f2Axu3tTVfHNAEVr0kRvq9wPD +yMe5BzLtm9amOwFvAYP/69zXk4ig88mbOmXjK+EC5AUzwBhg9oI/Kv2AeLbKx+nr +DuguMe6RCp4NXBS1X3/cjRN37+ayJEHynFdWKiVNcvxABVFLGVHBA4fMD9kTjT2a +cf413mhywUcVTfpoj/94Kcqvl3oxgHWGIig9RWExMkvmrkYT5hGqfws+NIGrCGaZ +GA0cUYAY5cbkAg8If3Htt4aSCdTu6g/RbatMFND2GURO2fHPajBILBiDxCJM6OmT +SUQPghQC3QvE48CM5J6KAjPosGh8Ay454FhKv0ShvhKTaHzN6anBih8AbwU5G8iP +XeoNY+jZbkv1gBJ4J+8nffm1n5aFbssbxazppqTLpFDXimduWUxSXZbjwGGwHc7G +FmLj14c8og+ItE+meToVXt6oFSF9hkri5Lmanen9SqU9IPgxiTv91olwmXW6d/3Y +D202odbWVpAIIjiVJngfyOulCeEQsz5WjmPyIjFkXNz8NiwAJSJu1XtBtAMdaCDe +6z6OUG7UaQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF0zCCA7ugAwIBAgITLgAAABDGRuhzKgVoqQAAAAAAEDANBgkqhkiG9w0BAQwF +ADBsMRMwEQYKCZImiZPyLGQBGRYDR292MRYwFAYKCZImiZPyLGQBGRYGQ2Vuc3Vz +MQwwCgYDVQQLEwNUQ08xDDAKBgNVBAsTA1BLSTEhMB8GA1UEAxMYVVMgQ2Vuc3Vz +IEJ1cmVhdSBSb290IENBMB4XDTIyMDkyMjE0NDUxN1oXDTI3MDkyMjE0NTUxN1ow +YTETMBEGCgmSJomT8ixkARkWA2dvdjEWMBQGCgmSJomT8ixkARkWBmNlbnN1czES +MBAGCgmSJomT8ixkARkWAmFkMR4wHAYDVQQDExVVUyBDZW5zdXMgQnVyZWF1IENB +IDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFLt4b/8hnKu0yk7IC +C0qY8gAF20DZrbE6rILe2quYeSQcztIw3H6K2+uAsvpCRjRc4+ra+bKQWLpTv5gP +6l6iDMlun3po1+Qqlga4S4/kJMoYP52AbcdHog33vdvpmtRhL2WLBdHfXLfahVx3 +OB1WkrZMFP4T3L4mTo8SW4abdIf5Q7SmClrHzy+znv4jhKEU9tiY7NXJBCINETx3 +5B8PE8F0r1s0Mv+yhoDHWk2Poa/rC+CrXZ+NdzWfI2ajUc1Nb2b+6f4Wrpc9qC+a +kxYywDcrUoGnwqJYDoIFZY2ErqTQUw7JGQkG/i+7gYs+VaHPcD3DNQq3iFzab26I +0vG5AgMBAAGjggF3MIIBczAQBgkrBgEEAYI3FQEEAwIBAjAjBgkrBgEEAYI3FQIE +FgQUxgMHEbdrxtDC64yaqubXVeW060owHQYDVR0OBBYEFOpnUT2Oc868n6qxmUrj +FdfUn3tOMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAP +BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFMdeIHdBm/YaIFKQSuoag5Pxw6se +MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9wa2kudGNvLmNlbnN1cy5nb3YvQ2Vy +dEVucm9sbC9VUyUyMENlbnN1cyUyMEJ1cmVhdSUyMFJvb3QlMjBDQS5jcmwwZQYI +KwYBBQUHAQEEWTBXMFUGCCsGAQUFBzAChklodHRwOi8vcGtpLnRjby5jZW5zdXMu +Z292L0NlcnRFbnJvbGwvVVMlMjBDZW5zdXMlMjBCdXJlYXUlMjBSb290JTIwQ0Eu +Y3J0MA0GCSqGSIb3DQEBDAUAA4ICAQB/Kn2/ohaTr4XDgu5msLiKzjA3Rqb4Wf4r +FmzpJXcaB9N4Tyg19qgZ9l57AVDO6DWlXBENY+FXERe/qrvhFawZqActT7dPqJJv +Z30hwBcXc8ELjNxVp54MDJfd2oHUkXwJ46i1GphHfie0Q/csoraRpf/DjXuaruxM +Vgt4Roo6zBGf2nSCfqVLR2NZ93orfSybg5g2eutYuftkd5tzbcxdhHlTlhhbNpIV +quVaT46hN1h/q1bMmS4bGBdLUQggY5BtY9RM4gDhcyh1K8k5auM+uPyWqnnd10wI +vuRSu2zNueWlqVstSTbnZdf138nssj+MzN8xcmn+mXH7z8COXwhJLBKRr7Xg7l7G +UMmc86eYbmpphs3LhzZNMooAGUedm15Ln1u9wgywtP6CbpvBVIcSxmjJeiN6bXy6 +dtbZCCziijO1UehOqc81jZy/jdG158D0WfOumNkx1biGwZ/YR+oGslaSkMr58e/7 +abPBMlQmDwvlTWeiUqMZJAzNHk13c8jSeMtaGXtE9D9Sv2oPVGwjeB2krn1Lb8uU +YeEl0YmQ2W1GpoYC4zU7gnnNjSbLr13L8Gjsmk9FYy4HWDRgJvAvF2O3DldldxP2 +MurPmXriFtEUNo4e1UKJciPJlYChWz1/0Hwncab8AWaw3MPkyYpELKis+vTELriO +iHAYOPwOJg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF1DCCA7ygAwIBAgITLgAAAA4zbBR3VlxWyAAAAAAADjANBgkqhkiG9w0BAQwF +ADBsMRMwEQYKCZImiZPyLGQBGRYDR292MRYwFAYKCZImiZPyLGQBGRYGQ2Vuc3Vz +MQwwCgYDVQQLEwNUQ08xDDAKBgNVBAsTA1BLSTEhMB8GA1UEAxMYVVMgQ2Vuc3Vz +IEJ1cmVhdSBSb290IENBMB4XDTIyMDIyODE3NTUxOFoXDTI3MDIyODE4MDUxOFow +YjETMBEGCgmSJomT8ixkARkWA2dvdjEWMBQGCgmSJomT8ixkARkWBmNlbnN1czET +MBEGCgmSJomT8ixkARkWA2VhZDEeMBwGA1UEAxMVVVMgQ2Vuc3VzIEJ1cmVhdSBD +QSAzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxA+7bWM9ZExFO/ZN +uFodd+ktg0TWojeV8QJTYAdtwzMquqDl/zMLgkHPD8xC730qMdKB6Df74i3moN5c +6h9S087T0tdf02U0J95AfO06oZiaGNzq/zacINhfbxWf2ZAyZCiwpcQL3w3uAjS1 +MK++iC8ZWDBnd5z64ewCDFS8d9FD5RrJ0GxGCcC4IJ8DyhOq7i3a/Td29wLTP1wz +QuFLVD/5JFWirqnJwgqVVEUdzf8ZK3MSk9DAZcIjY/mIZgnnZ+ukcD0TtYkOnPU7 +j7EGeqo6Jby3T75p4x3uRlNaEKAqXBqiu7bVx+T0cTtuJEjtw4l/8WEGEFGI6Jfs +0Du9+QIDAQABo4IBdzCCAXMwEAYJKwYBBAGCNxUBBAMCAQEwIwYJKwYBBAGCNxUC +BBYEFE2wPwIWNvlAbZy05X4kklJu09q8MB0GA1UdDgQWBBQgeDnrT+0C8IDam1yA +6LKRQtYpxDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTHXiB3QZv2GiBSkErqGoOT8cOr +HjBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vcGtpLnRjby5jZW5zdXMuZ292L0Nl +cnRFbnJvbGwvVVMlMjBDZW5zdXMlMjBCdXJlYXUlMjBSb290JTIwQ0EuY3JsMGUG +CCsGAQUFBwEBBFkwVzBVBggrBgEFBQcwAoZJaHR0cDovL3BraS50Y28uY2Vuc3Vz +Lmdvdi9DZXJ0RW5yb2xsL1VTJTIwQ2Vuc3VzJTIwQnVyZWF1JTIwUm9vdCUyMENB +LmNydDANBgkqhkiG9w0BAQwFAAOCAgEAjDWz6k+6ModUkHRJgTjv8nHfPJv1qI9d +WUejF3YSwU6ExE44C5C2oEXPtEAWR+LiEsW+U4ZZ8Zgi/F5qI3AblQbNXDplAbo/ +6UoKeieBftV5cf7WgbdFoVFuX2HppSVrDQPf4t6DpCM6qVs8/EIrBQOeKhVckhB1 +XgiuFTb3sRoOmWvRramBf3xp7WJ1P4T76gBUg2I6GMFV3EO/mv8XWM9QzFZ1nFOQ +z8/zRa1x53WuAc36d8ESGqL0ZxjNjSNU/HtpJnwtYj3hzJIsYgm938nU5p1diF00 +C89+a0CKkVnL7JW6tC8MQqnyE7TBBWjSmssxa4FHT753W/NaU6JVIJqOwuGTTenv +bQlHi+NxfqL0alNXX3ukUNDPB5XfGWCEBMGZ9xUNDXdxTS7lJzZGAddjqu94e5gd +KgDiEq52RQgkbZ8d+DYwpo/4XY7rj/bC4jvVXUhVd8E/NAbzTSo3VppK0pi/wDri +lm4p8WlzrCoGTVPeiZdCApa/bOoaq+X7/vN4HDUakJZFEPfxIwznfJbDEu7hrVE3 +fck3YuSBrQx6yYtmpLEnybaB5so0w+djeswxBVQSlBODYhrMFW+l3VIRa9PqHQWw +8TvAglbHxFUWWtlHBbwXgVdOqAVlh1LHU8mfbtkY8D4h+iXk+4nvBY1aKdDaZFTB +kDgqyXZwIww= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFSDCCBDCgAwIBAgIJAMn9gqHMdnl3MA0GCSqGSIb3DQEBBQUAMIGfMQswCQYD +VQQGEwJVUzERMA8GA1UECBMITWFyeWxhbmQxGzAZBgNVBAoTElUuUy4gQ2Vuc3Vz +IEJ1cmVhdTEiMCAGA1UECxMZVGVsZWNvbW11bmljYXRpb25zIE9mZmljZTEaMBgG +A1UEAxMRY2EudGNvLmNlbnN1cy5nb3YxIDAeBgkqhkiG9w0BCQEWEWNhQHRjby5j +ZW5zdXMuZ292MB4XDTEyMDgxNTE2MTM0OFoXDTMyMDgxMDE2MTM0OFowgZ8xCzAJ +BgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEbMBkGA1UEChMSVS5TLiBDZW5z +dXMgQnVyZWF1MSIwIAYDVQQLExlUZWxlY29tbXVuaWNhdGlvbnMgT2ZmaWNlMRow +GAYDVQQDExFjYS50Y28uY2Vuc3VzLmdvdjEgMB4GCSqGSIb3DQEJARYRY2FAdGNv +LmNlbnN1cy5nb3YwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSqB5S +s674S6Hnpnl+/cT3OLrUCmuM1KZs+Uo5EsFcZzm4Me/XiF8izGSydFtAKFRbyyk5 +j/K5WLGxo7Ix6eCA1PZXWu6aJOfMmPRb1LaeIst1IlSCpjUoZ8pl60fjYLtbEK79 +STM/nrdV0E2EqcJu7dfzMB1oK96NG6tu8C7m7UgIbSv15NDapgDhyril6J4wVQJU +DOUGRbWjv0Qo6Re0NPBkRFf3owToopNQlQSGZU2UnUehheqXPzk4VQisPrhcVsbg +iu4c98gjtGHK1k2DyJOwsFq2hWmAByLZLJXR7pTqv7Ue8gogFl/ggbvuWrKlVmCh +wKln1pPSLYZ/txTZAgMBAAGjggGDMIIBfzA4BgNVHR8EMTAvMC2gK6AphidodHRw +Oi8vY2EuYXBwcy50Y28uY2Vuc3VzLmdvdi9jZXJ0cy9jcmwwHQYDVR0OBBYEFA8x +pgy5aVvXWgTVO8E7yyO3kp9yMIHUBgNVHSMEgcwwgcmAFA8xpgy5aVvXWgTVO8E7 +yyO3kp9yoYGlpIGiMIGfMQswCQYDVQQGEwJVUzERMA8GA1UECBMITWFyeWxhbmQx +GzAZBgNVBAoTElUuUy4gQ2Vuc3VzIEJ1cmVhdTEiMCAGA1UECxMZVGVsZWNvbW11 +bmljYXRpb25zIE9mZmljZTEaMBgGA1UEAxMRY2EudGNvLmNlbnN1cy5nb3YxIDAe +BgkqhkiG9w0BCQEWEWNhQHRjby5jZW5zdXMuZ292ggkAyf2Cocx2eXcwDwYDVR0T +AQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYwLwYDVR0RBCgwJoERY2FAdGNvLmNlbnN1 +cy5nb3aCEWNhLnRjby5jZW5zdXMuZ292MA0GCSqGSIb3DQEBBQUAA4IBAQCLNU9/ +OxA2adbFXwiAh8XztL3MN7OUeXasSKtSDo00Ays/Sph1DXkUozSwx3B2JHtfrMj+ +A64qzjRm/Y7sDaM4SFa+Y3rdt7U9UY2UxQLo92zHQMqIbQhrdKBTiCVMrBvBzwWg +SI7KPi2lel499yb0vH/I6czuyQNTuYzHAsufYKeMMq4CeiBbboAegClpYJi5jJLl +dFQZpDUwSs+Pfb95CjPlfc0V3AH6GazbS3BNMMghECpL4rF0m7F7L3nDCklx1PsC +z2chyETY1X74Cg3D1mFV3iUjIvr6+eIZDQ3BStGwFjzxmdH2U2yh1nJnJzNXka9g +lUpluNENkgVZmOys +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIE1zCCA7+gAwIBAgITZQAANNYDIG4D4LElTwABAAA01jANBgkqhkiG9w0BAQsF +ADBiMRMwEQYKCZImiZPyLGQBGRYDZ292MRYwFAYKCZImiZPyLGQBGRYGY2Vuc3Vz +MRMwEQYKCZImiZPyLGQBGRYDZWFkMR4wHAYDVQQDExVVUyBDZW5zdXMgQnVyZWF1 +IENBIDMwHhcNMjIxMjI3MjExNTIxWhcNMjYxMjI3MjEyNTIxWjBoMRMwEQYKCZIm +iZPyLGQBGRYDZ292MRYwFAYKCZImiZPyLGQBGRYGY2Vuc3VzMRMwEQYKCZImiZPy +LGQBGRYDZWFkMSQwIgYDVQQDExtVUyBDZW5zdXMgQnVyZWF1IENBIDMgU3ViIDEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPzIqL5D96G48OMzx7WZdi +01e6K5Tllvz5REVKMOlFIS22y/iAnr3hbA1FXH1ML+t0n7e7jKic+E4pXc90n5DP +0bBS5+srnkw3OvjTY//uBU6rMl5vTtbGY3BhL0jsoeT+/JdTTrif6gyNCSkpNvw0 +Hao3Yc5kfcU5Vo90nm1+gonOqa6bQFN/i4hwI2quu4M3IkLJZaWQQ0z1pIbbJyk0 +qANrUKy4yTABo4KkNdqKmRvvvRWuDpFmNJwDDpdT010HDX5Pdc48fFVPO0Faoox9 +A7BtBZL273u7O9dpE0ajTHk1De5ZxbgO8yFmGWVj6BYgI86HJCq74RP4K6IJuOGZ +AgMBAAGjggF+MIIBejAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUGFK9+ZBI +M/dcDY4ObcigYRSrASQwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDgYDVR0P +AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUIHg560/tAvCA +2ptcgOiykULWKcQwVwYDVR0fBFAwTjBMoEqgSIZGaHR0cDovL3BraS5lYWQuY2Vu +c3VzLmdvdi9DZXJ0RW5yb2xsL1VTJTIwQ2Vuc3VzJTIwQnVyZWF1JTIwQ0ElMjAz +LmNybDCBkAYIKwYBBQUHAQEEgYMwgYAwUgYIKwYBBQUHMAKGRmh0dHA6Ly9wa2ku +ZWFkLmNlbnN1cy5nb3YvQ2VydEVucm9sbC9VUyUyMENlbnN1cyUyMEJ1cmVhdSUy +MENBJTIwMy5jcnQwKgYIKwYBBQUHMAGGHmh0dHA6Ly9wa2kuZWFkLmNlbnN1cy5n +b3Yvb2NzcDANBgkqhkiG9w0BAQsFAAOCAQEAm1wFAR44iAl7dNHMjzIaaQe7dBbQ +gyS1t2mygO843JtcS2J/m3yGmEfo8wEwK5IxwX2UTmnc7Dh/iWlMO6cl8JKN12Fp +FM/yfpb+jaKECrsGW3uY5yKhrqmVGO9YnbiiGN07w0t+dbWAYGCtULoocYhFaLVQ +68Iv9KpOKVB3XKbP4bI2uhtx9H+uPHanhWVTJRHjg5pqI+xV7BoPfmods74oQfgm +PrsZqbwEvItVBMTGFQvhi60iEklk42s7ln/X7EqpKjtXwR4WAGuWPjTJ3OWkvVa4 +cNFBQRSALyDpqJFCqFoZBym9coyibi39QkWD2eizR4wm69jC66GOEmEb/A== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIE1zCCA7+gAwIBAgITZQAANNSyNhQfwZNfDwABAAA01DANBgkqhkiG9w0BAQsF +ADBiMRMwEQYKCZImiZPyLGQBGRYDZ292MRYwFAYKCZImiZPyLGQBGRYGY2Vuc3Vz +MRMwEQYKCZImiZPyLGQBGRYDZWFkMR4wHAYDVQQDExVVUyBDZW5zdXMgQnVyZWF1 +IENBIDMwHhcNMjIxMjI3MTcyOTQ3WhcNMjYxMjI3MTczOTQ3WjBoMRMwEQYKCZIm +iZPyLGQBGRYDZ292MRYwFAYKCZImiZPyLGQBGRYGY2Vuc3VzMRMwEQYKCZImiZPy +LGQBGRYDZWFkMSQwIgYDVQQDExtVUyBDZW5zdXMgQnVyZWF1IENBIDMgU3ViIDIw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+k0X7b2zULKIK7n3QEo6I +tY03iLD1+h4SLS+TcD1boOS5SR5A7nmtcSkn03xieHzQvb2YdQ8+ltlBBXFeQR4g +vTieZ77DN1pqDLkwThHscavRr8HHyuW20Bf9YYH11DzpuXe4WsMhkLeJWzZJ5GPI +TwWZFeCluJ9fb9/8wPhVERSDYtqS3DwdJ/6qkueJZ75AOMcmObx5pQWszypYQupm +L+oiofej7mu0gb7ioXwwM7XL8f28a2BEDFqM5M0sitBrC1yxN7a3cRnegT+PlCe/ +yiiihAZVYQt/HDEs4R4A85Wx/YUhiB3BKkyTUIV+abjeWMIrRi17SrxNDT9ZQkld +AgMBAAGjggF+MIIBejAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU4wpH6ieo +Hr13KKDb4stKDQFKE/MwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDgYDVR0P +AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUIHg560/tAvCA +2ptcgOiykULWKcQwVwYDVR0fBFAwTjBMoEqgSIZGaHR0cDovL3BraS5lYWQuY2Vu +c3VzLmdvdi9DZXJ0RW5yb2xsL1VTJTIwQ2Vuc3VzJTIwQnVyZWF1JTIwQ0ElMjAz +LmNybDCBkAYIKwYBBQUHAQEEgYMwgYAwUgYIKwYBBQUHMAKGRmh0dHA6Ly9wa2ku +ZWFkLmNlbnN1cy5nb3YvQ2VydEVucm9sbC9VUyUyMENlbnN1cyUyMEJ1cmVhdSUy +MENBJTIwMy5jcnQwKgYIKwYBBQUHMAGGHmh0dHA6Ly9wa2kuZWFkLmNlbnN1cy5n +b3Yvb2NzcDANBgkqhkiG9w0BAQsFAAOCAQEAs3Kf6bImA8lfZweCuCtcaSDRCr0X +pyr8A1TI95PgzpAEptGay/Ve2Bbs9JAzMIPqznEy7hC9kNY6Wn8jRxwSFhHJ1MVV +bMITRguhJ5asApmDInCx1/iha3WnsnmeonmPFOgpF/lgiyY7kMwXPzBNYPrs3qdf +AFTaF0rMRxJ3nz0R6C2K16hCDNOFW1E8X3eBFRK9poGsdOzpkrugrFDXGBWGIxIr +IUIE1xbQQzVv/qZ9Q1s7g6nt3zci//CgBXXRHn30G9SWbHASJhbN/XZOYMKtS15T +COzOm7B5Ujjw2h8YspiZKgINsWLbhU9E5OQkJuHeDpBpp/EFMbwsRQH//A== +-----END CERTIFICATE----- diff --git a/buildspecs/scripts/pip.conf b/buildspecs/scripts/pip.conf new file mode 100644 index 0000000..8f18e29 --- /dev/null +++ b/buildspecs/scripts/pip.conf @@ -0,0 +1,10 @@ +[global] +cert = ~/.pip/pip-cert.pem +# proxy = http://proxy.tco.census.gov:3128 +index = https://nexus.it.census.gov:8443/repository/DataScience-Group/pypi +index-url = https://nexus.it.census.gov:8443/repository/DataScience-Group/simple +trusted-host = nexus.it.census.gov + pypi.python.org + pypi.org + files.pythonhosted.org + proxy.tco.census.gov diff --git a/buildspecs/scripts/sechub_parser.py b/buildspecs/scripts/sechub_parser.py new file mode 100644 index 0000000..34c43a3 --- /dev/null +++ b/buildspecs/scripts/sechub_parser.py @@ -0,0 +1,101 @@ +import json +import boto3 +import datetime +import os + +# import sechub + sts boto3 client +securityhub = boto3.client('securityhub') +sts = boto3.client('sts') + +# retrieve account id from STS GetCallerID +getAccount = sts.get_caller_identity() +awsAccount = str(getAccount['Account']) +# retrieve env vars from codebuild +awsRegion = os.environ['AWS_REGION'] +codebuildBuildArn = os.environ['CODEBUILD_BUILD_ARN'] +containerName = os.environ['docker_img_name'] +containerTag = os.environ['docker_tag'] + +# open Trivy vuln report & parse out vuln info +with open('results.json') as json_file: + data = json.load(json_file) + if data[0]['Vulnerabilities'] is None: + print('No vulnerabilities') + else: + for p in data[0]['Vulnerabilities']: + cveId = str(p['VulnerabilityID']) + cveTitle = str(p['Title']) + cveDescription = str(p['Description']) + cveDescription = (cveDescription[:1021] + '..') if len(cveDescription) > 1021 else cveDescription + packageName = str(p['PkgName']) + installedVersion = str(p['InstalledVersion']) + fixedVersion = str(p['FixedVersion']) + trivySeverity = str(p['Severity']) + cveReference = str(p['References'][0]) + # create ISO 8601 timestamp + iso8601Time = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + # map Trivy severity to ASFF severity + if trivySeverity == 'LOW': + trivyProductSev = int(1) + trivyNormalizedSev = trivyProductSev * 10 + elif trivySeverity == 'MEDIUM': + trivyProductSev = int(4) + trivyNormalizedSev = trivyProductSev * 10 + elif trivySeverity == 'HIGH': + trivyProductSev = int(7) + trivyNormalizedSev = trivyProductSev * 10 + elif trivySeverity == 'CRITICAL': + trivyProductSev = int(9) + trivyNormalizedSev = trivyProductSev * 10 + else: + print('No vulnerability information found') + try: + response = securityhub.batch_import_findings( + Findings=[ + { + 'SchemaVersion': '2018-10-08', + 'Id': containerName + ':' + containerTag + '/' + cveId, + 'ProductArn': 'arn:aws:securityhub:' + awsRegion + ':' + ':product/aquasecurity/aquasecurity', + 'GeneratorId': codebuildBuildArn, + 'AwsAccountId': awsAccount, + 'Types': [ 'Software and Configuration Checks/Vulnerabilities/CVE' ], + 'CreatedAt': iso8601Time, + 'UpdatedAt': iso8601Time, + 'Severity': { + 'Product': trivyProductSev, + 'Normalized': trivyNormalizedSev + }, + 'Title': 'Trivy found a vulnerability to ' + cveId + ' in container ' + containerName, + 'Description': cveDescription, + 'Remediation': { + 'Recommendation': { + 'Text': 'More information on this vulnerability is provided in the hyperlink', + 'Url': cveReference + } + }, + 'ProductFields': { 'Product Name': 'Trivy' }, + 'Resources': [ + { + 'Type': 'Container', + 'Id': containerName + ':' + containerTag, + 'Partition': 'aws', + 'Region': awsRegion, + 'Details': { + 'Container': { 'ImageName': containerName + ':' + containerTag }, + 'Other': { + 'CVE ID': cveId, + 'CVE Title': cveTitle, + 'Installed Package': packageName + ' ' + installedVersion, + 'Patched Package': packageName + ' ' + fixedVersion + } + } + }, + ], + 'RecordState': 'ACTIVE' + } + ] + ) + print(response) + except Exception as e: + print(e) + raise diff --git a/buildspecs/terragrunt.yml b/buildspecs/terragrunt.yml index 57dae77..4e3b74c 100644 --- a/buildspecs/terragrunt.yml +++ b/buildspecs/terragrunt.yml @@ -2,21 +2,18 @@ version: 0.2 env: variables: + ARTIFACT_BUCKET: "${ARTIFACT_BUCKET}" BASE_DIR: "lab" - TOOLS_DIR: "/tmp/build-tools/" - TERRAGRUNT_PATH: "${TERRAGRUNT_PATH}" - ARTIFACTS_BUCKET: "${ARTIFACTS_BUCKET}" - PROXY_CONFIG: "${PROXY_CONFIG}" - - secrets-manager: - GITHUB_TOKEN: ${GITHUB_TOKEN_ARN} - + PROXY_CONFIG: ${PROXY_CONFIG} + REQUIRED_TOOLS: "terraform terragrunt" + TOOL_DEFINITIONS: ${TOOL_DEFINITIONS} + TOOLS_DIR: "/tmp/build-tools" exported-variables: - TERRAGRUNT_PATH cache: paths: - - '/tmp/build-tools/**/*' + - $CODEBUILD_SRC_DIR/.tool_cache/**/* phases: install: @@ -36,31 +33,23 @@ phases: # Create tools directory if it doesn't exist - mkdir -p $TOOLS_DIR/bin - # Get tools from S3 artifacts bucket instead of downloading from internet + # Download and execute the centralized tool management script - | - # Terraform - if [ ! -f "$TOOLS_DIR/bin/terraform" ]; then - echo "Copying Terraform from S3 artifacts bucket" - if ! aws s3 cp s3://${ARTIFACTS_BUCKET}/tools/terraform.zip $TOOLS_DIR; then - echo "Failed to download Terraform" - exit 1 - fi - unzip -o $TOOLS_DIR/terraform.zip -d $TOOLS_DIR/bin/ - chmod +x $TOOLS_DIR/bin/terraform - fi + echo "--- Downloading and Executing Tool Management Script ---" + MANAGE_TOOLS_SCRIPT_S3_KEY="tools/scripts/manage_tools.sh" + LOCAL_SCRIPT_PATH="${TOOLS_DIR}/manage_tools.sh" - # Terragrunt - if [ ! -f "$TOOLS_DIR/bin/terragrunt" ]; then - echo "Copying Terragrunt from S3 artifacts bucket" - if ! aws s3 cp s3://${ARTIFACTS_BUCKET}/tools/terragrunt $TOOLS_DIR; then - echo "Failed to download Terragrunt" - exit 1 - fi - mv $TOOLS_DIR/terragrunt $TOOLS_DIR/bin/ - chmod +x $TOOLS_DIR/bin/terragrunt + - | + if [ ! -f "$LOCAL_SCRIPT_PATH" ]; then + echo "Downloading Tools Script from S3 ${ARTIFACT_BUCKET}...." + aws s3 cp s3://${ARTIFACT_BUCKET}/$MANAGE_TOOLS_SCRIPT_S3_KEY $LOCAL_SCRIPT_PATH fi - # Add tools to PATH + chmod +x "$LOCAL_SCRIPT_PATH" + echo "Executing $LOCAL_SCRIPT_PATH..." + "$LOCAL_SCRIPT_PATH" # Script will use ARTIFACT_BUCKET, TOOL_DEFINITIONS, REQUIRED_TOOLS, CODEBUILD_SRC_DIR + echo "--- Tool Management Script Execution Finished ---" + - export PATH=$TOOLS_DIR/bin:$PATH - aws sts get-caller-identity - terraform --version @@ -68,13 +57,12 @@ phases: build: commands: - - echo "Running Terragrunt plan with assumed role profile" + - echo "Running Terragrunt plan" - cd $TERRAGRUNT_PATH - export http_proxy=$PROXY_CONFIG - export https_proxy=$PROXY_CONFIG - export NO_PROXY=.census.gov,169.254.169.254,148.129.0.0/16,10.0.0.0/8,172.16.0/12,.eks.amazonaws.com,.s3.amazonaws.com,.amazonaws.com,.gcr.io,.pkg.dev - - - terragrunt run-all plan --terragrunt-non-interactive --terragrunt-debug --terragrunt-log-level debug + - terragrunt run-all plan --terragrunt-non-interactive post_build: commands: