Quantcast
Channel: mattintosh note
Viewing all articles
Browse latest Browse all 893

孤独の AWS Cloud9 ハンズオン 〜Cloud9 は SSH 踏み台として使えるのか〜

$
0
0

先日、Amazon本社で AWSスタートアップの説明を聞く機会があった。そこで「最近では踏み台に Cloud9 を使っている方も」という話を聞いたので「本当に踏み台として使えるのか」「Terraform との相性はどうなのか」というのを確認してみた。なお、Cloud9 には新規に EC2 インスタンスを作成するタイプと既存の EC2 インスタンスなどに Cloud9 IDEをインストールして Cloud9 environment として使う SSHタイプもあるが後者については触れない。

AWS Cloud9 とは

AWS Cloud9 はブラウザで使えるクラウド上の IDE。本来は開発目的で利用するものなんだろうが EC2 インスタンスを使っているので普通に踏み台として EC2 を構築するのとあまり変わらない。実際、既存の EC2 インスタンスに Cloud9 IDEをインストールしてそのまま「environment」として使うこともできる。

一般的な SSH接続を中継する方法以外に Cloud9 IDEのターミナルからプライベートネットワークにあるインスタンスSSH接続をするという選択肢がある。AWSマネジメントコンソールにアクセスでき、対象の「environment」でターミナルを使う権限があるユーザであれば SSHクライアントなしでプライベートネットワークに配置されているインスタンスにアクセスができる。ブラウザ上でファイル転送もできるので大抵のことは Cloud9 IDE上でできるだろう。

他にも Cloud9 を使うメリットとしては以下のようなことが考えられる。

  • membership というもので IDEにアクセスできる(=ターミナルを利用できる)ユーザを管理することができる。
  • (Cloud9 IDEからしかアクセス出来ないと思われる)セキュリティグループを自動的に作成してくれる。名前は aws-cloud9-環境名-乱数のような書式。
  • インアクティブな状態が続いた場合の自動シャットダウンを「30 分」「1 時間」「4 時間」「1 日」「1 週間」「なし」から設定することができる。踏み台用のインスタンスのコストを抑えることができる。

「お、これは結構使えるのでは?」と思ったが、試しているうちにデメリットも色々と出てきた。詳しくは後述するが、だいたい以下のような点。

  • Cloud9 用のインスタンスを作成したらサーバに接続するための秘密鍵を保存しておく必要がある。
  • 一般的な SSH接続を中継する踏み台として利用する場合、セキュリティグループの編集が必要になったり、EIP を設定したりする必要がああるが自動化が難しい。

現時点での個人的な感想は「踏み台として『使える』か『使えない』かなら『使える』が、何か中途半端」といった感じ。

では孤独のハンズオンで色々と試していく。

Cloud9 environment の作成

Cloud9 では environment(以下、「Cloud9 environment」とする)と membershipでアクセス制限を行っている。作成した Cloud9 environment の membership に IAM ユーザやロールを read-onlyまたは read-writeのいずれかの権限で追加することによって複数のユーザで Cloud9 environment を共有することができるようになっている。

AWSマネジメントコンソールの Cloud9 メニューでは以下のように environment の種類が分かれている。

Your environmentOwner が自身の ARN で登録されている Cloud9 environment が表示される。AWSマネジメントコンソールから作成した場合は必ずこちらに登録される。
Shared with you対象の Cloud9 environment の membership に自身の ARN が read-onlyまたは read-write権限で登録されている Cloud9 environment が表示される。
Account EnvironmentsAWSアカウントに登録されている Cloud9 environment が表示される。自身が Owner の Cloud9 environment 以外に、Owner でもなく membership でも登録されていない Cloud9 environment も表示される。Terraform や AWSCLIで Owner の ARN を指定しなかった場合はここにのみ表示される。

membership には read-onlyread-write以外に ownerがあり、これは Cloud9 environment 作成時に自動的に作成される membership であり変更はできない。

AWSマネジメントコンソール、Terraform、AWSCLIで Cloud9 environment 作成時の挙動が異なるので以下にまとめておく。

AWSマネジメントコンソールから Cloud9 environment を作成する

「Name」は必須だが「Description」は任意。既に同名の Cloud9 environment が存在する場合は作成できない。(Owner が異なる場合は作成可能)

f:id:mattintosh4:20190714230009p:plain
AWS Cloud9

インスタンスタイプや配置する VPC、サブネットなどを選択する。インスタンスタイプは「Other instance type」を選択すれば好きなものを選ぶことができる。執筆時点ではここの「AmazonLinux」は「AmazonLinux 2」ではない。VPCやサブネットはデフォルトでは「デフォルト VPC」と「デフォルトサブネット」を使うようになっている。実際にはプロジェクト用に作成した VPCとサブネットに配置すると思うが、ここで気をつけなければならないのは Cloud9 environment を配置するサブネットはパブリックサブネットでなければならない。Cloud9 によって自動的に作成されるセキュリティグループを見ればわかるがインバウンドルールに Cloud9 用に予約されたネットワークからの 22 番接続を許可する設定が追加される。Cloud9 environment への接続へはグローバルから行われることになるため NAT ゲートウェイの有無に関わらずプライベートサブネットへ配置すると Cloud9 IDEにアクセスできなくなる。

f:id:mattintosh4:20190714230357p:plain
AWS Cloud9 - Create environment

設定内容を確認して[Create environment]ボタンをクリックする。

f:id:mattintosh4:20190714231315p:plain
AWS Cloud9 - Create environment

Cloud9 用のインスタンス作成が行われて接続画面に移行する。Cloud9 用の AMI から作成されているのでだいたい 60 秒もあれば使えるようになる。

f:id:mattintosh4:20190714231458p:plain
AWS Cloud9 - Cloud9 IDE

インスタンスの状態を確認してみる

f:id:mattintosh4:20190715011314p:plain

Cloud9 で作成される EC2 インスタンスとセキュリティグループは CloudFormation によって作成されているのでまずは CloudFormation のスタックを確認する。スタック名は aws-cloud9-{Cloud9環境名}-{Cloud9環境ID}となっている。Cloud9 environment の ID は Cloud9 IDEの URL か AWSCLIcloud9 list-environmentsで調べることができる。

aws cloudformation describe-stacks \--stack-name aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
{"Stacks": [{"StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            "StackName": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "CreationTime": "2019-07-14T14:59:45.535Z",
            "RollbackConfiguration": {},
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Tags": [{"Key": "aws:cloud9:environment",
                    "Value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                },
                {"Key": "aws:cloud9:owner",
                    "Value": "XXXXXXXXXXXXXXXXXXXXX:john"
                }],
            "EnableTerminationProtection": false}]}

スタックのリソースを確認する。

Terminal

aws cloudformation describe-stack-resources --stack-name aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

ここから EC2 インスタンスの ID とセキュリティグループの ID がわかる。

Result

{"StackResources": [{"StackName": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            "LogicalResourceId": "Instance",
            "PhysicalResourceId": "i-xxxxxxxxxxxxxxxxx",
            "ResourceType": "AWS::EC2::Instance",
            "Timestamp": "2019-07-14T15:00:31.775Z",
            "ResourceStatus": "CREATE_COMPLETE"
        },
        {"StackName": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            "LogicalResourceId": "InstanceSecurityGroup",
            "PhysicalResourceId": "sg-xxxxxxxxxxxxxxxxx",
            "ResourceType": "AWS::EC2::SecurityGroup",
            "Timestamp": "2019-07-14T14:59:55.347Z",
            "ResourceStatus": "CREATE_COMPLETE"
        }]}

セキュリティグループは aws-cloud9-{Cloud9環境名}-{Cloud9環境ID}-InstanceSecurityGroup-{乱数}という名前で作成されているので、GroupNameかタグ値で CloudFormation のスタック名をフィルタリングすればすぐ出てくるだろう。

Terminal

aws ec2 describe-security-groups --filters'Name=group-name,Values=aws-cloud9-foo-*'

セキュリティグループのインバウンドルールには Cloud9 で予約されていると思われるネットワークアドレスが二つ登録されている。

Result

{"SecurityGroups": [{"Description": "Security group for AWS Cloud9 environment aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "GroupName": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-InstanceSecurityGroup-XXXXXXXXXXXXX",
            "IpPermissions": [{"FromPort": 22,
                    "IpProtocol": "tcp",
                    "IpRanges": [{"CidrIp": "18.179.48.96/27"
                        },
                        {"CidrIp": "18.179.48.128/27"
                        }],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "ToPort": 22,
                    "UserIdGroupPairs": []}],
            "OwnerId": "000000000000",
            "GroupId": "sg-xxxxxxxxxxxxxxxxx",
            "IpPermissionsEgress": [{"IpProtocol": "-1",
                    "IpRanges": [{"CidrIp": "0.0.0.0/0"
                        }],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "UserIdGroupPairs": []}],
            "Tags": [{"Key": "aws:cloud9:owner",
                    "Value": "XXXXXXXXXXXXXXXXXXXXX:john"
                },
                {"Key": "aws:cloudformation:stack-name",
                    "Value": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                },
                {"Key": "aws:cloud9:environment",
                    "Value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                },
                {"Key": "aws:cloudformation:logical-id",
                    "Value": "InstanceSecurityGroup"
                },
                {"Key": "aws:cloudformation:stack-id",
                    "Value": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
                }],
            "VpcId": "vpc-xxxxxxxx"
        }]}

EC2 インスタンスも同様。

Terminal

aws describe-instances --filters'Name=tag-key,Values=Name''Name=tag-value,Values=aws-cloud9-foo-*'

{"Reservations": [{"Groups": [],
            "Instances": [{"AmiLaunchIndex": 0,
                    "ImageId": "ami-0872d0e3184cfc976",
                    "InstanceId": "i-xxxxxxxxxxxxxxxxx",
                    "InstanceType": "t2.micro",
                    "LaunchTime": "2019-07-14T14:59:58.000Z",
                    "Monitoring": {"State": "disabled"
                    },
                    "Placement": {"AvailabilityZone": "ap-northeast-1a",
                        "GroupName": "",
                        "Tenancy": "default"
                    },
                    "PrivateDnsName": "ip-172-31-40-231.ap-northeast-1.compute.internal",
                    "PrivateIpAddress": "172.31.40.231",
                    "ProductCodes": [],
                    "PublicDnsName": "ec2-00-000-000-000.ap-northeast-1.compute.amazonaws.com",
                    "PublicIpAddress": "00.000.000.000",
                    "State": {"Code": 16,
                        "Name": "running"
                    },
                    "StateTransitionReason": "",
                    "SubnetId": "subnet-xxxxxxxx",
                    "VpcId": "vpc-xxxxxxxx",
                    "Architecture": "x86_64",
                    "BlockDeviceMappings": [{"DeviceName": "/dev/xvda",
                            "Ebs": {"AttachTime": "2019-07-14T14:59:59.000Z",
                                "DeleteOnTermination": true,
                                "Status": "attached",
                                "VolumeId": "vol-xxxxxxxxxxxxxxxxx"
                            }}],
                    "ClientToken": "aws-c-Insta-XXXXXXXXXXXX",
                    "EbsOptimized": false,
                    "EnaSupport": true,
                    "Hypervisor": "xen",
                    "NetworkInterfaces": [{"Association": {"IpOwnerId": "amazon",
                                "PublicDnsName": "ec2-00-000-000-000.ap-northeast-1.compute.amazonaws.com",
                                "PublicIp": "00.000.000.000"
                            },
                            "Attachment": {"AttachTime": "2019-07-14T14:59:58.000Z",
                                "AttachmentId": "eni-attach-xxxxxxxxxxxxxxxxx",
                                "DeleteOnTermination": true,
                                "DeviceIndex": 0,
                                "Status": "attached"
                            },
                            "Description": "",
                            "Groups": [{"GroupName": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-InstanceSecurityGroup-XXXXXXXXXXXXX",
                                    "GroupId": "sg-xxxxxxxxxxxxxxxxx"
                                }],
                            "Ipv6Addresses": [],
                            "MacAddress": "xx:xx:xx:xx:xx:xx",
                            "NetworkInterfaceId": "eni-xxxxxxxxxxxxxxxxx",
                            "OwnerId": "000000000000",
                            "PrivateDnsName": "ip-172-31-40-231.ap-northeast-1.compute.internal",
                            "PrivateIpAddress": "172.31.40.231",
                            "PrivateIpAddresses": [{"Association": {"IpOwnerId": "amazon",
                                        "PublicDnsName": "ec2-00-000-000-000.ap-northeast-1.compute.amazonaws.com",
                                        "PublicIp": "00.000.000.000"
                                    },
                                    "Primary": true,
                                    "PrivateDnsName": "ip-172-31-40-231.ap-northeast-1.compute.internal",
                                    "PrivateIpAddress": "172.31.40.231"
                                }],
                            "SourceDestCheck": true,
                            "Status": "in-use",
                            "SubnetId": "subnet-xxxxxxxx",
                            "VpcId": "vpc-xxxxxxxx"
                        }],
                    "RootDeviceName": "/dev/xvda",
                    "RootDeviceType": "ebs",
                    "SecurityGroups": [{"GroupName": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-InstanceSecurityGroup-XXXXXXXXXXXXX",
                            "GroupId": "sg-xxxxxxxxxxxxxxxxx"
                        }],
                    "SourceDestCheck": true,
                    "Tags": [{"Key": "aws:cloudformation:logical-id",
                            "Value": "Instance"
                        },
                        {"Key": "Name",
                            "Value": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                        },
                        {"Key": "aws:cloud9:environment",
                            "Value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                        },
                        {"Key": "aws:cloudformation:stack-id",
                            "Value": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
                        },
                        {"Key": "aws:cloud9:owner",
                            "Value": "XXXXXXXXXXXXXXXXXXXXX:john"
                        },
                        {"Key": "aws:cloudformation:stack-name",
                            "Value": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                        }],
                    "VirtualizationType": "hvm",
                    "CpuOptions": {"CoreCount": 1,
                        "ThreadsPerCore": 1}}],
            "OwnerId": "000000000000",
            "RequesterId": "000000000000",
            "ReservationId": "r-xxxxxxxxxxxxxxxxx"
        }]}

いずれもタグに Cloud9 environment の ID が入っているのでフィルタリングはしやすい。

Cloud9 environment の menbership について学ぶ

AWSマネジメントコンソールから Cloud9 environment を作成した場合は現在の IAM ユーザやロールの ARN が Owner として設定されるようになっている。

Terminal

aws cloud9 describe-environments \--environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Result

{"environments": [{"id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "name": "AWS Management Console",
            "description": "",
            "type": "ec2",
            "arn": "arn:aws:cloud9:ap-northeast-1:000000000000:environment:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "ownerArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john"
        }]}

上記の Cloud9 environment の membership がどうなっているか確認してみる。

Terminal

aws cloud9 describe-environment-memberships \--environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Result

{"memberships": [{"permissions": "owner",
            "userId": "XXXXXXXXXXXXXXXXXXXXX:john",
            "userArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john",
            "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "lastAccess": 1562846115.0}]}

membershipsが配列になっていることからもわかるが membership には owner以外に read-onlyread-writeといった権限で複数のユーザを登録することができるようになっている。

Cloud9 environment と membership の関連性

Cloud9 environment の権限には ownerread-onlyread-writeの 3 種類がある。AWSマネジメントコンソールから Cloud9 environment を作成した場合、ownerには現在の IAM ユーザやロールが設定される。ownerは当然のことながら変更や削除などすべての操作を行うことができる。

Cloud9 environment には membership というものがあり、この membership をカスタマイズすることで environment を他のユーザと共有できるようになっている。現状の AWSマネジメントコンソールには membership を操作する画面は無く、Cloud9 IDE内の共有設定で行うようになっている。

f:id:mattintosh4:20190712195322p:plain
AWS Cloud9 IDE

Cloud9 environment を AWSマネジメントコンソール、Terraform、AWSCLIからownerArn を指定せずにそれぞれ作成すると以下のようになる。なお、AWSマネジメントコンソールでは ownerArn の指定方法が無いため Owner には自動的に現在のユーザに設定される。

{"environments": [{"id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "name": "AWS Management Console",
            "description": "",
            "type": "ec2",
            "arn": "arn:aws:cloud9:ap-northeast-1:000000000000:environment:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "ownerArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john"
        },
        {"id": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
            "name": "Terraform",
            "description": "",
            "type": "ec2",
            "arn": "arn:aws:cloud9:ap-northeast-1:000000000000:environment:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
            "ownerArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/1562929437597457834"
        },
        {"id": "cccccccccccccccccccccccccccccccc",
            "name": "AWS CLI",
            "description": "",
            "type": "ec2",
            "arn": "arn:aws:cloud9:ap-northeast-1:000000000000:environment:cccccccccccccccccccccccccccccccc",
            "ownerArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/botocore-session-1562929475"
        }]}

それぞれの ownerArnを見てみると Terraform ではナノ秒タイムスタンプ、AWSCLIでは botocore-session-接頭辞にタイムスタンプが自動的に設定されている。これらの違いを AWSマネジメントコンソールで確認してみる。

まずは「Your environments」を見てみると AWSマネジメントコンソールで作成した Cloud9 environment が表示されている。自分で作成したので Permissions は Owner となっている。作成したはずの「Terraform」や「AWSCLI」は無い。

f:id:mattintosh4:20190712202037p:plain
AWS Cloud9

「Shared with you」を飛ばして「Account environments」を見てみる。先程は無かった「Terraform」と「AWSCLI」も表示されている。

f:id:mattintosh4:20190712202423p:plain
AWS Cloud9

ではこの「Terraform」や「AWSCLI」が使えるのかというのを「Open IDE」を押して試してみるがアクセス権が無いと言われる。

f:id:mattintosh4:20190712202938p:plain
AWS Cloud9

IDEを開くことができないので「Terraform」と「AWSCLI」は AWSマネジメントコンソールから共有設定をすることができず、AWSCLIから設定することになる。しかし、AWSCLIのロールはオーナーではない membership への DeleteEnvironmentMembership権限が無いので後述する「membership の削除」で問題が発生することがある。

フローにしてみると下記のようになる。

f:id:mattintosh4:20190713012300p:plain
AWS Cloud9

自身が Cloud9 environment の Owner であれば AWSCLIから追加した membership を Cloud9 IDEから削除することができるが、最も困るのが Terraform で owner_arnを未指定で作成してしまった場合。Owner が Terraform で作成した ARN になっているので自身を read-writeで membership を追加したとしても Cloud9 IDEに共有設定が表示されないので membership 操作が一切できない。

membership の追加、更新、削除

Cloud9 environment の ID を調べ方

作成した Cloud9 environment の ID を控え忘れてしまった場合は AWSマネジメントコンソールで「Open IDE」や「View Details」、「Edit」を開けば URL に ID が含まれているのでそれをコピーしてもいい。

AWSCLIでは list-environmentsサブコマンドで ID の一覧を表示することができるが、この結果を describe-environmentsに渡さないと名前がわからない。

Terminal

aws cloud9 list-environments

Result

{"environmentIds": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
        "cccccccccccccccccccccccccccccccc"
    ]}

一つずつ調べるのは面倒だが list-environmentsの結果を --queryオプションで整形して describe-environments --environment-idsの引数に渡せば名前付きで一覧を取り出すことができる。

Terminal

aws cloud9 describe-environments \--environment-ids$(aws cloud9 list-environments --query "environmentIds" --output text)\--query"environments[].[id, name]"\--output text

Result

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa        AWS Management Console
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb        Terraform
cccccccccccccccccccccccccccccccc        AWS CLI

membership を追加する

membership の追加には create-environment-membershipサブコマンドを使う。ここでは例として IAM ユーザである aliceread-onlyで追加する。

Terminal

aws cloud9 create-environment-membership \--environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \--user-arn arn:aws:iam::000000000000:user/alice \--permissions read-only

Result

{"membership": {"permissions": "read-only",
        "userId": "YYYYYYYYYYYYYYYYYYYYY",
        "userArn": "arn:aws:iam::000000000000:user/alice",
        "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    }}

describe-environment-membershipsで確認すると aliceread-onlyで追加されていることがわかる。

Terminal

aws cloud9 describe-environment-memberships \--environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Result

{"memberships": [{"permissions": "read-only",
            "userId": "YYYYYYYYYYYYYYYYYYYYY",
            "userArn": "arn:aws:iam::000000000000:user/alice",
            "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        },
        {"permissions": "owner",
            "userId": "XXXXXXXXXXXXXXXXXXXXX:john",
            "userArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john",
            "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "lastAccess": 1562920538.0}]}

membership を更新する

membership の更新には update-environment-membershipサブコマンドを使う。

ここでは上で追加した aliceの権限を read-onlyから read-writeに変更する。

Terminal

aws cloud9 update-environment-membership \--environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \--user-arn arn:aws:iam::000000000000:user/alice \--permissions read-write

Result

{"membership": {"permissions": "read-write",
        "userId": "YYYYYYYYYYYYYYYYYYYYY",
        "userArn": "arn:aws:iam::000000000000:user/alice",
        "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    }}

再び describe-environment-membershipsで確認すると aliceの権限が read-writeに変更されていることがわかる。

Terminal

aws cloud9 describe-environment-memberships \--environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Result

{"memberships": [{"permissions": "read-write",
            "userId": "YYYYYYYYYYYYYYYYYYYYY",
            "userArn": "arn:aws:iam::000000000000:user/alice",
            "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        },
        {"permissions": "owner",
            "userId": "XXXXXXXXXXXXXXXXXXXXX:john",
            "userArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john",
            "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "lastAccess": 1562920538.0}]}

membership を削除する

membership の削除には delete-environment-membershipサブコマンドを使う。

成功した場合は何も表示されない。

Terminal

aws cloud9 delete-environment-membership \--environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \--user-arn arn:aws:iam::000000000000:user/alice

AccessDeniedException の例

先に説明したように AWSマネジメントコンソールで Cloud9 environment を作成した場合、AWSCLIからの操作には DeleteEnvironmentMembership権限が無いためエラーになる。

Result

An error occurred (AccessDeniedException) when calling the DeleteEnvironmentMembership operation:
User arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/botocore-session-1562923366
is not authorized to perform: cloud9:DeleteEnvironmentMembership on resource: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Cloud9 と Terraform

Terraform v0.12.1

Terraform で Cloud9 を扱う場合は aws_cloud9_environment_ec2リソースを使用する。現状では Cloud9 に関連するリソースはこれしか無い。

aws_cloud9_environment_ec2 を使うときの注意点

Cloud9 envionment の membership の説明に書いたとおり、owner_arnの指定を忘れると membership の操作ができないばかりか Cloud9 IDEに入ることすらできないので(使い方によるが)指定しておいた方が無難。

aws_cloud9_environment_ec2に変更を加えた場合、大抵は destroy される。automatic_stop_time_minutesは modify になりそうだが、これも destroy の対象なので自動シャットダウンの時間を変更したければ Cloud9 IDEから変更する必要がある。

modify になるもの

  • nameを変更する
  • descriptionを変更する

destroy になるもの

  • instance_typeを変更する
  • automatic_stop_time_minutesを設定(never から 30 など)または変更(30 から 60 など)する
  • owner_arnを設定または変更する

Terraform による Cloud9 environment の作成

nameinstance_typeだけ指定すれば Cloud9 environment を作成することができる。

aws_cloud9.tf

resource"aws_cloud9_environment_ec2""foo" {// STEP 1name          = "Terraform"
  instance_type = "t2.micro"
}

Terminal

terraform apply --target aws_cloud9_environment_ec2.foo
terraform state show     aws_cloud9_environment_ec2.foo

owner_arnにはユーザ名としてタイムスタンプが設定される。

# aws_cloud9_environment_ec2.foo: 
resource"aws_cloud9_environment_ec2""foo" {arn           = "arn:aws:cloud9:ap-northeast-1:000000000000:environment:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    id            = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    instance_type = "t2.micro"
    name          = "foo"
    owner_arn     = "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/1562844915108062971"
    type          = "ec2"
}

STEP 2: owner_arn の指定

先程の TF ファイルを変更して owner_arnを追記する。Terraform に限らずだが owner の変更は出来ないため同一名の Cloud9 environment を作成する場合は再構築する必要がある。

aws_cloud9.tf

resource"aws_cloud9_environment_ec2""foo" {// STEP 1name          = "foo"
  instance_type = "t2.micro"
  // STEP 2
  owner_arn     = "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john"
}

Terminal

terraform apply --target aws_cloud9_environment_ec2.foo
terraform state show     aws_cloud9_environment_ec2.foo

terraform state showで確認すると owner_arnが指定したとおりに設定されているのがわかる。

# aws_cloud9_environment_ec2.foo: 
resource"aws_cloud9_environment_ec2""foo" {arn           = "arn:aws:cloud9:ap-northeast-1:000000000000:environment:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
    id            = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
    instance_type = "t2.micro"
    name          = "foo"
    owner_arn     = "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john"
    type          = "ec2"
}

STEP 3: automatic_stop_time_minutes を指定する

Cloud9 ではインアクティブな状態が続いたときに自動的に EC2 インスタンスを停止させることができる機能が用意されている。aws_cloud9_environment_ec2リソースでは automatic_stop_time_minutesという引数(数値型)で指定する。未指定の場合は AWSCLI同様に never(停止しない)となる。destroy が発生するので「時間を変更したいけれど Cloud9 environment は壊したくない」という場合は IDEを開いて「AWS Cloud9 > Pereferences > PROJECT SETTINGS > EC2 Instance」で設定を変更する。なお、AWSマネジメントコンソールからは変更できない。

aws_cloud9.tf

resource"aws_cloud9_environment_ec2""foo" {// STEP 1name                        = "foo"
  instance_type               = "t2.micro"
  // STEP 2
  owner_arn                   = "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john"
  // STEP 3
  automatic_stop_time_minutes = 30}

Terminal

terraform apply --target aws_cloud9_environment_ec2.foo
terraform state show     aws_cloud9_environment_ec2.foo

指定した場合は下記のように属性と値が表示される。Q & A に書いておくが設定によっては IDE上で確認できる値と一致しなくなる。指定しなかった場合は自動的に neverとなり automatic_stop_time_minutes属性は出てこない。

Result

# aws_cloud9_environment_ec2.foo: 
resource"aws_cloud9_environment_ec2""foo" {arn                         = "arn:aws:cloud9:ap-northeast-1:000000000000:environment:cccccccccccccccccccccccccccccccc"
    automatic_stop_time_minutes = 30
    id                          = "cccccccccccccccccccccccccccccccc"
    instance_type               = "t2.nano"
    name                        = "bar"
    owner_arn                   = "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john"
    type                        = "ec2"
}

EIP の設定やセキュリティグループのカスタマイズの自動化はできるのか

Cloud9 envionment には EIP の設定が無いので自動シャットダウンが行われればパブリック IP アドレスは変わってしまうし、セキュリティグループは自動作成以外の選択肢が無いのでインバウンドルールを追加しないと本来の「踏み台」としては使えない。

しかし、Cloud9 で EC2 インスタンスを作成してもステータスには EC2 インスタンスの詳細情報は含まれていない。これは AWSCLIを使っても同じである。EC2 インスタンスやセキュリティグループは CloudFormation によって作成されているので Nameタグには aws-cloud9-環境名-環境IDという書式で名前が設定されている。CloudFormation の aws:cloud9:environmentタグにも Cloud9 environment の ID が入っているのでここからリソースを検索することもできるだろう。しかし、CloudFormation のスタックが Terraform の管理外で作成されているので変数呼び出しができない。

EC2 インスタンスの作成を通常通り行い、そちらで Cloud9 IDEのインストールと EIP やセキュリティグループを設定したあとに Cloud9 に SSHタイプで追加すれば…という方法も思いつくが残念ながらいまのところ Terraform には aws_cloud9_environment_sshというリソースは存在しない。

従って、Cloud9 で EC2 インスタンスを新規に作成する場合は「踏み台」として使うための設定の自動化は簡単ではないと思われる。

Try & Result

Try: owner_arn を指定し忘れたのだが Cloud9 IDEを使いたい

AWSCLIcloud9 create-environment-membershipで使用するユーザを membership に追加すればいい。ただし、Cloud9 IDEに membership の設定が表示されず GUIから membership の編集や削除ができないなどの制限があるので潔く作り直したほうがよい。

Try: automatic_stop_time_minutes に適当な値(たとえば 45 など)を設定するとどうなるか

terraform showでは設定した通りの値が入っているが Cloud9 IDEで確認してみると切り上げられてプリセット値に設定されていた。(45 の場合であれば 1 時間)

Try: automatic_stop_time_minutes に明示的に neverを指定するにはどうするのか

数値型なので automatic_stop_time_minutes = 0と指定すれば neverになる。

まとめ

一般的な「踏み台」として使わずに Cloud9 IDESSHログイン用のスポットとして使う分にはいいと思うが、リモートサーバにログインするための秘密鍵を配置する必要があるなどいくつかの手間は残る。

個人的にはシェルのキーバインドを多用するので Ctrl + W(1単語前を消す)なんて押そうものならタブが落ちてしまうため Cloud9 IDE上のターミナルは少々使いにくかったりする。

ただ、フロントの開発をやっているエンジニアさん相手には Proxy Jump の説明をするのも一苦労だったりするので AWSアカウントだけあれば使えるというのは便利なのかもしれない。

他のプロジェクトが Cloud9 をどのように SSHの踏み台として使っているのかは知らないがベストプラクティス的なものが教えてもらいたいものである。


Viewing all articles
Browse latest Browse all 893

Trending Articles