1+ #! /bin/bash
2+ set -o errexit
3+ set -o nounset
4+ set -o pipefail
5+
6+ set -x
7+
8+ # For disconnected or otherwise unreachable environments, we want to
9+ # have steps use an HTTP(S) proxy to reach the API server. This proxy
10+ # configuration file should export HTTP_PROXY, HTTPS_PROXY, and NO_PROXY
11+ # environment variables, as well as their lowercase equivalents (note
12+ # that libcurl doesn't recognize the uppercase variables).
13+ if test -f " ${SHARED_DIR} /proxy-conf.sh"
14+ then
15+ # shellcheck disable=SC1091
16+ source " ${SHARED_DIR} /proxy-conf.sh"
17+ fi
18+
19+ # logger function prints standard logs
20+ logger () {
21+ local level=" $1 "
22+ local message=" $2 "
23+ local timestamp
24+
25+ # Generate a timestamp for the log entry
26+ timestamp=$( date +" %Y-%m-%d %H:%M:%S" )
27+
28+ # Print the log message with the level and timestamp
29+ echo " [$timestamp ] [$level ] $message "
30+ }
31+
32+ switch_aws_credentials () {
33+ local mode=" $1 "
34+ if [[ " $mode " == " shared" ]]; then
35+ export AWS_SHARED_CREDENTIALS_FILE=" ${CLUSTER_PROFILE_DIR} /.awscred_shared_account"
36+ logger " INFO" " Using shared AWS account(B)."
37+ else
38+ export AWS_SHARED_CREDENTIALS_FILE=" ${CLUSTER_PROFILE_DIR} /.awscred"
39+ logger " INFO" " Using default AWS account(A)."
40+ fi
41+ }
42+
43+ REGION=${REGION:- $LEASED_RESOURCE }
44+ switch_aws_credentials default
45+ AWS_ACCOUNT_A_ARN=$( aws sts get-caller-identity | jq -r ' .Arn' )
46+ AWS_ACCOUNT_A_ID=$( echo " $AWS_ACCOUNT_A_ARN " | awk -F " :" ' {print $5}' ) || return 1
47+ export AWS_ACCOUNT_A_ID
48+ AWS_ACCOUNT_A_VPC_ID=$( oc get infrastructure cluster -o jsonpath=' {.status.platformStatus.aws.vpc.id}' )
49+ AWS_ACCOUNT_A_VPC_CIDR=$( aws ec2 describe-vpcs \
50+ --vpc-ids " $AWS_ACCOUNT_A_VPC_ID " \
51+ --output json | jq -r ' .Vpcs[0].CidrBlock' )
52+ switch_aws_credentials shared
53+ AWS_ACCOUNT_B_ARN=$( aws sts get-caller-identity | jq -r ' .Arn' )
54+ AWS_ACCOUNT_B_ID=$( echo " $AWS_ACCOUNT_B_ARN " | awk -F " :" ' {print $5}' ) || return 1
55+ export AWS_ACCOUNT_B_ID
56+ CLUSTER_NAME=" $( jq -r .clusterName " ${SHARED_DIR} /metadata.json" ) "
57+
58+
59+ # Get the VPC ID from the shared account
60+ AWS_ACCOUNT_B_VPC_ID=$( cat " ${SHARED_DIR} /vpc_id" )
61+
62+ # Creating a region-wide EFS filesystem in Account B
63+ ACROSS_ACCOUNT_FS_ID=$( aws efs create-file-system --creation-token ci-cross-account-token \
64+ --region " ${AWS_REGION} " \
65+ --encrypted | jq -r ' .FileSystemId' )
66+ logger " INFO" " Created efs volume FileSystemId:$ACROSS_ACCOUNT_FS_ID "
67+
68+ # Configure a region-wide Mount Target for EFS (this will create a mount point in each subnet of your VPC by default)
69+ aws efs create-mount-target --file-system-id " $ACROSS_ACCOUNT_FS_ID " \
70+ --subnet-id " ${AWS_ACCOUNT_B_VPC_ID} " \
71+ --security-groups " ${SECURITY_GROUP_ID} " \
72+ --region " ${AWS_REGION} " \
73+ --tag-specifications " ResourceType=mount-target,Tags=[{Key=Name,Value=ci-cross-account-mount-target}]" \
74+ --client-token ci-cross-account-token
75+ AWS_ACCOUNT_B_PRIVATE_SUBNET_IDS=$( tr -d " [],'" < " ${SHARED_DIR} /private_subnet_ids" )
76+ for SUBNET in $AWS_ACCOUNT_B_PRIVATE_SUBNET_IDS ; do \
77+ MOUNT_TARGET=$( aws efs create-mount-target --file-system-id " $ACROSS_ACCOUNT_FS_ID " \
78+ --subnet-id " $SUBNET " \
79+ --region " $AWS_REGION " \
80+ | jq -r ' .MountTargetId' ) ; \
81+ logger " INFO" " Created $MOUNT_TARGET for $SUBNET " ; \
82+ done
83+
84+ # Prepare the security groups on Account B to allow NFS traffic to EFS
85+ AWS_ACCOUNT_B_VPC_CIDR=$( aws ec2 describe-vpcs \
86+ --vpc-ids " $AWS_ACCOUNT_B_VPC_ID " \
87+ --output json | jq -r ' .Vpcs[0].CidrBlock' )
88+ SECURITY_GROUP_ID=$( aws ec2 describe-security-groups --filters Name=vpc-id,Values=" $AWS_ACCOUNT_B_VPC_ID " | jq -r ' .SecurityGroups[].GroupId' )
89+ aws ec2 authorize-security-group-ingress \
90+ --group-id " $SECURITY_GROUP_ID " \
91+ --protocol tcp \
92+ --port 2049 \
93+ --cidr " $AWS_ACCOUNT_A_VPC_CIDR " | jq .
94+
95+ # Create VPC peering between the Red Hat OpenShift cluster VPC in AWS account A and the AWS EFS VPC in AWS account B
96+ PEER_REQUEST_ID=$( aws ec2 create-vpc-peering-connection \
97+ --vpc-id " ${ACCOUNT_B_VPC_ID} " \
98+ --peer-vpc-id " ${ACCOUNT_A_VPC_ID} " \
99+ --peer-owner-id " ${AWS_ACCOUNT_A_ID} " \
100+ --output json | jq -r ' .VpcPeeringConnection.VpcPeeringConnectionId' )
101+
102+ switch_aws_credentials default
103+ aws ec2 accept-vpc-peering-connection --vpc-peering-connection-id " ${PEER_REQUEST_ID} "
104+ SUBNETS=$( aws ec2 describe-subnets \
105+ --filters Name=vpc-id,Values=" ${AWS_ACCOUNT_A_VPC_ID} " \
106+ | jq -r ' .Subnets[].SubnetId' )
107+
108+ for subnet in $SUBNETS ; do
109+ # Get route table associated with this subnet (specific or main)
110+ RTB=$( aws ec2 describe-route-tables \
111+ --filters Name=association.subnet-id,Values=" $subnet " \
112+ | jq -r ' .RouteTables[0].RouteTableId' )
113+
114+ if [ " $RTB " == " null" ]; then
115+ # No subnet-specific route table, fall back to main route table
116+ RTB=$( aws ec2 describe-route-tables \
117+ --filters Name=vpc-id,Values=" ${AWS_ACCOUNT_A_VPC_ID} " Name=association.main,Values=true \
118+ | jq -r ' .RouteTables[0].RouteTableId' )
119+ fi
120+
121+ # Check if the route table has a route to an Internet Gateway
122+ HAS_IGW=$( aws ec2 describe-route-tables --route-table-ids " $RTB " \
123+ | jq -e ' .RouteTables[0].Routes[] | select(.GatewayId | startswith("igw-"))' > /dev/null && echo " yes" || echo " no" )
124+
125+ # If no IGW, it's private
126+ if [ " $HAS_IGW " == " no" ]; then
127+ ROUTE_TABLE_ID=$( aws ec2 describe-route-tables --filters " Name=association.subnet-id,Values=${SUBNET} " --query ' RouteTables[*].RouteTableId' | jq -r ' .[0]' )
128+ aws ec2 create-route --route-table-id " ${ROUTE_TABLE_ID} " --destination-cidr-block " ${AWS_ACCOUNT_B_VPC_CIDR} " --vpc-peering-connection-id " ${PEER_REQUEST_ID} "
129+ logger " INFO" " Created route for $SUBNET to peering-connection in account A"
130+ fi
131+ done
132+
133+ switch_aws_credentials shared
134+ for SUBNET in $AWS_ACCOUNT_B_PRIVATE_SUBNET_IDS ; do
135+ ROUTE_TABLE_ID=$( aws ec2 describe-route-tables --filters " Name=association.subnet-id,Values=${SUBNET} " --query ' RouteTables[*].RouteTableId' | jq -r ' .[0]' )
136+ aws ec2 create-route --route-table-id " ${ROUTE_TABLE_ID} " --destination-cidr-block " ${AWS_ACCOUNT_A_VPC_CIDR} " --vpc-peering-connection-id " ${PEER_REQUEST_ID} "
137+ logger " INFO" " Created route for $SUBNET to peering-connection in account B"
138+ done
139+
140+ sleep 6h
141+
142+ cat << EOF > "${SHARED_DIR} "/EfsPolicyInAccountB.json
143+ {
144+ "Version": "2012-10-17",
145+ "Statement": [
146+ {
147+ "Sid": "VisualEditor0",
148+ "Effect": "Allow",
149+ "Action": [
150+ "ec2:DescribeNetworkInterfaces",
151+ "ec2:DescribeSubnets"
152+ ],
153+ "Resource": "*"
154+ },
155+ {
156+ "Sid": "VisualEditor1",
157+ "Effect": "Allow",
158+ "Action": [
159+ "elasticfilesystem:DescribeMountTargets",
160+ "elasticfilesystem:DeleteAccessPoint",
161+ "elasticfilesystem:ClientMount",
162+ "elasticfilesystem:DescribeAccessPoints",
163+ "elasticfilesystem:ClientWrite",
164+ "elasticfilesystem:ClientRootAccess",
165+ "elasticfilesystem:DescribeFileSystems",
166+ "elasticfilesystem:CreateAccessPoint"
167+ ],
168+ "Resource": [
169+ "arn:aws:elasticfilesystem:*:${AWS_ACCOUNT_B_ID} :access-point/*",
170+ "arn:aws:elasticfilesystem:*:${AWS_ACCOUNT_B_ID} :file-system/*"
171+ ]
172+ }
173+ ]
174+ }
175+ EOF
176+
177+ cat << EOF > "${SHARED_DIR} "/TrustPolicyInAccountB.json
178+ {
179+ "Version": "2012-10-17",
180+ "Statement": [
181+ {
182+ "Effect": "Allow",
183+ "Principal": {
184+ "AWS": "arn:aws:iam::${AWS_ACCOUNT_A_ID} :root"
185+ },
186+ "Action": "sts:AssumeRole",
187+ "Condition": {}
188+ }
189+ ]
190+ }
191+ EOF
192+
193+ ACCOUNT_B_POLICY=$( aws iam create-policy --policy-name " ${CLUSTER_NAME} -efs-csi" \
194+ --policy-document file://" ${SHARED_DIR} " /efs-policy.json \
195+ --query ' Policy.Arn' --output text) || \
196+ logger " INFO" " Created efs policy $ACCOUNT_B_POLICY in account B"
197+
198+ # Create Role for the EFS CSI Driver Operator
199+ ACCOUNT_B_ROLE_ARN=$( aws iam create-role \
200+ --role-name " ${CLUSTER_NAME} -aws-efs-csi-operator" \
201+ --assume-role-policy-document file://" ${SHARED_DIR} " /TrustPolicy.json \
202+ --query " Role.Arn" --output text)
203+ logger " INFO" " Created efs csi driver role $ACCOUNT_B_ROLE_ARN in account B"
204+
205+ aws iam attach-role-policy \
206+ --role-name " ${CLUSTER_NAME} -aws-efs-csi-operator" \
207+ --policy-arn " ${ACCOUNT_B_POLICY} "
208+ logger " INFO" " Attach the Policies to the Role in account B"
0 commit comments