diff --git a/.github/workflows/reusable-check-network.yml b/.github/workflows/reusable-check-network.yml new file mode 100644 index 0000000..ca675eb --- /dev/null +++ b/.github/workflows/reusable-check-network.yml @@ -0,0 +1,70 @@ +name: Check Network File Changes + +on: + workflow_call: + secrets: + token: + required: true + outputs: + devnet_changed: + description: 'Whether the devnet network files were changed' + value: ${{ jobs.check-network-changes.outputs.devnet_changed }} + testnet_changed: + description: 'Whether the testnet network files were changed' + value: ${{ jobs.check-network-changes.outputs.testnet_changed }} + +jobs: + check-network-changes: + runs-on: ubuntu-latest + outputs: + devnet_changed: ${{ steps.check_changes.outputs.devnet_changed }} + testnet_changed: ${{ steps.check_changes.outputs.testnet_changed }} + + steps: + - uses: jitterbit/get-changed-files@v1 + id: changed_files + with: + format: space-delimited + token: ${{ secrets.token }} + + - name: Determine if relevant files changed and retrieve network types + id: check_changes + run: | + CHANGED_FILES="${{ steps.changed_files.outputs.modified }}" + echo "Changed files: $CHANGED_FILES" + + # Define the files we care about + AUTHORIZED_KEYS_DEVNET="authorized_keys_odyssey_devnet" + AUTHORIZED_KEYS_TESTNET="authorized_keys_odyssey_testnet" + BASTION_ACCESS_DEVNET="bastion-access-devnet.yml" + BASTION_ACCESS_TESTNET="bastion-access-testnet.yml" + + # Initialize flags for network types + DEVNET_CHANGED=false + TESTNET_CHANGED=false + + # Check if any of the files were modified and set the flags + if echo "$CHANGED_FILES" | grep -q "$AUTHORIZED_KEYS_DEVNET"; then + DEVNET_CHANGED=true + echo "authorized_keys for devnet changed" + fi + if echo "$CHANGED_FILES" | grep -q "$AUTHORIZED_KEYS_TESTNET"; then + TESTNET_CHANGED=true + echo "authorized_keys for testnet changed" + fi + if echo "$CHANGED_FILES" | grep -q "$BASTION_ACCESS_DEVNET"; then + DEVNET_CHANGED=true + echo "bastion access for devnet changed" + fi + if echo "$CHANGED_FILES" | grep -q "$BASTION_ACCESS_TESTNET"; then + TESTNET_CHANGED=true + echo "bastion access for testnet changed" + fi + + # Output the values for use in subsequent steps + echo "devnet_changed=$DEVNET_CHANGED" >> $GITHUB_OUTPUT + echo "testnet_changed=$TESTNET_CHANGED" >> $GITHUB_OUTPUT + + # Print the results for verification + echo "DevNet changed: $DEVNET_CHANGED" + echo "TestNet changed: $TESTNET_CHANGED" diff --git a/.github/workflows/reusable-fetch-bastion-ips.yml b/.github/workflows/reusable-fetch-bastion-ips.yml new file mode 100644 index 0000000..00db9f1 --- /dev/null +++ b/.github/workflows/reusable-fetch-bastion-ips.yml @@ -0,0 +1,116 @@ +name: Fetch Bastion Host IPs + +on: + workflow_call: + inputs: + role_to_assume: + description: "The role to assume" + required: true + type: string + aws_region: + description: "The AWS region to use" + required: true + type: string + devnet_changed: + description: "Boolean to check if DevNet files have changed" + required: true + type: string + testnet_changed: + description: "Boolean to check if TestNet files have changed" + required: true + type: string + instance_name_devnet: + description: "The instance name for Odyssey DevNet Bastion" + required: true + type: string + instance_name_testnet: + description: "The instance name for Odyssey TestNet Bastion" + required: true + type: string + role_bastion: + description: "The role for the Bastion host (default: bastion)" + required: true + type: string + default: "bastion" + instance_region: + description: "The region to search for instances" + required: true + type: string + outputs: + instance_ip_odyssey_devnet: + description: "The IP address of the DevNet Bastion host" + value: ${{ jobs.fetch_bastion_ips.outputs.instance_ip_odyssey_devnet }} + instance_ip_odyssey_testnet: + description: "The IP address of the TestNet Bastion host" + value: ${{ jobs.fetch_bastion_ips.outputs.instance_ip_odyssey_testnet }} + +jobs: + fetch_bastion_ips: + runs-on: ubuntu-latest + outputs: + instance_ip_odyssey_devnet: ${{ steps.get_instance_ips.outputs.instance_ip_odyssey_devnet }} + instance_ip_odyssey_testnet: ${{ steps.get_instance_ips.outputs.instance_ip_odyssey_testnet }} + + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + role-to-assume: ${{ inputs.role_to_assume }} + aws-region: ${{ inputs.aws_region }} + role-session-name: github-actions + + - name: Fetch Bastion Host IPs + id: get_instance_ips + run: | + DEVNET_CHANGED="${{ inputs.devnet_changed }}" + TESTNET_CHANGED="${{ inputs.testnet_changed }}" + INSTANCE_NAME_ODYSSEY_DEVNET="${{ inputs.instance_name_devnet }}" + INSTANCE_NAME_ODYSSEY_TESTNET="${{ inputs.instance_name_testnet }}" + ROLE_BASTION="${{ inputs.role_bastion }}" + INSTANCE_REGION="${{ inputs.instance_region }}" + + # Fetch DevNet Bastion IP if changes are detected + if [ "$DEVNET_CHANGED" = "true" ]; then + echo "Fetching DevNet Bastion IP" + INSTANCE_IP_ODYSSEY_DEVNET=$(aws ec2 describe-instances \ + --region $INSTANCE_REGION \ + --filters "Name=tag:Network,Values=$INSTANCE_NAME_ODYSSEY_DEVNET" "Name=tag:Role,Values=$ROLE_BASTION" \ + --query "Reservations[].Instances[].PublicIpAddress" \ + --output text) + + if [ -n "$INSTANCE_IP_ODYSSEY_DEVNET" ]; then + echo "Odyssey DevNet Instance IP: $INSTANCE_IP_ODYSSEY_DEVNET" + echo "instance_ip_odyssey_devnet=$INSTANCE_IP_ODYSSEY_DEVNET" >> $GITHUB_OUTPUT + else + echo "Failed to fetch DevNet IP or no instance found" + fi + else + echo "DevNet unchanged, skipping IP fetch" + fi + + # Fetch TestNet Bastion IP if changes are detected + if [ "$TESTNET_CHANGED" = "true" ]; then + echo "Fetching TestNet Bastion IP" + INSTANCE_IP_ODYSSEY_TESTNET=$(aws ec2 describe-instances \ + --region $INSTANCE_REGION \ + --filters "Name=tag:Network,Values=$INSTANCE_NAME_ODYSSEY_TESTNET" "Name=tag:Role,Values=$ROLE_BASTION" \ + --query "Reservations[].Instances[].PublicIpAddress" \ + --output text) + + if [ -n "$INSTANCE_IP_ODYSSEY_TESTNET" ]; then + echo "Odyssey TestNet Instance IP: $INSTANCE_IP_ODYSSEY_TESTNET" + echo "instance_ip_odyssey_testnet=$INSTANCE_IP_ODYSSEY_TESTNET" >> $GITHUB_OUTPUT + else + echo "Failed to fetch TestNet IP or no instance found" + fi + else + echo "TestNet unchanged, skipping IP fetch" + fi + + # Display the fetched IPs (if any) + if [ -n "$INSTANCE_IP_ODYSSEY_DEVNET" ]; then + echo "Odyssey DevNet Instance IP: $INSTANCE_IP_ODYSSEY_DEVNET" + fi + if [ -n "$INSTANCE_IP_ODYSSEY_TESTNET" ]; then + echo "Odyssey TestNet Instance IP: $INSTANCE_IP_ODYSSEY_TESTNET" + fi diff --git a/.github/workflows/reusable-fetch-network-node-ips.yml b/.github/workflows/reusable-fetch-network-node-ips.yml new file mode 100644 index 0000000..f503cf2 --- /dev/null +++ b/.github/workflows/reusable-fetch-network-node-ips.yml @@ -0,0 +1,132 @@ +name: Fetch Network Node IPs + +on: + workflow_call: + inputs: + role_to_assume: + description: "The role to assume" + required: true + type: string + aws_region: + description: "The AWS region to use" + required: true + type: string + devnet_changed: + description: "Boolean to check if DevNet files have changed" + required: true + type: string + testnet_changed: + description: "Boolean to check if TestNet files have changed" + required: true + type: string + network_odyssey_devnet: + description: "The network name for Odyssey DevNet" + required: true + type: string + network_odyssey_testnet: + description: "The network name for Odyssey TestNet" + required: true + type: string + aws_regions: + description: "Comma-separated list of AWS regions" + required: true + type: string + outputs: + node_ips_odyssey_devnet: + description: "The IP addresses for DevNet" + value: ${{ jobs.fetch_network_node_ips.outputs.node_ips_odyssey_devnet }} + node_ips_odyssey_testnet: + description: "The IP addresses for TestNet" + value: ${{ jobs.fetch_network_node_ips.outputs.node_ips_odyssey_testnet }} + +jobs: + fetch_network_node_ips: + runs-on: ubuntu-latest + outputs: + node_ips_odyssey_devnet: ${{ steps.get_all_node_ips.outputs.node_ips_odyssey_devnet }} + node_ips_odyssey_testnet: ${{ steps.get_all_node_ips.outputs.node_ips_odyssey_testnet }} + + steps: + - name: Checkout repository + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + role-to-assume: ${{ inputs.role_to_assume }} + aws-region: ${{ inputs.aws_region }} + role-session-name: github-actions + + - name: Fetch All Network Node IPs, Regions, and SSH Users + id: get_all_node_ips + run: | + DEVNET_CHANGED="${{ inputs.devnet_changed }}" + TESTNET_CHANGED="${{ inputs.testnet_changed }}" + NETWORK_ODYSSEY_DEVNET="${{ inputs.network_odyssey_devnet }}" + NETWORK_ODYSSEY_TESTNET="${{ inputs.network_odyssey_testnet }}" + + # Convert the comma-separated regions into an array + IFS=',' read -ra regions <<< "${{ inputs.aws_regions }}" + + pwd + ls -la + + # Make the script executable + chmod +x scripts/fetch_all_node_ips.sh + + if [ "$DEVNET_CHANGED" = "true" ]; then + echo "Fetching DevNet Node IPs" + DEVNET_NODE_IPS="" + for region in "${regions[@]}"; do + NODE_INFOS=$(./scripts/fetch_all_node_ips.sh "$NETWORK_ODYSSEY_DEVNET" "$region") + if [ -n "$NODE_INFOS" ]; then + # Append node infos to DEVNET_NODE_IPS, separated by semicolons + while IFS= read -r line; do + DEVNET_NODE_IPS+="${line};" + done <<< "$NODE_INFOS" + fi + done + # Remove trailing semicolon + DEVNET_NODE_IPS="${DEVNET_NODE_IPS%?}" + echo "node_ips_odyssey_devnet<> $GITHUB_OUTPUT + echo "$DEVNET_NODE_IPS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "Odyssey DevNet Node IPs: $DEVNET_NODE_IPS" + else + echo "DevNet unchanged, skipping Node IP fetch" + fi + + if [ "$TESTNET_CHANGED" = "true" ]; then + echo "Fetching TestNet Node IPs" + TESTNET_NODE_IPS="" + for region in "${regions[@]}"; do + NODE_INFOS=$(./scripts/fetch_all_node_ips.sh "$NETWORK_ODYSSEY_TESTNET" "$region") + if [ -n "$NODE_INFOS" ]; then + # Append node infos to TESTNET_NODE_IPS, separated by semicolons + while IFS= read -r line; do + TESTNET_NODE_IPS+="${line};" + done <<< "$NODE_INFOS" + fi + done + # Remove trailing semicolon + TESTNET_NODE_IPS="${TESTNET_NODE_IPS%?}" + echo "node_ips_odyssey_testnet<> $GITHUB_OUTPUT + echo "$TESTNET_NODE_IPS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "Odyssey TestNet Node IPs: $TESTNET_NODE_IPS" + else + echo "TestNet unchanged, skipping Node IP fetch" + fi + + # If both are false, output a message + if [ "$DEVNET_CHANGED" = "false" ] && [ "$TESTNET_CHANGED" = "false" ]; then + echo "No changes detected for either network, no Node IPs fetched" + fi + + # Display the fetched Node IPs (if any) + if [ -n "$DEVNET_NODE_IPS" ]; then + echo "Odyssey DevNet Node IPs: $DEVNET_NODE_IPS" + fi + if [ -n "$TESTNET_NODE_IPS" ]; then + echo "Odyssey TestNet Node IPs: $TESTNET_NODE_IPS" + fi diff --git a/.github/workflows/reusable-fetch-security-group-ids.yml b/.github/workflows/reusable-fetch-security-group-ids.yml new file mode 100644 index 0000000..16692b8 --- /dev/null +++ b/.github/workflows/reusable-fetch-security-group-ids.yml @@ -0,0 +1,184 @@ +name: Fetch Security Group IDs + +on: + workflow_call: + inputs: + role_to_assume: + description: "The role to assume" + required: true + type: string + aws_region: + description: "The AWS region to use" + required: true + type: string + devnet_changed: + description: "Boolean to check if DevNet files have changed" + required: true + type: string + testnet_changed: + description: "Boolean to check if TestNet files have changed" + required: true + type: string + instance_name_devnet: + description: "The instance name for Odyssey DevNet Bastion" + required: true + type: string + instance_name_testnet: + description: "The instance name for Odyssey TestNet Bastion" + required: true + type: string + role_bastion: + description: "The role for the Bastion host (default: bastion)" + required: true + type: string + default: "bastion" + sg_name_devnet: + description: "The security group name for Odyssey DevNet" + required: true + type: string + sg_name_testnet: + description: "The security group name for Odyssey TestNet" + required: true + type: string + aws_regions: + description: "Comma-separated list of AWS regions" + required: true + type: string + instance_region: + description: "The region of the Bastion instance" + required: true + type: string + outputs: + security_group_ids_devnet: + description: "Comma-separated security group IDs for DevNet" + value: ${{ jobs.fetch_security_group_ids.outputs.security_group_ids_devnet }} + security_group_ids_testnet: + description: "Comma-separated security group IDs for TestNet" + value: ${{ jobs.fetch_security_group_ids.outputs.security_group_ids_testnet }} + bastion_sg_id_devnet: + description: "The security group ID for DevNet Bastion" + value: ${{ jobs.fetch_security_group_ids.outputs.bastion_sg_id_devnet }} + bastion_sg_id_testnet: + description: "The security group ID for TestNet Bastion" + value: ${{ jobs.fetch_security_group_ids.outputs.bastion_sg_id_testnet }} + +jobs: + fetch_security_group_ids: + runs-on: ubuntu-latest + outputs: + security_group_ids_devnet: ${{ steps.get_sg_ids.outputs.security_group_ids_devnet }} + security_group_ids_testnet: ${{ steps.get_sg_ids.outputs.security_group_ids_testnet }} + bastion_sg_id_devnet: ${{ steps.get_sg_ids.outputs.bastion_security_group_id_devnet }} + bastion_sg_id_testnet: ${{ steps.get_sg_ids.outputs.bastion_security_group_id_testnet }} + + steps: + - name: Checkout repository + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + role-to-assume: ${{ inputs.role_to_assume }} + aws-region: ${{ inputs.aws_region }} + role-session-name: github-actions + + - name: Get Security Group IDs + id: get_sg_ids + run: | + DEVNET_CHANGED="${{ inputs.devnet_changed }}" + TESTNET_CHANGED="${{ inputs.testnet_changed }}" + INSTANCE_NAME_ODYSSEY_DEVNET="${{ inputs.instance_name_devnet }}" + INSTANCE_NAME_ODYSSEY_TESTNET="${{ inputs.instance_name_testnet }}" + ROLE_BASTION="${{ inputs.role_bastion }}" + SG_NAME_DEVNET="${{ inputs.sg_name_devnet }}" + SG_NAME_TESTNET="${{ inputs.sg_name_testnet }}" + + # Convert the comma-separated regions into an array + IFS=',' read -ra regions <<< "${{ inputs.aws_regions }}" + + # Ensure the script has execution permissions + chmod +x ./scripts/get_sg_id_by_name.sh + + if [ "$DEVNET_CHANGED" = "true" ]; then + echo "Searching for DevNet security group..." + SG_IDS_DEVNET=$(./scripts/get_sg_id_by_name.sh "$SG_NAME_DEVNET" "${regions[@]}") + if [ $? -eq 0 ] && [ -n "$SG_IDS_DEVNET" ]; then + echo "security_group_ids_devnet=$SG_IDS_DEVNET" >> $GITHUB_OUTPUT + echo "DevNet Security Group IDs: $SG_IDS_DEVNET" + else + echo "Failed to fetch DevNet Security Group IDs or no security groups found" + exit 1 + fi + + echo "Searching for DevNet Bastion security group..." + INSTANCE_ID=$(aws ec2 describe-instances \ + --region ${{ inputs.instance_region }} \ + --filters \ + "Name=tag:Network,Values=$INSTANCE_NAME_ODYSSEY_DEVNET" \ + "Name=tag:Role,Values=$ROLE_BASTION" \ + "Name=tag:Name,Values=$INSTANCE_NAME_ODYSSEY_DEVNET" \ + --query "Reservations[].Instances[].InstanceId" \ + --output text) + + if [ -n "$INSTANCE_ID" ]; then + SG_ID_DEVNET_BASTION=$(aws ec2 describe-instances \ + --region ${{ inputs.instance_region }} \ + --instance-ids $INSTANCE_ID \ + --query "Reservations[].Instances[].SecurityGroups[0].GroupId" --output text) + + if [ -n "$SG_ID_DEVNET_BASTION" ]; then + echo "bastion_security_group_id_devnet=${SG_ID_DEVNET_BASTION}:${{ inputs.instance_region }}" >> $GITHUB_OUTPUT + echo "DevNet Bastion Security Group ID: $SG_ID_DEVNET_BASTION" + else + echo "Failed to fetch DevNet Bastion Security Group ID" + exit 1 + fi + else + echo "Failed to find DevNet Bastion instance" + exit 1 + fi + else + echo "DevNet unchanged, skipping security group ID fetch" + fi + + if [ "$TESTNET_CHANGED" = "true" ]; then + echo "Searching for TestNet security group..." + SG_IDS_TESTNET=$(./scripts/get_sg_id_by_name.sh "$SG_NAME_TESTNET" "${regions[@]}") + if [ $? -eq 0 ] && [ -n "$SG_IDS_TESTNET" ]; then + echo "security_group_ids_testnet=$SG_IDS_TESTNET" >> $GITHUB_OUTPUT + echo "TestNet Security Group IDs: $SG_IDS_TESTNET" + else + echo "Failed to fetch TestNet Security Group IDs or no security groups found" + exit 1 + fi + + echo "Searching for TestNet Bastion security group..." + INSTANCE_ID=$(aws ec2 describe-instances \ + --region ${{ inputs.instance_region }} \ + --filters \ + "Name=tag:Network,Values=$INSTANCE_NAME_ODYSSEY_TESTNET" \ + "Name=tag:Role,Values=$ROLE_BASTION" \ + "Name=tag:Name,Values=$INSTANCE_NAME_ODYSSEY_TESTNET" \ + --query "Reservations[].Instances[].InstanceId" \ + --output text) + + if [ -n "$INSTANCE_ID" ]; then + SG_ID_TESTNET_BASTION=$(aws ec2 describe-instances \ + --region ${{ inputs.instance_region }} \ + --instance-ids $INSTANCE_ID \ + --query "Reservations[].Instances[].SecurityGroups[0].GroupId" --output text) + + if [ -n "$SG_ID_TESTNET_BASTION" ]; then + echo "bastion_security_group_id_testnet=$SG_ID_TESTNET_BASTION" >> $GITHUB_OUTPUT + echo "TestNet Bastion Security Group ID: $SG_ID_TESTNET_BASTION" + else + echo "Failed to fetch TestNet Bastion Security Group ID" + exit 1 + fi + else + echo "Failed to find TestNet Bastion instance" + exit 1 + fi + else + echo "TestNet unchanged, skipping security group ID fetch" + fi diff --git a/.github/workflows/reusable-parse-bastion-access-files.yml b/.github/workflows/reusable-parse-bastion-access-files.yml new file mode 100644 index 0000000..040a99c --- /dev/null +++ b/.github/workflows/reusable-parse-bastion-access-files.yml @@ -0,0 +1,72 @@ +name: Parse Bastion Access Files + +on: + workflow_call: + inputs: + devnet_file: + description: "Path to the DevNet bastion-access file" + required: true + type: string + testnet_file: + description: "Path to the TestNet bastion-access file" + required: true + type: string + outputs: + ips_devnet: + description: "The IP addresses for DevNet" + value: ${{ jobs.parse_bastion_files.outputs.ips_devnet }} + ips_testnet: + description: "The IP addresses for TestNet" + value: ${{ jobs.parse_bastion_files.outputs.ips_testnet }} + +jobs: + parse_bastion_files: + runs-on: ubuntu-latest + outputs: + ips_devnet: ${{ steps.parse_yaml.outputs.ips_devnet }} + ips_testnet: ${{ steps.parse_yaml.outputs.ips_testnet }} + + steps: + - name: Checkout repository + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + + - name: Parse bastion-access files + id: parse_yaml + env: + DEVNET_FILE: ${{ inputs.devnet_file }} + TESTNET_FILE: ${{ inputs.testnet_file }} + run: | + # Parse DevNet bastion-access file + python3 scripts/parse_config.py $DEVNET_FILE + DEVNET_JSON="ip_permissions_${DEVNET_FILE%.*}.json" + if [ ! -f "$DEVNET_JSON" ]; then + echo "Error: $DEVNET_JSON not found." + exit 1 + fi + + # Add the DevNet IPs to GITHUB_OUTPUT as a single line + DEVNET_IPS=$(jq -c . < "$DEVNET_JSON") + echo "ips_devnet=$DEVNET_IPS" >> $GITHUB_OUTPUT + + # Parse TestNet bastion-access file + python3 scripts/parse_config.py $TESTNET_FILE + TESTNET_JSON="ip_permissions_${TESTNET_FILE%.*}.json" + if [ ! -f "$TESTNET_JSON" ]; then + echo "Error: $TESTNET_JSON not found." + exit 1 + fi + + # Add the TestNet IPs to GITHUB_OUTPUT as a single line + TESTNET_IPS=$(jq -c . < "$TESTNET_JSON") + echo "ips_testnet=$TESTNET_IPS" >> $GITHUB_OUTPUT + + # Print parsed data for verification + echo "DevNet IPs:" + cat "$DEVNET_JSON" + echo "TestNet IPs:" + cat "$TESTNET_JSON" + + - name: Print parsed data for verification + run: | + echo "IPS_DEVNET: ${{ steps.parse_yaml.outputs.ips_devnet }}" + echo "IPS_TESTNET: ${{ steps.parse_yaml.outputs.ips_testnet }}" diff --git a/.github/workflows/reusable-remove-gha-ip-from-sg.yml b/.github/workflows/reusable-remove-gha-ip-from-sg.yml new file mode 100644 index 0000000..51b5e3e --- /dev/null +++ b/.github/workflows/reusable-remove-gha-ip-from-sg.yml @@ -0,0 +1,133 @@ +name: Remove GitHub Actions Runner IP from Security Groups + +on: + workflow_call: + inputs: + role_to_assume: + description: "The role to assume" + required: true + type: string + aws_region: + description: "The AWS region to use" + required: true + type: string + runner_ip: + description: "The IP address of the GitHub Actions runner" + required: true + type: string + devnet_changed: + description: "Boolean to check if DevNet files have changed" + required: true + type: string + testnet_changed: + description: "Boolean to check if TestNet files have changed" + required: true + type: string + sg_ids_devnet: + description: "Comma-separated list of security group IDs for DevNet" + required: true + type: string + sg_ids_testnet: + description: "Comma-separated list of security group IDs for TestNet" + required: true + type: string + bastion_sg_id_devnet: + description: "Security group ID for DevNet Bastion" + required: true + type: string + bastion_sg_id_testnet: + description: "Security group ID for TestNet Bastion" + required: true + type: string + +jobs: + remove_runner_ip: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + role-to-assume: ${{ inputs.role_to_assume }} + aws-region: ${{ inputs.aws_region }} + role-session-name: github-actions + + - name: Remove GitHub Actions Runner IP from Security Groups + run: | + RUNNER_IP="${{ inputs.runner_ip }}" + DEVNET_CHANGED="${{ inputs.devnet_changed }}" + TESTNET_CHANGED="${{ inputs.testnet_changed }}" + SG_IDS_DEVNET="${{ inputs.sg_ids_devnet }}" + SG_IDS_TESTNET="${{ inputs.sg_ids_testnet }}" + BASTION_SG_ID_DEVNET="${{ inputs.bastion_sg_id_devnet }}" + BASTION_SG_ID_TESTNET="${{ inputs.bastion_sg_id_testnet }}" + + # Ensure the script has execution permissions + chmod +x scripts/remove_ip_from_sg.sh + + # Remove IP from DevNet security groups if changed + if [ "$DEVNET_CHANGED" = "true" ]; then + echo "Removing Runner IP from DevNet security groups" + IFS=',' read -r -a devnet_sg_array <<< "$SG_IDS_DEVNET" + for sg_data in "${devnet_sg_array[@]}"; do + sg_id=$(echo "$sg_data" | cut -d':' -f1) + sg_region=$(echo "$sg_data" | cut -d':' -f2) + echo "Removing Runner IP from DevNet Security Group $sg_id in region $sg_region" + if ./scripts/remove_ip_from_sg.sh "$sg_id" "DevNet" "$RUNNER_IP" "$sg_region"; then + echo "Successfully removed Runner IP from $sg_id in $sg_region" + else + echo "Failed to remove Runner IP from $sg_id in $sg_region" + exit 1 + fi + done + + # Remove IP from DevNet Bastion Security Group + if [ -n "$BASTION_SG_ID_DEVNET" ]; then + sg_id=$(echo "$BASTION_SG_ID_DEVNET" | cut -d':' -f1) + sg_region=$(echo "$BASTION_SG_ID_DEVNET" | cut -d':' -f2) + echo "Removing Runner IP from DevNet Bastion Security Group $sg_id in region $sg_region" + if ./scripts/remove_ip_from_sg.sh "$sg_id" "DevNet-Bastion" "$RUNNER_IP" "$sg_region"; then + echo "Successfully removed Runner IP from $sg_id in $sg_region" + else + echo "Failed to remove Runner IP from $sg_id in $sg_region" + exit 1 + fi + fi + else + echo "DevNet unchanged, skipping Runner IP removal" + fi + + # Remove IP from TestNet security groups if changed + if [ "$TESTNET_CHANGED" = "true" ]; then + echo "Removing Runner IP from TestNet security groups" + IFS=',' read -r -a testnet_sg_array <<< "$SG_IDS_TESTNET" + for sg_data in "${testnet_sg_array[@]}"; do + sg_id=$(echo "$sg_data" | cut -d':' -f1) + sg_region=$(echo "$sg_data" | cut -d':' -f2) + echo "Removing Runner IP from TestNet Security Group $sg_id in region $sg_region" + if ./scripts/remove_ip_from_sg.sh "$sg_id" "TestNet" "$RUNNER_IP" "$sg_region"; then + echo "Successfully removed Runner IP from $sg_id in $sg_region" + else + echo "Failed to remove Runner IP from $sg_id in $sg_region" + exit 1 + fi + done + + # Remove IP from TestNet Bastion Security Group + if [ -n "$BASTION_SG_ID_TESTNET" ]; then + sg_id=$(echo "$BASTION_SG_ID_TESTNET" | cut -d':' -f1) + sg_region=$(echo "$BASTION_SG_ID_TESTNET" | cut -d':' -f2) + echo "Removing Runner IP from TestNet Bastion Security Group $sg_id in region $sg_region" + if ./scripts/remove_ip_from_sg.sh "$sg_id" "TestNet-Bastion" "$RUNNER_IP" "$sg_region"; then + echo "Successfully removed Runner IP from $sg_id in $sg_region" + else + echo "Failed to remove Runner IP from $sg_id in $sg_region" + exit 1 + fi + fi + else + echo "TestNet unchanged, skipping Runner IP removal" + fi diff --git a/.github/workflows/reusable-revoke-inbound-rules.yml b/.github/workflows/reusable-revoke-inbound-rules.yml new file mode 100644 index 0000000..c29a522 --- /dev/null +++ b/.github/workflows/reusable-revoke-inbound-rules.yml @@ -0,0 +1,97 @@ +name: Revoke Inbound Rules + +on: + workflow_call: + inputs: + role_to_assume: + description: "The role to assume" + required: true + type: string + aws_region: + description: "The AWS region to use" + required: true + type: string + devnet_changed: + description: "Boolean to check if DevNet files have changed" + required: true + type: string + testnet_changed: + description: "Boolean to check if TestNet files have changed" + required: true + type: string + sg_ids_devnet: + description: "Comma-separated security group IDs for DevNet" + required: true + type: string + sg_ids_testnet: + description: "Comma-separated security group IDs for TestNet" + required: true + type: string + +jobs: + revoke_inbound_rules: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + role-to-assume: ${{ inputs.role_to_assume }} + aws-region: ${{ inputs.aws_region }} + role-session-name: github-actions + + - name: Revoke inbound rules for Bastion Hosts + run: | + DEVNET_CHANGED="${{ inputs.devnet_changed }}" + TESTNET_CHANGED="${{ inputs.testnet_changed }}" + SG_IDS_DEVNET="${{ inputs.sg_ids_devnet }}" + SG_IDS_TESTNET="${{ inputs.sg_ids_testnet }}" + + # Ensure the revoke script is executable + chmod +x scripts/revoke_inbound_rules.sh + + # Revoke rules for DevNet security group if changed + if [ "$DEVNET_CHANGED" = "true" ]; then + IFS=',' read -r -a devnet_sg_array <<< "$SG_IDS_DEVNET" + for sg_data in "${devnet_sg_array[@]}"; do + sg_id=$(echo "$sg_data" | cut -d':' -f1) + sg_region=$(echo "$sg_data" | cut -d':' -f2) + echo "Revoking inbound rules for DevNet security group $sg_id in region $sg_region" + ./scripts/revoke_inbound_rules.sh "$sg_id" "DevNet" "$sg_region" + if [ $? -ne 0 ]; then + echo "Failed to revoke inbound rules for DevNet security group $sg_id" + exit 1 + fi + done + echo "Inbound rules have been revoked for DevNet security groups" + else + echo "DevNet unchanged, skipping security group rule revocation" + fi + + # Revoke rules for TestNet security group if changed + if [ "$TESTNET_CHANGED" = "true" ]; then + IFS=',' read -r -a testnet_sg_array <<< "$SG_IDS_TESTNET" + for sg_data in "${testnet_sg_array[@]}"; do + sg_id=$(echo "$sg_data" | cut -d':' -f1) + sg_region=$(echo "$sg_data" | cut -d':' -f2) + echo "Revoking inbound rules for TestNet security group $sg_id in region $sg_region" + AWS_DEFAULT_REGION=$sg_region ./scripts/revoke_inbound_rules.sh "$sg_id" "TestNet" "$sg_region" + if [ $? -ne 0 ]; then + echo "Failed to revoke TestNet security group rules" + exit 1 + fi + done + echo "Inbound rules have been revoked for TestNet security groups" + else + echo "TestNet unchanged, skipping security group rule revocation" + fi + + # If both are false, output a message + if [ "$DEVNET_CHANGED" = "false" ] && [ "$TESTNET_CHANGED" = "false" ]; then + echo "No changes detected for either network, no security group rules revoked" + else + echo "Inbound rules have been revoked for changed security groups" + fi diff --git a/.github/workflows/reusable-update-security-groups.yml b/.github/workflows/reusable-update-security-groups.yml new file mode 100644 index 0000000..3a501a6 --- /dev/null +++ b/.github/workflows/reusable-update-security-groups.yml @@ -0,0 +1,130 @@ +name: Update Security Groups + +on: + workflow_call: + inputs: + role_to_assume: + description: "The role to assume" + required: true + type: string + aws_region: + description: "The AWS region to use" + required: true + type: string + devnet_changed: + description: "Boolean to check if DevNet files have changed" + required: true + type: string + testnet_changed: + description: "Boolean to check if TestNet files have changed" + required: true + type: string + ips_devnet: + description: "The IP addresses to be added to DevNet security groups" + required: true + type: string + ips_testnet: + description: "The IP addresses to be added to TestNet security groups" + required: true + type: string + sg_ids_devnet: + description: "Comma-separated list of security group IDs for DevNet" + required: true + type: string + sg_ids_testnet: + description: "Comma-separated list of security group IDs for TestNet" + required: true + type: string + +jobs: + update_security_groups: + runs-on: ubuntu-latest + outputs: + devnet_sg_update: ${{ steps.update_security_groups.outputs.devnet_sg_update }} + testnet_sg_update: ${{ steps.update_security_groups.outputs.testnet_sg_update }} + + steps: + - name: Checkout repository + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + role-to-assume: ${{ inputs.role_to_assume }} + aws-region: ${{ inputs.aws_region }} + role-session-name: github-actions + + - name: Update Security Groups with new IPs for Bastion Hosts + id: update_security_groups + run: | + DEVNET_CHANGED="${{ inputs.devnet_changed }}" + TESTNET_CHANGED="${{ inputs.testnet_changed }}" + IPS_DEVNET='${{ inputs.ips_devnet }}' + IPS_TESTNET='${{ inputs.ips_testnet }}' + SG_IDS_DEVNET="${{ inputs.sg_ids_devnet }}" + SG_IDS_TESTNET="${{ inputs.sg_ids_testnet }}" + + # Ensure the update script is executable + chmod +x scripts/update_security_group.sh + + # Update DevNet security group if changed + if [ "$DEVNET_CHANGED" = "true" ]; then + echo "Updating DevNet Security Group" + DEVNET_SG_UPDATE="" + IFS=',' read -ra sg_array <<< "$SG_IDS_DEVNET" + for sg_data in "${sg_array[@]}"; do + sg_id=$(echo "$sg_data" | cut -d':' -f1) + sg_region=$(echo "$sg_data" | cut -d':' -f2) + echo "Updating DevNet security group $sg_id in region $sg_region with IPs:" + echo "$IPS_DEVNET" + + OUTPUT=$(./scripts/update_security_group.sh "$sg_id" "$IPS_DEVNET" "DevNet" "$sg_region" 2>&1) + EXIT_CODE=$? + echo "$OUTPUT" + + if [ $EXIT_CODE -eq 0 ]; then + DEVNET_SG_UPDATE+="$sg_id:$sg_region:success;" + else + DEVNET_SG_UPDATE+="$sg_id:$sg_region:failure;" + fi + done + # Remove trailing semicolon + DEVNET_SG_UPDATE=$(echo "$DEVNET_SG_UPDATE" | sed 's/;$//') + echo "devnet_sg_update<> $GITHUB_OUTPUT + echo "$DEVNET_SG_UPDATE" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "DevNet Security Group Updates: $DEVNET_SG_UPDATE" + else + echo "DevNet unchanged, skipping security group update" + fi + + # Update TestNet security group if changed + if [ "$TESTNET_CHANGED" = "true" ]; then + echo "Updating TestNet Security Group" + TESTNET_SG_UPDATE="" + IFS=',' read -ra sg_array <<< "$SG_IDS_TESTNET" + for sg_data in "${sg_array[@]}"; do + sg_id=$(echo "$sg_data" | cut -d':' -f1) + sg_region=$(echo "$sg_data" | cut -d':' -f2) + echo "Updating TestNet security group $sg_id in region $sg_region with IPs:" + echo "$IPS_TESTNET" + + OUTPUT=$(./scripts/update_security_group.sh "$sg_id" "$IPS_TESTNET" "TestNet" "$sg_region" 2>&1) + EXIT_CODE=$? + echo "$OUTPUT" + + if [ $EXIT_CODE -eq 0 ]; then + TESTNET_SG_UPDATE+="$sg_id:$sg_region:success;" + else + TESTNET_SG_UPDATE+="$sg_id:$sg_region:failure;" + fi + done + # Remove trailing semicolon + TESTNET_SG_UPDATE=$(echo "$TESTNET_SG_UPDATE" | sed 's/;$//') + echo "testnet_sg_update<> $GITHUB_OUTPUT + echo "$TESTNET_SG_UPDATE" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "TestNet Security Group Updates: $TESTNET_SG_UPDATE" + else + echo "TestNet unchanged, skipping security group update" + fi