要件
- 学生がSSHログインできてroot権限を渡せるサーバーを人数分作りたい。
- Ubuntu で http サーバーを立ててページを公開できるようにしたい。
- sudo 権限を渡す
- なるべく渡す情報を減らしたいので、学籍番号でアクセスできるようにしたい
-
ssh student_id@student_id.example.comのようなホスト名でアクセスできるとよい
-
- SSH鍵ペアは事前に作っておく
- セキュアに渡せる経路は別途確保してある。
- AWS それ自体の使い方を学習させるにはちょっと重い気がする
- そういう時間を割けるならば AWS Education を使うのも手。無料だし。
- 環境は演習が終わったらすぐ捨てるが、鍵は来週再利用する
CloudFormation
Infrastructure as Code に基づき、AWS の EC2 やセキュリティ設定を含むいろんなリソースを設定ファイル(テンプレート)一つで与えられる仕組みのようだ。
略称は cfn。
方法
cfn の Nested Stack で学生1人に割り当てるスタックを人数分コピペでインクルードする。
EC2 の起動イメージ (AMI) の準備
-
AMI は Ubuntu 18 LTS の公式を以下のように弄ったもの
apt-get update -y
apt-get install -y python-pip
apt-get install -y python-setuptools
mkdir -p /opt/aws/bin
python /usr/lib/python2.7/dist-packages/easy_install.py --script-dir /opt/aws/bin https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz
## EC2 インスタンス等の設定ファイル
* 学生に渡す EC2 と Route53 とポート設定をまとめたファイル。 https://gist.github.com/mmasko/66d34b651642525c63cd39251e0c2a8b を元に色々と細かく修正した。
* cfn-init がエラーを吐くのだが原因はよくわからない。とりあえず動いているのでヨシ!
* Elastic IP で固定IPを割り当てている。
* 80人となると `The maximum number of addresses has been reached.` のエラーが出る。 [上限値の変更申請を日本語で出せる。](https://qiita.com/msyk_tym/items/e3bbed8d382d06a6a329)
* example.com は Route53 で確保したドメイン名に置き換える。 例えば `.name` は最も安い TLD の一つのようだった (Route53 の何かの PDF ドキュメントに載っていた価格一覧を見る限り)。
* kisojikken というのは講義の名前
## 初期ファイルとスクリプト
3つのスクリプトをスタック作成時においている。
* `/etc/kisojikken/mkusers.sh (学籍番号)`
* ユーザを作る
* `/etc/kisojikken/pubkey.sh`
* 公開鍵をダウンロードしてきて .ssh/authorized_key に加える
* 公開鍵の tar.gz を置いてある URL をハードコードしている:-p
* `/etc/kisojikken/init.sh (学籍番号)`
* スタック作成時に上記2つのスクリプトを呼び出す
その他の `/etc/cfn/cfn-hup.conf` などは参考元のスクリプトからそのまま持ってきたもので、 `cfn-init` が使っている (はず)
## テンプレート (ネストされる側)
```yaml
Parameters:
EnvironmentSize:
Type: String
Default: t3a.nano
AllowedValues:
- t2.nano
- t3a.nano
- t3a.small
Description: Select instance size
KeyPair:
Default: somekey
Description: Existing keypair
Type: AWS::EC2::KeyPair::KeyName
StudentID:
Description: Student ID to create
Type: String
SecurityGroupName:
Default: kisojikken2020-securitygroup
Description: Security Group's Name
Type: String
ImageId:
Default: ami-02fdb41f362247f2e
Description: AMI Image Id
Type: String
Resources:
DNSRecordSet:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName: example.com.
Name: {"Fn::Join": [ ".", [ !Ref StudentID, "example.com" ]]}
Type: A
TTL: '300'
ResourceRecords:
- !Ref PublicIP
DependsOn: PublicIP
PublicIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
InstanceId: !Ref NestedInstance
NestedInstance:
Type: AWS::EC2::Instance
CreationPolicy:
ResourceSignal:
Timeout: PT5M
Properties:
ImageId: !Ref ImageId
InstanceType: !Ref EnvironmentSize
SecurityGroups:
- !Ref SecurityGroupName
KeyName: !Ref KeyPair
Tags:
- Key: Name
Value: !Ref StudentID
UserData:
Fn::Base64: !Sub |
#!/bin/bash
/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource NestedInstance --configsets setup --region ${AWS::Region}
/etc/kisojikken/init.sh ${StudentID}
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource NestedInstance --region ${AWS::Region}
Metadata:
AWS::CloudFormation::Init:
configSets:
setup:
- "configure_cfn"
configure_cfn:
files:
/etc/kisojikken/init.sh:
content: !Sub |
#!/bin/bash
set -e
USERS=$*
/etc/kisojikken/mkusers.sh $USERS
/etc/kisojikken/pubkey.sh
mode: "000555"
owner: root
group: root
/etc/kisojikken/mkusers.sh:
content: !Sub |
#!/bin/bash
set -e
USERS=$*
for U in $USERS
do
adduser --disabled-password --gecos "" $U
usermod -G sudo $U
done
mode: "000555"
owner: root
group: root
/etc/kisojikken/pubkey.sh:
content: !Sub |
#!/bin/bash
set -e
PUBKEYDIR=/etc/kisojikken2020/pubkeys
mkdir -p $PUBKEYDIR
pushd $PUBKEYDIR > /dev/null
curl -s (鍵が置いてあるURL) |tar zxf - --overwrite
popd > /dev/null
for u in `ls /home`
do
if [ -d $PUBKEYDIR/$u ];
then
SSHDIR=/home/$u/.ssh
mkdir -p $SSHDIR
cat $PUBKEYDIR/$u/id_rsa.pub >> $SSHDIR/authorized_keys
chown -R $u $SSHDIR
chmod -R go-rwx $SSHDIR
echo ssh key added for user $u
fi
done
mode: "000555"
owner: root
group: root
/etc/cfn/cfn-hup.conf:
content: !Sub |
[main]
stack=${AWS::StackId}
region=${AWS::Region}
verbose=true
interval=5
mode: "000400"
owner: root
group: root
/etc/cfn/hooks.d/cfn-auto-reloader.conf:
content: !Sub |
[cfn-auto-reloader-hook]
triggers=post.update
path=Resources.NestedInstance.Metadata.AWS::CloudFormation::Init
action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource NestedInstance --configsets setup --region ${AWS::Region}
mode: "000400"
owner: root
group: root
/lib/systemd/system/cfn-hup.service:
content: !Sub |
[Unit]
Description=cfn-hup daemon
[Service]
Type=simple
ExecStart=/opt/aws/bin/cfn-hup
Restart=always
[Install]
WantedBy=multi-user.target
mode: "000400"
owner: root
group: root
commands:
01_enable_cfn-hup:
command: "systemctl enable cfn-hup.service"
02_start_cfn-hup:
command: "systemctl start cfn-hup.service"
テンプレート (ネストする側)
Parameters:
EnvironmentSize:
Type: String
Default: t3a.nano
AllowedValues:
- t2.nano
- t3a.nano
- t3a.small
Description: Select instance size
KeyPair:
Description: Name of an existing EC2 KeyPair to enable SSH access to the instance.
ConstraintDescription: must be the name of an existing EC2 KeyPair.
Type: AWS::EC2::KeyPair::KeyName
SecurityGroupName:
Description: Security Group's Name
Default: kisojikken2020-securitygroup
Type: String
ImageId:
Default: ami-02fdb41f362247f2e
Type: String
InstanceTemplateURL:
Default: (上記 kinstance.template のURL)
Description: template url
Type: String
Resources:
y3033111:
Type: 'AWS::CloudFormation::Stack'
Properties:
TemplateURL: !Ref InstanceTemplateURL
Parameters:
EnvironmentSize: !Ref EnvironmentSize
SecurityGroupName: !Ref SecurityGroupName
ImageId: !Ref ImageId
KeyPair: !Ref KeyPair
StudentID: *****
z3033003:
Type: 'AWS::CloudFormation::Stack'
Properties:
TemplateURL: !Ref InstanceTemplateURL
Parameters:
EnvironmentSize: !Ref EnvironmentSize
SecurityGroupName: !Ref SecurityGroupName
ImageId: !Ref ImageId
KeyPair: !Ref KeyPair
StudentID: *****
.... (これが人数分つづく)