Created
February 11, 2016 07:20
-
-
Save noda-sin/025060a4f9a071444477 to your computer and use it in GitHub Desktop.
bastion with managing iam
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"AWSTemplateFormatVersion": "2010-09-09", | |
"Description" : "Bastion Instance", | |
"Parameters" : { | |
"KeyName" : { | |
"Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instances", | |
"Type" : "AWS::EC2::KeyPair::KeyName", | |
"ConstraintDescription" : "can contain only alphanumeric characters, spaces, dashes and underscores." | |
}, | |
"InstanceType" : { | |
"Description" : "Instance type for node.", | |
"Type" : "String", | |
"Default" : "t2.nano", | |
"AllowedValues" : [ "t2.nano", "t2.micro","t2.small","t2.medium","m3.medium","m3.large","m3.xlarge","m3.2xlarge","c3.large","c3.xlarge","c3.2xlarge","c3.4xlarge","c3.8xlarge" ], | |
"ConstraintDescription" : "must be a valid T2, M3 or C3 instance type." | |
}, | |
"Subnets" : { | |
"Description" : "ID of your existing subnet for launching Bastion", | |
"Type" : "List<AWS::EC2::Subnet::Id>" | |
}, | |
"SSHSecurityGroup" : { | |
"Description" : "SecurityGroup for accessing with SSH", | |
"Type" : "AWS::EC2::SecurityGroup::Id" | |
}, | |
"ElasticIpId": { | |
"Description" : "ID of ElasticIp for launching Bastion", | |
"Type" : "String", | |
"AllowedPattern" : "eipalloc-[a-z0-9]{8}", | |
"ConstraintDescription" : "Input format is eipalloc-xxxxxxxx" | |
}, | |
"Recurrence" : { | |
"Type" : "String", | |
"Description" : "schedule for updating keys. crontab style syntax. ex) 0 0 * * *", | |
"AllowedPattern" : "(((([*])|(((([0-5])?[0-9])((-(([0-5])?[0-9])))?)))((/(([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?[0-9])))?))(,(((([*])|(((([0-5])?[0-9])((-(([0-5])?[0-9])))?)))((/(([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?[0-9])))?)))* (((([*])|(((((([0-1])?[0-9]))|(([2][0-3])))((-(((([0-1])?[0-9]))|(([2][0-3])))))?)))((/(([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?[0-9])))?))(,(((([*])|(((((([0-1])?[0-9]))|(([2][0-3])))((-(((([0-1])?[0-9]))|(([2][0-3])))))?)))((/(([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?[0-9])))?)))* (((((((([*])|(((((([1-2])?[0-9]))|(([3][0-1]))|(([1-9])))((-(((([1-2])?[0-9]))|(([3][0-1]))|(([1-9])))))?)))((/(([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?[0-9])))?))|(L)|(((((([1-2])?[0-9]))|(([3][0-1]))|(([1-9])))W))))(,(((((([*])|(((((([1-2])?[0-9]))|(([3][0-1]))|(([1-9])))((-(((([1-2])?[0-9]))|(([3][0-1]))|(([1-9])))))?)))((/(([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?[0-9])))?))|(L)|(((((([1-2])?[0-9]))|(([3][0-1]))|(([1-9])))W)))))*)|([?])) (((([*])|((((([1-9]))|(([1][0-2])))((-((([1-9]))|(([1][0-2])))))?))|((((JAN)|(FEB)|(MAR)|(APR)|(MAY)|(JUN)|(JUL)|(AUG)|(SEP)|(OKT)|(NOV)|(DEC))((-((JAN)|(FEB)|(MAR)|(APR)|(MAY)|(JUN)|(JUL)|(AUG)|(SEP)|(OKT)|(NOV)|(DEC))))?)))((/(([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?[0-9])))?))(,(((([*])|((((([1-9]))|(([1][0-2])))((-((([1-9]))|(([1][0-2])))))?))|((((JAN)|(FEB)|(MAR)|(APR)|(MAY)|(JUN)|(JUL)|(AUG)|(SEP)|(OKT)|(NOV)|(DEC))((-((JAN)|(FEB)|(MAR)|(APR)|(MAY)|(JUN)|(JUL)|(AUG)|(SEP)|(OKT)|(NOV)|(DEC))))?)))((/(([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?[0-9])))?)))* (((((((([*])|((([0-6])((-([0-6])))?))|((((SUN)|(MON)|(TUE)|(WED)|(THU)|(FRI)|(SAT))((-((SUN)|(MON)|(TUE)|(WED)|(THU)|(FRI)|(SAT))))?)))((/(([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?[0-9])))?))|((([0-6])L))|(W)|(([#][1-5]))))(,(((((([*])|((([0-6])((-([0-6])))?))|((((SUN)|(MON)|(TUE)|(WED)|(THU)|(FRI)|(SAT))((-((SUN)|(MON)|(TUE)|(WED)|(THU)|(FRI)|(SAT))))?)))((/(([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?[0-9])))?))|((([0-6])L))|(W)|(([#][1-5])))))*)|([?]))((( (((([*])|((([1-2][0-9][0-9][0-9])((-([1-2][0-9][0-9][0-9])))?)))((/(([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?[0-9])))?))(,(((([*])|((([1-2][0-9][0-9][0-9])((-([1-2][0-9][0-9][0-9])))?)))((/(([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?[0-9])))?)))*))?)", | |
"Default" : "*/10 * * * *" | |
} | |
}, | |
"Mappings": { | |
"AWSBastionAMI" : { | |
"ap-northeast-1" : { "AMI" : "ami-383c1956" } | |
} | |
}, | |
"Resources" : { | |
"BastionRole": { | |
"Type": "AWS::IAM::Role", | |
"Properties": { | |
"AssumeRolePolicyDocument": { | |
"Statement": [ { | |
"Effect": "Allow", | |
"Principal": { | |
"Service": [ "ec2.amazonaws.com" ] | |
}, | |
"Action": [ "sts:AssumeRole" ] | |
} ] | |
}, | |
"Path": "/", | |
"Policies": [ { | |
"PolicyName": "Bastion_Takeover", | |
"PolicyDocument": { | |
"Statement": [ { | |
"Effect": "Allow", | |
"Action": [ | |
"iam:ListUsers", | |
"iam:ListSshPublicKeys", | |
"iam:GetSshPublicKey", | |
"ec2:AssociateAddress" | |
], | |
"Resource": "*" | |
} ] | |
} | |
} ] | |
} | |
}, | |
"BastionRoleProfile": { | |
"Type": "AWS::IAM::InstanceProfile", | |
"Properties": { | |
"Path": "/", | |
"Roles": [ { | |
"Ref": "BastionRole" | |
} ] | |
} | |
}, | |
"BastionLaunchConfig" : { | |
"Type" : "AWS::AutoScaling::LaunchConfiguration", | |
"Metadata" : { | |
"AWS::CloudFormation::Init" : { | |
"config" : { | |
"packages" : { | |
"yum" : { | |
"jq" : [] | |
} | |
}, | |
"files" : { | |
"/etc/sshkey-update/cron.sh" : { | |
"content" : { "Fn::Join" : [ "\n", [ | |
"#!/bin/bash", | |
"PATH=$PATH:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin", | |
"function contains() {", | |
" for row in $1; do", | |
" if [ \"$row\" == \"$2\" ]; then", | |
" echo \"y\"", | |
" return 0", | |
" fi", | |
" done", | |
" echo \"n\"", | |
" return 1", | |
"}", | |
"# current users in host.", | |
"host_users=$(find /home -maxdepth 1 -type d | grep '^/home/' | sed -e 's/^\\/home\\/\\(.\\)/\\1/')", | |
"# create iam users", | |
"iam_users=$(aws iam list-users | jq -r '.Users[].UserName')", | |
"for iam_user in $iam_users; do", | |
" user_home=\"/home/$iam_user\"", | |
" user_ssh_dir=\"$user_home/.ssh\"", | |
" useradd \"$iam_user\"", | |
" # setup ssh directory", | |
" mkdir -p \"$user_ssh_dir\"", | |
" chown -R \"$iam_user:$iam_user\" \"$user_home\"", | |
" chmod -R 500 \"$user_home\"", | |
" # setup ssh key", | |
" touch \"$user_ssh_dir/authorized_keys\"", | |
" touch \"$user_ssh_dir/new_authorized_keys\"", | |
" key_ids=$(aws iam list-ssh-public-keys --user-name $iam_user | jq -r 'select(.SSHPublicKeys[].Status == \"Active\") | .SSHPublicKeys[].SSHPublicKeyId')", | |
" for key_id in $key_ids; do", | |
" aws iam get-ssh-public-key --user-name $iam_user --ssh-public-key-id $key_id --encoding SSH | jq -r '.SSHPublicKey.SSHPublicKeyBody' >> \"$user_ssh_dir/new_authorized_keys\"", | |
" done", | |
" keys_diff=$(diff \"$user_ssh_dir/authorized_keys\" \"$user_ssh_dir/new_authorized_keys\")", | |
" if [ \"$keys_diff\" == \"\" ]; then", | |
" rm -f \"$user_ssh_dir/new_authorized_keys\"", | |
" continue", | |
" fi", | |
" rm -f \"$user_ssh_dir/authorized_keys\"", | |
" mv \"$user_ssh_dir/new_authorized_keys\" \"$user_ssh_dir/authorized_keys\"", | |
" chown \"$iam_user:$iam_user\" \"$user_ssh_dir/authorized_keys\"", | |
" chmod 500 \"$user_ssh_dir/authorized_keys\"", | |
" ps aux | grep \"sshd: $iam_user@pts/\" | grep -v grep | awk '{ print \"kill -9\", $2 }' | sh", | |
"done", | |
"# delete iam user deleted by aws management console from host", | |
"for host_user in $host_users; do", | |
" # if ec2-user, not action", | |
" if [ \"$host_user\" = \"ec2-user\" ]; then", | |
" continue", | |
" fi", | |
" if [[ \"$(contains \"${iam_users[@]}\" \"$host_user\")\" == \"y\" ]]; then", | |
" continue", | |
" fi", | |
" ps aux | grep \"sshd: $host_user@pts/\" | grep -v grep | awk '{ print \"kill -9\", $2 }' | sh", | |
" userdel -r \"$host_user\"", | |
" rm -rf \"/home/$host_user\"", | |
" echo \"delete $host_user from host.\"", | |
"done" | |
]]}, | |
"mode" : "0755", | |
"owner" : "root", | |
"group" : "root" | |
} | |
} | |
} | |
} | |
}, | |
"Properties" : { | |
"InstanceType" : { "Ref" : "InstanceType" }, | |
"KeyName" : { "Ref" : "KeyName" }, | |
"ImageId" : { "Fn::FindInMap" : [ "AWSBastionAMI", { "Ref" : "AWS::Region" }, "AMI"] }, | |
"IamInstanceProfile" : { "Ref" : "BastionRoleProfile" }, | |
"AssociatePublicIpAddress" : "true", | |
"SecurityGroups" : [ { "Ref" : "SSHSecurityGroup" } ], | |
"UserData" : { "Fn::Base64" : { "Fn::Join" : [ "", [ | |
"#!/bin/bash\n", | |
"yum update -y\n", | |
"export AWS_DEFAULT_REGION=", { "Ref" : "AWS::Region" }, "\n", | |
"/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r BastionLaunchConfig --region ", { "Ref" : "AWS::Region" }, "\n", | |
"echo \"", { "Ref" : "Recurrence" }, " /bin/bash /etc/sshkey-update/cron.sh\" >> /tmp/$$.tmp\n", | |
"crontab /tmp/$$.tmp && rm -rf /tmp/$$.tmp\n", | |
"/bin/bash /etc/sshkey-update/cron.sh\n", | |
"instanceId=$(curl http://169.254.169.254/latest/meta-data/instance-id)\n", | |
"aws ec2 associate-address --instance-id $instanceId --allocation-id ", { "Ref" : "ElasticIpId" }, "\n" | |
]]} | |
} | |
} | |
}, | |
"BastionAutoScalingGroup" : { | |
"Type" : "AWS::AutoScaling::AutoScalingGroup", | |
"Properties" : { | |
"LaunchConfigurationName" : { "Ref" : "BastionLaunchConfig" }, | |
"MaxSize" : "1", | |
"MinSize" : "1", | |
"VPCZoneIdentifier" : { "Ref" : "Subnets" }, | |
"Tags" : [ | |
{ "Key" : "Name", "Value" : "bastion", "PropagateAtLaunch" : "true" } | |
] | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment