プライベートサブネット内のUbuntuから、S3上の自作aptリポジトリにアクセスする
以下のページで、S3上にyumリポジトリを作成する方法をご紹介しました。
今回はS3上でaptリポジトリを構築し、プライベートサブネット上に配置されたUbuntuからアクセスします。
構築する環境
2つのVPCを作成します。
一方のVPCはaptリポジトリ作成用です。
最新のUbuntuインスタンスを配置し、aptlyを使用してリポジトリを作成します。
今回はプライベートサブネットからApacheをインストールするためのリポジトリを作成します。
もう一方のVPCに、自作リポジトリの検証インスタンスを配置します。
こちらのVPCにはインターネットゲートウェイやNATゲートウェイを配置せず、S3用VPCエンドポイントを経由して、S3バケットにアクセスします。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置してます。
https://github.com/awstut-an-r/awstut-fa/tree/main/032
テンプレートファイルのポイント解説
静的Webサイトホスティング機能を有効化する
S3バケットを確認します。
ポイントは静的Webサイトホスティングに関する設定です。
Resources:
Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref Prefix
AccessControl: Private
WebsiteConfiguration:
IndexDocument: index.html
Code language: YAML (yaml)
WebsiteConfigurationプロパティで静的Webサイトホスティング機能を設定します。
本機能を有効化することで、aptクライアントからのHTTP通信を受け付けることができます。
注意点はIndexDocumentプロパティです。
本プロパティに「index.html」を設定します。
これは本プロパティを設定しなければ、ホスティング機能が有効化できないためです。
そのため実際にインデックスファイルを設置するわけではありませんが、形式的に設定します。
バケットポリシーでアクセス制限する
作成したバケットへのアクセスを、バケットポリシーを使って制限します。
ポイントはアクセスを許可する条件です。
今回は以下の方針でアクセス制限します。
- 送信元のIPアドレスが、NATゲートウェイに付与したElastic IPアドレスの場合は、アクセスを許可する ※ VPC1内のインスタンス用
- 送信元のVPCがVPC2の場合は、アクセスを許可する ※ VPC2内のインスタンス用
Resources:
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref Bucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- s3:*
Effect: Allow
Resource:
- !Sub "arn:aws:s3:::${Bucket}"
- !Sub "arn:aws:s3:::${Bucket}/*"
Condition:
IpAddress:
"aws:SourceIp":
- !Ref EIP
Principal: "*"
- Action:
- s3:*
Effect: Allow
Resource:
- !Sub "arn:aws:s3:::${Bucket}"
- !Sub "arn:aws:s3:::${Bucket}/*"
Condition:
StringEquals:
"aws:SourceVpc":
- !Ref VPC
Principal: "*"
Code language: YAML (yaml)
2つのポリシーを定義します。
1つ目のポリシーは、インスタンス1からのアクセスを許可するためのものです。
Conditionプロパティがポイントです。
IpAddressプロパティに「aws:SourceIp」およびNATゲートウェイに付与したElastic IPアドレスを設定することで、同アドレスからの通信を許可するポリシーとなります。
インスタンス1がS3バケットにアクセスする際は、NATゲートウェイを経由し、送信元アドレスがElastic IPアドレスに付け替わるためです。
2つ目のポリシーは、インスタンス2からのアクセスを許可するためのものです。
こちらもConditionプロパティで条件を設定します。
StringEqualsプロパティに「aws:SourceVpc」およびVPC2のIDを設定することで、同VPC内からの通信を許可するポリシーとなります。
S3用VPCエンドポイント
VPC2はインターネットとの接点を作成せず、VPCエンドポイントを通じて、S3バケットへアクセスします。
Resources:
S3Endpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
RouteTableIds:
- !Ref PrivateRouteTable
ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
VpcId: !Ref VPC
Code language: YAML (yaml)
特別な設定は不要です。
ServiceNameプロパティにS3を設定します。
S3用のVPCエンドポイントはゲートウェイタイプですから、VPC2のIDおよび同VPC内のサブネットに関連づいたルートテーブルを設定します。
aptlyでリポジトリを作成する
aptリポジトリを作成するために、インスタンス1で実行する内容を確認します。
Resources:
RunShellScriptAssociation1:
Type: AWS::SSM::Association
Properties:
AssociationName: !Sub "${Prefix}-runshellscript-association1"
Name: AWS-RunShellScript
Parameters:
commands:
- "sudo apt update"
- "sudo apt install -y aptly"
- "sudo apt install -y unzip"
- "curl 'https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip' -o 'awscliv2.zip'"
- "unzip awscliv2.zip"
- "sudo ./aws/install"
- !Sub |
sudo cat << EOF > ./gpg.txt
Key-Type: RSA
Subkey-Type: RSA
Key-Length: 2048
Subkey-Length: 2048
Expire-Date: 0
Name-Real: ${GPGName}
Name-Email: ${GPGEmail}
Passphrase: ${GPGPassphrase}
EOF
- "sudo gpg1 --gen-key --batch ./gpg.txt"
- "sudo gpg1 --no-default-keyring --keyring trustedkeys.gpg --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5 3B4FE6ACC0B21F32"
- !Sub "sudo aptly mirror create -architectures=${Arch} -filter='apache2' -filter-with-deps myrepo http://ap-northeast-1a.clouds.ports.ubuntu.com/ubuntu-ports/ xenial main restricted universe multiverse"
- !Sub "sudo aptly mirror update ${Repository}"
- !Sub "sudo aptly snapshot create ${Repository}-${SnapshotSuffix} from mirror ${Repository}"
- !Sub |
sudo cat << EOF > /root/.aptly.conf
{
"rootDir": "/root/.aptly",
"downloadConcurrency": 4,
"downloadSpeedLimit": 0,
"architectures": [],
"dependencyFollowSuggests": false,
"dependencyFollowRecommends": false,
"dependencyFollowAllVariants": false,
"dependencyFollowSource": false,
"dependencyVerboseResolve": false,
"gpgDisableSign": false,
"gpgDisableVerify": false,
"gpgProvider": "gpg",
"downloadSourcePackages": false,
"skipLegacyPool": true,
"ppaDistributorID": "ubuntu",
"ppaCodename": "",
"skipContentsPublishing": false,
"FileSystemPublishEndpoints": {},
"S3PublishEndpoints": {
"fa-032": {
"region": "${AWS::Region}",
"bucket": "${Bucket}"
}
},
"SwiftPublishEndpoints": {}
}
EOF
- !Sub "sudo aptly publish snapshot -batch -passphrase='${GPGPassphrase}' ${Repository}-${SnapshotSuffix} s3:${Bucket}:"
- "sudo gpg1 --export --armor > ./gpg.pub"
- !Sub "aws s3 cp ./gpg.pub s3://${Bucket}/"
Targets:
- Key: InstanceIds
Values:
- !Ref Instance1
WaitForSuccessTimeoutSeconds: !Ref WaitForSuccessTimeoutSeconds
Code language: YAML (yaml)
今回はインスタンス初期化処理の一環として、SSMドキュメントAWS-RunShellScriptを実行し、リポジトリ作成を行います。
同ドキュメントを使用した初期化処理に関しては、以下のページをご確認ください。
冒頭でも述べた通り、aptlyを使用してリポジトリを作成し、S3バケットに公開します。
commandsプロパティで、実際に実行するコマンドを定義します。
コマンドの内容は以下の通りです。
- 事前準備(パッケージ更新、aptlyインストール、AWS CLIインストール)
- バッチモードのGnuPGで公開鍵を作成する
- 元リポジトリ(http://ap-northeast-1a.clouds.ports.ubuntu.com/ubuntu-ports/)用の公開鍵を取得して、ミラーリポジトリを作成し、スナップショットを作成する
- aptlyのコンフィグファイルにS3バケットの情報を記載し、バッチモードでS3バケットにスナップショットを公開する。
- リポジトリ署名時に使用した鍵の公開鍵をエクスポートし、AWS CLIを使用してS3バケットにアップロードする。
注意点はaptlyが対応しているGnuPGのバージョンです。
Aptly at this time only supports GNUPG 1.x for server-side use. On newer Debian systems you’ll want to make sure that the gnupg1 and gpgv1 packages are installed.
PGP PROVIDERS
上記の通り、1系にのみ対応しているとされていますので、2系を使用しないように注意してください。
ですから今回は「gpg」コマンドではなく、「gpg1」コマンドを使用することで、明示的に1系を使用しています。
もう1つの注意点は、aptlyからS3バケットにアクセスするための権限についてです。
公式サイトでは、必要な権限を持つIAMユーザーのアクセスキーを使用する方法が紹介されています。
awsAccessKeyID, awsSecretAccessKey: (optional) Amazon credentials to access S3 bucket. If not supplied, environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are used.
PUBLISHING TO AMAZON S3
ただし今回は上記で記されているようなパラメータ設定や、環境変数の指定は不要です。
これは先述のS3バケットポリシー側で、両インスタンスからのアクセスを許可しているためです。
なおaptlyの使用方法については、以下のサイトを参照しました。
https://gihyo.jp/admin/serial/01/ubuntu-recipe/0485
https://qiita.com/roki18d/items/0f63a85292df1ef511c5
aptに自作リポジトリを登録する
インスタンス2で実行する内容を確認します。
ポイントは自作したaptリポジトリを参照する方法です。
Resources:
RunShellScriptAssociation2:
Type: AWS::SSM::Association
DependsOn:
- RunShellScriptAssociation1
Properties:
AssociationName: !Sub "${Prefix}-runshellscript-association2"
Name: AWS-RunShellScript
Parameters:
commands:
- !Sub "curl ${BucketWebsiteURL}/gpg.pub | sudo apt-key add -"
- !Sub |
sudo cat << EOF > /etc/apt/sources.list
deb ${BucketWebsiteURL}/ xenial main
EOF
Targets:
- Key: InstanceIds
Values:
- !Ref Instance2
WaitForSuccessTimeoutSeconds: !Ref WaitForSuccessTimeoutSeconds
Code language: YAML (yaml)
こちらもSSMドキュメントAWS-RunShellScriptを実行して、自作リポジトリ参照に必要な処理を行います。
コマンドの内容は以下の通りです。
- S3バケットに配置した公開鍵をインポートする
- 自作リポジトリを記載した/etc/apt/sources.listを配置する
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- S3バケット:fa-032
- S3バケットのWebサイトエンドポイント:http://fa-032.s3-website-ap-northeast-1.amazonaws.com
- インスタンス1:i-09467a9a4e6bb9bc4
- インスタンス2:i-063843a7185abe446
AWS Management Consoleからも、リソースの作成状況を確認します。
まずインスタンスの作成状況です。
確かに2つのインスタンスが作成されています。
次に両インスタンスにおけるSSMドキュメントの実行結果を確認します。
Outputから実行結果を確認することができます。
S3バケットを確認します。
aptlyでアップロードされたリポジトリファイルと、このリポジトリの公開鍵が配置されていることがわかります。
静的Webサイトホスティング機能が有効化され、HTTPリクエストを受け付ける準備が整っていることも確認できます。
バケットポリシーによってNATゲートウェイのアドレスと、VPC2からのアクセスが許可されています。
これで両インスタンスがS3バケットにアクセス可能ということです。
動作確認
準備が整いましたので、インスタンス2にアクセスします。
インスタンスへのアクセスは、SSM Session Managerを使用します。
% aws ssm start-session --target i-063843a7185abe446
Starting session with SessionId: root-05d2fb1be2c79590b
$
Code language: Bash (bash)
詳細は以下のページをご確認ください。
公開鍵の登録状況を確認します。
$ sudo apt-key list
/etc/apt/trusted.gpg
--------------------
pub rsa2048 2022-06-19 [SCEA]
1133 C594 73B1 E2D9 5E32 09A2 BD71 F30D 3250 8A8D
uid [ unknown] fa-032 <fa-032@example.com>
sub rsa2048 2022-06-19 [SEA]
...
Code language: Bash (bash)
自作リポジトリの検証用鍵が登録されています。
自作リポジトリの登録用ファイルを確認します。
$ sudo cat /etc/apt/sources.list
deb http://fa-032.s3-website-ap-northeast-1.amazonaws.com/ xenial main
Code language: Bash (bash)
確かに自作リポジトリが登録されています。
パッケージ一覧を更新します。
$ sudo apt update
Get:1 http://fa-032.s3-website-ap-northeast-1.amazonaws.com xenial InRelease [3170 B]
Get:2 http://fa-032.s3-website-ap-northeast-1.amazonaws.com xenial/main arm64 Packages [31.8 kB]
Fetched 34.9 kB in 0s (151 kB/s)
Reading package lists... Done
Building dependency tree
Reading state information... Done
All packages are up to date.
Code language: Bash (bash)
自作リポジトリにアクセスし、正常に更新されました。
先述の鍵を使って検証が行われたということです。
Apacheをインストールします。
$ sudo apt install -y apache2
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
apache2-bin apache2-data apache2-utils libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap liblua5.1-0 libssl1.0.0
Suggested packages:
www-browser apache2-doc apache2-suexec-pristine | apache2-suexec-custom
Recommended packages:
ssl-cert
The following NEW packages will be installed:
apache2 apache2-bin apache2-data apache2-utils libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap liblua5.1-0 libssl1.0.0
0 upgraded, 10 newly installed, 0 to remove and 0 not upgraded.
Need to get 2061 kB of archives.
After this operation, 8762 kB of additional disk space will be used.
Get:1 http://fa-032.s3-website-ap-northeast-1.amazonaws.com xenial/main arm64 libapr1 arm64 1.5.2-3 [71.1 kB]
Get:2 http://fa-032.s3-website-ap-northeast-1.amazonaws.com xenial/main arm64 libssl1.0.0 arm64 1.0.2g-1ubuntu4 [726 kB]
Get:3 http://fa-032.s3-website-ap-northeast-1.amazonaws.com xenial/main arm64 libaprutil1 arm64 1.5.4-1build1 [67.4 kB]
Get:4 http://fa-032.s3-website-ap-northeast-1.amazonaws.com xenial/main arm64 libaprutil1-dbd-sqlite3 arm64 1.5.4-1build1 [9656 B]
Get:5 http://fa-032.s3-website-ap-northeast-1.amazonaws.com xenial/main arm64 libaprutil1-ldap arm64 1.5.4-1build1 [8358 B]
Get:6 http://fa-032.s3-website-ap-northeast-1.amazonaws.com xenial/main arm64 liblua5.1-0 arm64 5.1.5-8ubuntu1 [88.7 kB]
Get:7 http://fa-032.s3-website-ap-northeast-1.amazonaws.com xenial/main arm64 apache2-bin arm64 2.4.18-2ubuntu3 [764 kB]
Get:8 http://fa-032.s3-website-ap-northeast-1.amazonaws.com xenial/main arm64 apache2-utils arm64 2.4.18-2ubuntu3 [77.5 kB]
Get:9 http://fa-032.s3-website-ap-northeast-1.amazonaws.com xenial/main arm64 apache2-data all 2.4.18-2ubuntu3 [162 kB]
Get:10 http://fa-032.s3-website-ap-northeast-1.amazonaws.com xenial/main arm64 apache2 arm64 2.4.18-2ubuntu3 [86.7 kB]
...
Code language: Bash (bash)
正常に自作リポジトリからインストールが完了しました。
最後にApacheの動作状況を確認します。
$ sudo systemctl status apache2
● apache2.service - LSB: Apache2 web server
Loaded: loaded (/etc/init.d/apache2; generated)
Drop-In: /usr/lib/systemd/system/apache2.service.d
└─apache2-systemd.conf
Active: active (running) since Sun 2022-06-19 03:58:31 UTC; 1min 54s ago
Docs: man:systemd-sysv-generator(8)
Tasks: 55 (limit: 1061)
Memory: 4.6M
CGroup: /system.slice/apache2.service
├─2221 /usr/sbin/apache2 -k start
├─2224 /usr/sbin/apache2 -k start
└─2225 /usr/sbin/apache2 -k start
Jun 19 03:58:30 ip-10-0-1-205 systemd[1]: Starting LSB: Apache2 web server...
Jun 19 03:58:30 ip-10-0-1-205 apache2[2199]: * Starting Apache httpd web server apache2
Jun 19 03:58:31 ip-10-0-1-205 apache2[2199]: *
Jun 19 03:58:31 ip-10-0-1-205 systemd[1]: Started LSB: Apache2 web server.
Code language: Bash (bash)
正常に動作しています。
まとめ
S3上に、自作のaptリポジトリを構築する方法を確認しました。
自作リポジトリを作成し、参照することで、プライベートサブネットに配置されたUbuntuインスタンスでも、インターネットにアクセスせずとも、任意のパッケージをインストールできることできました。