<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>interface endpoint &#8211; Blog of Kliment Andreev &#8211; A place so I won&#039;t forget things</title>
	<atom:link href="https://blog.andreev.it/tag/interface-endpoint/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.andreev.it</link>
	<description></description>
	<lastBuildDate>Tue, 18 Jun 2024 21:19:14 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>AWS: Access an S3 bucket using gateway and interface endpoints (PrivateLink)</title>
		<link>https://blog.andreev.it/2024/06/aws-access-an-s3-bucket-using-gateway-and-interface-endpoints-privatelink/</link>
					<comments>https://blog.andreev.it/2024/06/aws-access-an-s3-bucket-using-gateway-and-interface-endpoints-privatelink/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Tue, 18 Jun 2024 21:19:14 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[gateway endpoint]]></category>
		<category><![CDATA[interface endpoint]]></category>
		<category><![CDATA[PrivateLink]]></category>
		<category><![CDATA[S3]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=9930</guid>

					<description><![CDATA[If you have a use case where you need to transfer a lot of&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>If you have a use case where you need to transfer a lot of data back-and-forth between various resources and an S3 bucket, you will definitely benefit if you use gateway or interface endpoints for S3. The monthly bill for regional and zonal transfer will be much less and on top of that it&#8217;s much more secure.<br />
From the AWS documentation, &#8220;<em>Amazon S3 supports both gateway endpoints and interface endpoints. With a gateway endpoint, you can access Amazon S3 from your VPC, without requiring an internet gateway or NAT device for your VPC, and with no additional cost. However, gateway endpoints do not allow access from on-premises networks, from peered VPCs in other AWS Regions, or through a transit gateway. For those scenarios, you must use an interface endpoint, which is available for an additional cost.</em>&#8221;<br />
Keep this table handy as it explains the differences between gateway and interface S3 endpoint.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-01.png"><img fetchpriority="high" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-01-1024x325.png" alt="" width="1024" height="325" class="aligncenter size-large wp-image-9934" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-01-1024x325.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-01-300x95.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-01-768x243.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-01-1170x371.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-01-585x185.png 585w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-01.png 1404w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Now that we know the use case and the differences, we&#8217;ll see three different scenarios. For this, you&#8217;ll need access to two different accounts and two different regions.</p>
<ul>
<li>An S3 bucket in account A and an ec2 instance in the same account A and the same region A</li>
<li>An S3 bucket in account A and an ec2 instance in a different account B, but the same region A</li>
<li>An S3 bucket in account A and an ec2 instance in a different account B and a different region B</li>
</ul>
<p>The diagram looks like this.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-03.png"><img decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-03.png" alt="" width="572" height="451" class="aligncenter size-full wp-image-9937" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-03.png 572w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-03-300x237.png 300w" sizes="(max-width: 572px) 100vw, 572px" /></a></p>
<h1>An S3 bucket in account A, ec2 in account A, both in the same region</h1>
<p>Let&#8217;s create an S3 bucket first. We&#8217;ll use AWS CLI with different profiles.</p>
<pre class="brush: bash; title: ; notranslate">
BUCKET_NAME=&quot;myuniquenameforthebucket&quot;
REGION_A=&quot;us-east-2&quot;
aws s3api create-bucket \
    --bucket $BUCKET_NAME \
    --region $REGION_A \
    --create-bucket-configuration LocationConstraint=$REGION_A
</pre>
<p>Let&#8217;s copy a file there.</p>
<pre class="brush: bash; title: ; notranslate">
dd if=/dev/zero of=somefile bs=1024 count=1
aws s3 cp somefile &quot;s3://$BUCKET_NAME/somefile&quot;
</pre>
<p>Using the same profile, create a VPC with one public subnet with the same CIDR as the VPC.</p>
<pre class="brush: bash; title: ; notranslate">
CIDR_A=&quot;192.168.10.0/24&quot;
VPCA_ID=$(aws ec2 create-vpc \
    --cidr-block $CIDR_A \
    --region $REGION_A \
    --tag-specification &#039;ResourceType=vpc,Tags=&#x5B;{Key=Name,Value=VPC-A}]&#039; \
    --output text --query &#039;Vpc.VpcId&#039;)
echo $VPCA_ID
</pre>
<p>Enable DNS and hostnames resolution, needed for SSM. Two separate lines needed.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 modify-vpc-attribute --enable-dns-hostnames &quot;{\&quot;Value\&quot;:true}&quot;  --vpc-id $VPCA_ID
aws ec2 modify-vpc-attribute --enable-dns-support &quot;{\&quot;Value\&quot;:true}&quot;  --vpc-id $VPCA_ID
</pre>
<p>Create a default network ACL.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 create-network-acl --vpc-id $VPCA_ID
</pre>
<p>Create an Internet Gateway and attach it to the VPC.</p>
<pre class="brush: bash; title: ; notranslate">
IGWA_ID=$(aws ec2 create-internet-gateway \
    --region $REGION_A \
    --tag-specifications &#039;ResourceType=internet-gateway,Tags=&#x5B;{Key=Name,Value=igw-A}]&#039; \
    --output text --query &#039;InternetGateway.InternetGatewayId&#039;)
echo $IGWA_ID
aws ec2 attach-internet-gateway \
    --internet-gateway-id $IGWA_ID \
    --vpc-id $VPCA_ID
</pre>
<p>Let&#8217;s create a subnet.</p>
<pre class="brush: bash; title: ; notranslate">
SUBA_ID=$(aws ec2 create-subnet \
    --vpc-id $VPCA_ID \
    --cidr-block 192.168.10.0/24 \
    --region $REGION_A \
    --tag-specifications &#039;ResourceType=subnet,Tags=&#x5B;{Key=Name,Value=subPubA}]&#039; \
    --output text --query &#039;Subnet.SubnetId&#039;)
echo $SUBA_ID
</pre>
<p>Create a route table.</p>
<pre class="brush: bash; title: ; notranslate">
RTA_ID=$(aws ec2 create-route-table \
     --vpc-id $VPCA_ID \
     --output text --query &#039;RouteTable.RouteTableId&#039;)
echo $RTA_ID
aws ec2 create-route --route-table-id $RTA_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $IGWA_ID
</pre>
<p>Associate this route table to the public subnet.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 associate-route-table --route-table-id $RTA_ID --subnet-id $SUBA_ID
</pre>
<p>Now, we have to create an EC2 instance where we can test the S3 access. We won&#8217;t be using keys and SSH to access, we&#8217;ll use SSM. For that we need the instance to have access to the Internet which means we need a security group that allows access to Internet.</p>
<pre class="brush: bash; title: ; notranslate">
SGA_ID=$(aws ec2 create-security-group \
    --group-name sgAllowICMPandOutboundAccess --description &quot;Allows ICMP and outbound access&quot; \
    --vpc-id $VPCA_ID \
    --output text --query &#039;GroupId&#039;)
echo $SGA_ID
</pre>
<p>Allow unrestricted egress. By default this rule is there, so don&#8217;t execute the code below.</p>
<pre class="brush: bash; title: ; notranslate">
#aws ec2 authorize-security-group-egress \
    --group-id $SGA_ID \
    --protocol all \
    --cidr &quot;0.0.0.0/0&quot; 
</pre>
<p>Do this one, so we can ping instances from each other when we do VPC peering.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 authorize-security-group-ingress \
  --group-id $SGA_ID \
  --ip-permissions IpProtocol=icmp,FromPort=-1,ToPort=-1,IpRanges=&quot;&#x5B;{CidrIp=192.168.10.0/24},{CidrIp=192.168.20.0/24},{CidrIp=192.168.30.0/24}]&quot;
</pre>
<p>Let&#8217;s create a role so we can access the EC2 over SSM. First we need a policy that the role can assume.</p>
<pre class="brush: bash; title: ; notranslate">
cat &lt;&lt; &#039;EOF&#039; &gt;&gt; trust-policy.json
{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: &#x5B;
    {
      &quot;Sid&quot;: &quot;&quot;,
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Principal&quot;: {
        &quot;Service&quot;: &quot;ec2.amazonaws.com&quot;
      },
      &quot;Action&quot;: &quot;sts:AssumeRole&quot;
    }
  ]
}
EOF
</pre>
<p>Then create the role and attach the AWS Managed policy for SSM (<strong>AmazonSSMManagerInstanceCore</strong>).</p>
<pre class="brush: bash; title: ; notranslate">
aws iam create-role --role-name rolSSM --assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore --role-name rolSSM
</pre>
<p>Create instance profile.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam create-instance-profile --instance-profile-name ipEC2
</pre>
<p>Add the role.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam add-role-to-instance-profile --role-name rolSSM --instance-profile-name ipEC2
</pre>
<p>Create an instance.</p>
<pre class="brush: bash; title: ; notranslate">
AMI_ID=&quot;ami-033fabdd332044f06&quot;
INS_TYPE=&quot;t3.micro&quot;
aws ec2 run-instances \
    --image-id $AMI_ID \
    --instance-type $INS_TYPE \
    --security-group-ids $SGA_ID \
    --subnet-id $SUBA_ID \
    --iam-instance-profile Name=&quot;ipEC2&quot; \
    --associate-public-ip-address  \
    --tag-specifications &#039;ResourceType=instance,Tags=&#x5B;{Key=Name,Value=ec2-A}]&#039;
</pre>
<p>We&#8217;ll need an IAM user that we&#8217;ll use to access the S3 bucket.</p>
<pre class="brush: bash; title: ; notranslate">
IAM_USER=&quot;usrMyBucket&quot;
aws iam create-user --user-name $IAM_USER
</pre>
<p>We&#8217;ll also need a policy that gives access to that specific bucket only.</p>
<pre class="brush: bash; title: ; notranslate">
cat &gt;&gt; bucket-policy.json &lt;&lt; EOF
{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: &#x5B;
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &quot;s3:*&quot;,
            &quot;Resource&quot;: &#x5B;
                &quot;arn:aws:s3:::$BUCKET_NAME&quot;,
                &quot;arn:aws:s3:::$BUCKET_NAME/*&quot;
            ]
        },
        {
            &quot;Effect&quot;: &quot;Deny&quot;,
            &quot;NotAction&quot;: &quot;s3:*&quot;,
            &quot;NotResource&quot;: &#x5B;
                &quot;arn:aws:s3:::$BUCKET_NAME&quot;,
                &quot;arn:aws:s3:::$BUCKET_NAME/*&quot;
            ]
        }
    ]
}
EOF
</pre>
<p>Create the policy.</p>
<pre class="brush: bash; title: ; notranslate">
POL_ARN=$(aws iam create-policy \
    --policy-name polMyBucketAccess \
    --policy-document file://bucket-policy.json \
    --output text --query &#039;Policy.Arn&#039;)
echo $POL_ARN
</pre>
<p>Attach the policy to the user.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam attach-user-policy --policy-arn $POL_ARN --user-name $IAM_USER
</pre>
<p>Create access keys.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam create-access-key --user-name $IAM_USER --output table --query &#x5B;&#039;AccessKey.AccessKeyId&#039;,&#039;AccessKey.SecretAccessKey&#039;]
</pre>
<p>Copy these two values.<br />
Now, go to AWS console, select the EC2 instance and click <strong>Connect</strong>. Use the 2nd tab <strong>Session Manager</strong> and click <strong>Connect</strong>.<br />
Type <em>aws configure</em> and paste the copied values for the IAM user you just created.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-04.png"><img decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-04.png" alt="" width="940" height="183" class="aligncenter size-full wp-image-9967" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-04.png 940w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-04-300x58.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-04-768x150.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-04-585x114.png 585w" sizes="(max-width: 940px) 100vw, 940px" /></a><br />
If you try to do <em>aws s3 ls</em>, you&#8217;ll get access denied, but if you try <em>aws s3 ls s3://myuniquenameforthebucket</em>, you&#8217;ll see that it works.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-05.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-05-1024x159.png" alt="" width="1024" height="159" class="aligncenter size-large wp-image-9968" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-05-1024x159.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-05-300x47.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-05-768x119.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-05-585x91.png 585w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-05.png 1158w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Now, this type of access goes over the Internet and you&#8217;ll be charged for the data transfers. In order to avoid that, we&#8217;ll create an S3 gateway endpoint.<br />
Go to <strong>VPC </strong>menu and from the left side choose <strong>Endpoints</strong>.<br />
Create the endpoint, name the endpoint, choose <strong>AWS services</strong>, filter by <strong>Type = Gateway</strong> and select the S3 service name. Then select the VPC where you created your EC2 and the route table that&#8217;s associated with the subnet where EC2 resides. For policy choose Full Access. This means, the S3 gateway endpoint will have access to all S3 buckets. Finally, click to create the endpoint.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-06.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-1024x573.png" alt="" width="1024" height="573" class="aligncenter size-large wp-image-9969" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-1024x573.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-300x168.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-768x430.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-1536x860.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-2048x1146.png 2048w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-1920x1075.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-1170x655.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-585x327.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
If you check your route table, you&#8217;ll see that there is another entry there. Wait 10 seconds or less if you don&#8217;t see it.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-07.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-07-1024x650.png" alt="" width="1024" height="650" class="aligncenter size-large wp-image-9970" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-07-1024x650.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-07-300x190.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-07-768x488.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-07-1170x743.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-07-585x371.png 585w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-07.png 1315w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
If you go back to the EC2 instance and do the <em>aws s3 ls s3://myuniquenameforthebucket</em> command again, you&#8217;ll see that nothing changed. So, how do you know that we did the right thing? It&#8217;s simple. Click on the endpoint that you&#8217;ve created, click on the <strong>Policy </strong>tab and then click the <strong>Edit Policy</strong> button.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-08.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-1024x561.png" alt="" width="1024" height="561" class="aligncenter size-large wp-image-9971" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-1024x561.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-300x164.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-768x421.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-1536x842.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-2048x1122.png 2048w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-1920x1052.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-1170x641.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-585x321.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Change line 5 from <em>Allow </em>to <em>Deny </em>and click <strong>Save</strong>.<br />
Go back to the EC2 instance and do the <em>aws s3 ls s3://myuniquenameforthebucket</em> again. You&#8217;ll get access denied. There you go&#8230;Traffic goes over the endpoint. Revert the changes back from <em>Deny </em>to <em>Allow</em>. Let&#8217;s move to the 2nd use case.</p>
<h1>An S3 bucket in account A, an ec2 instance in a different account B, but both in the same region A</h1>
<p><strong>NOTE:Make sure you switch your AWS CLI to a different profile, because we&#8217;ll be building the EC2 instance in another account.</strong> The S3 bucket stays the same.<br />
Let&#8217;s build the 2nd VPC and EC2.</p>
<pre class="brush: bash; title: ; notranslate">
REGION_A=&quot;us-east-2&quot;
CIDR_B=&quot;192.168.20.0/24&quot;
VPCB_ID=$(aws ec2 create-vpc \
    --cidr-block $CIDR_B \
    --region $REGION_A \
    --tag-specification &#039;ResourceType=vpc,Tags=&#x5B;{Key=Name,Value=VPC-B}]&#039; \
    --output text --query &#039;Vpc.VpcId&#039;)
echo $VPCB_ID
</pre>
<p>Enable DNS and hostnames resolution, needed for SSM. Two separate lines needed.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 modify-vpc-attribute --enable-dns-hostnames &quot;{\&quot;Value\&quot;:true}&quot;  --vpc-id $VPCB_ID
aws ec2 modify-vpc-attribute --enable-dns-support &quot;{\&quot;Value\&quot;:true}&quot;  --vpc-id $VPCB_ID
</pre>
<p>Create a default network ACL.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 create-network-acl --vpc-id $VPCB_ID
</pre>
<p>Create an Internet Gateway and attach it to the VPC.</p>
<pre class="brush: bash; title: ; notranslate">
IGWB_ID=$(aws ec2 create-internet-gateway \
    --region $REGION_A \
    --tag-specifications &#039;ResourceType=internet-gateway,Tags=&#x5B;{Key=Name,Value=igw-B}]&#039; \
    --output text --query &#039;InternetGateway.InternetGatewayId&#039;)
echo $IGWB_ID
aws ec2 attach-internet-gateway \
    --internet-gateway-id $IGWB_ID \
    --vpc-id $VPCB_ID
</pre>
<p>Let&#8217;s create a subnet.</p>
<pre class="brush: bash; title: ; notranslate">
SUBB_ID=$(aws ec2 create-subnet \
    --vpc-id $VPCB_ID \
    --cidr-block 192.168.20.0/24 \
    --region $REGION_A \
    --tag-specifications &#039;ResourceType=subnet,Tags=&#x5B;{Key=Name,Value=subPubB}]&#039; \
    --output text --query &#039;Subnet.SubnetId&#039;)
echo $SUBB_ID
</pre>
<p>Create a route table.</p>
<pre class="brush: bash; title: ; notranslate">
RTB_ID=$(aws ec2 create-route-table \
     --vpc-id $VPCB_ID \
     --output text --query &#039;RouteTable.RouteTableId&#039;)
echo $RTB_ID
aws ec2 create-route --route-table-id $RTB_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $IGWB_ID
</pre>
<p>Associate this route table to the public subnet.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 associate-route-table --route-table-id $RTB_ID --subnet-id $SUBB_ID
</pre>
<p>Now, we have to create an EC2 instance where we can test the S3 access. We won&#8217;t be using keys and SSH to access, we&#8217;ll use SSM. For that we need the instance to have access to the Internet which means we need a security group that allows access to Internet.</p>
<pre class="brush: bash; title: ; notranslate">
SGB_ID=$(aws ec2 create-security-group \
    --group-name sgAllowICMPandOutboundAccess --description &quot;Allows ICMP and outbound access&quot; \
    --vpc-id $VPCB_ID \
    --output text --query &#039;GroupId&#039;)
echo $SGB_ID
</pre>
<p>Allow unrestricted egress. By default this rule is there, so don&#8217;t execute the code below.</p>
<pre class="brush: bash; title: ; notranslate">
#aws ec2 authorize-security-group-egress \
    --group-id $SGB_ID \
    --protocol all \
    --cidr &quot;0.0.0.0/0&quot; 
</pre>
<p>Do this one, so we can ping instances from each other when we do VPC peering.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 authorize-security-group-ingress \
  --group-id $SGB_ID \
  --ip-permissions IpProtocol=icmp,FromPort=-1,ToPort=-1,IpRanges=&quot;&#x5B;{CidrIp=192.168.10.0/24},{CidrIp=192.168.20.0/24},{CidrIp=192.168.30.0/24}]&quot;
</pre>
<p>We can reuse the same policy to create a role that will allow us to use SSM.</p>
<pre class="brush: bash; title: ; notranslate">
cat trust-policy.json
</pre>
<p>Then create the role and attach the AWS Managed policy for SSM (<strong>AmazonSSMManagerInstanceCore</strong>).</p>
<pre class="brush: bash; title: ; notranslate">
aws iam create-role --role-name rolSSM --assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore --role-name rolSSM
</pre>
<p>Create instance profile. We&#8217;ll use the same names, because they are different accounts.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam create-instance-profile --instance-profile-name ipEC2
</pre>
<p>Add the role.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam add-role-to-instance-profile --role-name rolSSM --instance-profile-name ipEC2
</pre>
<p>Create an instance. The AMI_ID is the same because it&#8217;s the same region.</p>
<pre class="brush: bash; title: ; notranslate">
AMI_ID=&quot;ami-033fabdd332044f06&quot;
INS_TYPE=&quot;t3.micro&quot;
aws ec2 run-instances \
    --image-id $AMI_ID \
    --instance-type $INS_TYPE \
    --security-group-ids $SGB_ID \
    --subnet-id $SUBB_ID \
    --iam-instance-profile Name=&quot;ipEC2&quot; \
    --associate-public-ip-address  \
    --tag-specifications &#039;ResourceType=instance,Tags=&#x5B;{Key=Name,Value=ec2-B}]&#039;
</pre>
<p>Wait for a couple of minutes and then connect from SSM, same as you did with the first instance. Configure AWS again using the same set of credentials that you did before and then try to list the content of the bucket. You should be able to see the file.</p>
<pre class="brush: bash; title: ; notranslate">
BUCKET_NAME=&quot;myuniquenameforthebucket&quot;
aws s3 ls s3://$BUCKET_NAME
</pre>
<p>Again, the traffic goes over the Internet. So we need another gateway S3 endpoint. You create the gateway S3 endpoint where the client is located, not the destination.<br />
Let&#8217;s create another S3 gateway endpoint as described above.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-09.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-1024x505.png" alt="" width="1024" height="505" class="aligncenter size-large wp-image-9977" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-1024x505.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-300x148.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-768x379.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-1536x758.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-1920x948.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-1170x577.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-585x289.png 585w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09.png 2020w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Check the route table and you&#8217;ll see the route created. At this point, you are accessing the bucket over the endpoint. Do the same test. Change the policy to Deny for the endpoint and you&#8217;ll get access denied.</p>
<h1>An S3 bucket in account A, an ec2 instance in a different account B and a different region B</h1>
<p>For the 3rd use case, we&#8217;ll use an interface endpoint (PrivateLink), not a gateway endpoint. This is what AWS documentation has to say. &#8220;<em>With AWS PrivateLink for Amazon S3, you can provision interface VPC endpoints (interface endpoints) in your virtual private cloud (VPC). These endpoints are directly accessible from applications that are on premises over VPN and AWS Direct Connect, or in a different AWS Region over VPC peering. Interface endpoints are represented by one or more elastic network interfaces (ENIs) that are assigned private IP addresses from subnets in your VPC. Requests to Amazon S3 over interface endpoints stay on the Amazon network. You can also access interface endpoints in your VPC from on-premises applications through AWS Direct Connect or AWS Virtual Private Network (AWS VPN).</em>&#8221;<br />
Let&#8217;s create the resources. VPC and EC2. Change the AWS CLI profile so it&#8217;s using the same account B and a new region B.</p>
<pre class="brush: bash; title: ; notranslate">
REGION_B=&quot;us-east-1&quot;
CIDR_C=&quot;192.168.30.0/24&quot;
VPCC_ID=$(aws ec2 create-vpc \
    --cidr-block $CIDR_C \
    --region $REGION_B \
    --tag-specification &#039;ResourceType=vpc,Tags=&#x5B;{Key=Name,Value=VPC-C}]&#039; \
    --output text --query &#039;Vpc.VpcId&#039;)
echo $VPCC_ID
</pre>
<p>Enable DNS and hostnames resolution, needed for SSM. Two separate lines needed.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 modify-vpc-attribute --enable-dns-hostnames &quot;{\&quot;Value\&quot;:true}&quot;  --vpc-id $VPCC_ID
aws ec2 modify-vpc-attribute --enable-dns-support &quot;{\&quot;Value\&quot;:true}&quot;  --vpc-id $VPCC_ID
</pre>
<p>Create a default network ACL.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 create-network-acl --vpc-id $VPCC_ID
</pre>
<p>Create an Internet Gateway and attach it to the VPC.</p>
<pre class="brush: bash; title: ; notranslate">
IGWC_ID=$(aws ec2 create-internet-gateway \
    --region $REGION_B \
    --tag-specifications &#039;ResourceType=internet-gateway,Tags=&#x5B;{Key=Name,Value=igw-C}]&#039; \
    --output text --query &#039;InternetGateway.InternetGatewayId&#039;)
echo $IGWC_ID
aws ec2 attach-internet-gateway \
    --internet-gateway-id $IGWC_ID \
    --vpc-id $VPCC_ID
</pre>
<p>Let&#8217;s create a subnet.</p>
<pre class="brush: bash; title: ; notranslate">
SUBC_ID=$(aws ec2 create-subnet \
    --vpc-id $VPCC_ID \
    --cidr-block 192.168.30.0/24 \
    --region $REGION_B \
    --tag-specifications &#039;ResourceType=subnet,Tags=&#x5B;{Key=Name,Value=subPubC}]&#039; \
    --output text --query &#039;Subnet.SubnetId&#039;)
echo $SUBC_ID
</pre>
<p>Create a route table.</p>
<pre class="brush: bash; title: ; notranslate">
RTC_ID=$(aws ec2 create-route-table \
     --vpc-id $VPCC_ID \
     --output text --query &#039;RouteTable.RouteTableId&#039;)
echo $RTC_ID
aws ec2 create-route --route-table-id $RTC_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $IGWC_ID
</pre>
<p>Associate this route table to the public subnet.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 associate-route-table --route-table-id $RTC_ID --subnet-id $SUBC_ID
</pre>
<p>Now, we have to create an EC2 instance where we can test the S3 access. We won&#8217;t be using keys and SSH to access, we&#8217;ll use SSM. For that we need the instance to have access to the Internet which means we need a security group that allows access to Internet.</p>
<pre class="brush: bash; title: ; notranslate">
SGC_ID=$(aws ec2 create-security-group \
    --group-name sgAllowICMPandOutboundAccess --description &quot;Allows ICMP and outbound access&quot; \
    --vpc-id $VPCC_ID \
    --output text --query &#039;GroupId&#039;)
echo $SGC_ID
</pre>
<p>Allow unrestricted egress. By default this rule is there, so don&#8217;t execute the code below.</p>
<pre class="brush: bash; title: ; notranslate">
#aws ec2 authorize-security-group-egress \
    --group-id $SGB_ID \
    --protocol all \
    --cidr &quot;0.0.0.0/0&quot; 
</pre>
<p>Do this one, so we can ping instances from each other when we do VPC peering.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 authorize-security-group-ingress \
  --group-id $SGC_ID \
  --ip-permissions IpProtocol=icmp,FromPort=-1,ToPort=-1,IpRanges=&quot;&#x5B;{CidrIp=192.168.10.0/24},{CidrIp=192.168.20.0/24},{CidrIp=192.168.30.0/24}]&quot;
</pre>
<p>We don&#8217;t have to create any roles. We&#8217;ll use the existing ones that we built for the 2nd use case.<br />
Create the EC2. The AMI ID will change this time.</p>
<pre class="brush: bash; title: ; notranslate">
AMI_ID=&quot;ami-08a0d1e16fc3f61ea&quot;
INS_TYPE=&quot;t3.micro&quot;
aws ec2 run-instances \
    --image-id $AMI_ID \
    --instance-type $INS_TYPE \
    --security-group-ids $SGC_ID \
    --subnet-id $SUBC_ID \
    --iam-instance-profile Name=&quot;ipEC2&quot; \
    --associate-public-ip-address  \
    --tag-specifications &#039;ResourceType=instance,Tags=&#x5B;{Key=Name,Value=ec2-C}]&#039;
</pre>
<p>Same thing as we did before, connect from SSM, configure AWS CLI using the previous credentials and try to list the bucket.</p>
<pre class="brush: bash; title: ; notranslate">
BUCKET_NAME=&quot;myuniquenameforthebucket&quot;
aws s3 ls s3://$BUCKET_NAME
</pre>
<p>You should see the file there. But, this access goes over Internet. Now, we have to create an interface endpoint in the destination VPC which is VPC-A. When we create an interface endpoint a private IP will be generated for this interface endpoint. In order this private IP to access the bucket, we need some sort of connection between regions, e.g. VPC peering or Transit Gateway. Let&#8217;s do the peering between account A (VPC-A) and account B (VPC-C). Enter the 12 digit account number for account A where the S3 bucket resides.</p>
<pre class="brush: bash; title: ; notranslate">
echo $VPCA_ID
echo $VPCC_ID
echo $REGION_A
ACCOUNT_A=&quot;123456789012&quot;
echo $ACCOUNT_A
</pre>
<p>Make sure all of the above returns some value. Do the peering.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 create-vpc-peering-connection \
  --vpc-id $VPCC_ID --peer-vpc-id $VPCA_ID \
  --peer-owner-id $ACCOUNT_A --peer-region $REGION_A
</pre>
<p>Log to the account A and under <strong>VPC | Peering</strong> menu on the left, select the peering and accept it. But this is not enough. You need to tell the VPC to use the routes over the peering connection. Go to account A, VPC A, find the route for the subnet where the EC2 instance is and add a route for destination 192.168.30.0/24 to go over peering. As you can see this is the same route that we modified initially in our first use case.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-10.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-1024x575.png" alt="" width="1024" height="575" class="aligncenter size-large wp-image-9985" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-1024x575.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-300x168.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-768x431.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-1200x675.png 1200w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-1170x657.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-585x328.png 585w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-10.png 1290w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Now, log to account B and VPC C and modify the route but this time, the destination is VPC A (192.168.10.0/24). At this point you should be able to ping EC2-A from EC2-C. Don&#8217;t worry if you can&#8217;t ping EC2-C from EC2-A.<br />
At this point we can create the interface endpoint where the bucket is. This time choose interface as type, VPC-A, our security group, full access policy and the subnet where EC2 is. This subnet has nothing to do with the bucket access, we are just creating a network interface there. We can choose any subnet in that VPC, we just need the IP assigned to the endpoint interface.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-11.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-1024x835.png" alt="" width="1024" height="835" class="aligncenter size-large wp-image-9994" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-1024x835.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-300x245.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-768x626.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-1536x1252.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-2048x1669.png 2048w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-1920x1565.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-1170x954.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-585x477.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
There will be 2 DNS names created for you. One is referring the zone and the other the region. Use the regional DNS name, the first one and add the word bucket instead of the * character.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-12.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-12.png" alt="" width="913" height="526" class="aligncenter size-full wp-image-9995" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-12.png 913w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-12-300x173.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-12-768x442.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-12-585x337.png 585w" sizes="(max-width: 913px) 100vw, 913px" /></a><br />
From instance EC2-C, this is how you will access the bucket.</p>
<pre class="brush: bash; title: ; notranslate">
aws s3 ls s3://myuniquenameforthebucket --endpoint-url https://bucket.vpce-038d5f94d78d8b216-vyvk5uon.s3.us-east-2.vpce.amazonaws.com
</pre>
<p>If you try from the instance EC2-C, this won&#8217;t work. Why? Because of the security group that we used. We have to allow HTTPS traffic from 192.168.30.0/24. Go find the first security group, that&#8217;s the one we used for the interface endpoint and allow HTTPS from 192.168.30.0/24. After the change, you&#8217;ll be able to access the bucket.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2024/06/aws-access-an-s3-bucket-using-gateway-and-interface-endpoints-privatelink/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
