Skip to content
This repository was archived by the owner on Nov 8, 2021. It is now read-only.

Commit 5831d02

Browse files
mvanbaakmichaelwittig
authored andcommitted
Merge of my AssumeRole branch, and some other forks (#24)
* If defined, use IAM AssumeRole to fetch users and ssh keys from another AWS account * Untested: Fold the ability to use IAM AssumeRole in the showcase CF template * Import only specified IAM groups While trying to implement this feature looking at the many forks, I decided to mix the various implementations into something for ourselves. Split up the script in functions Created some meaningfull global variables * Change variable name so its the same as in the import_users.sh. Use read -r * Restore ability to import all users by leaving IAM_AUTHORIZED_GROUPS empty * Restore ability to specify an IAM group that should be added to sudo * Add assumerole to install.sh * Document how to setup cross account access * Use same markdown style as the README.md * Default to import all IAM users and update install.sh to explain how to limit the IAM groups to import * Make the LOCAL_GROUPS optional and document in install.sh * Handle iam users with a dash in the name * Remove users no longer in the IAM groups we give access to the instance * Handle the situation where a user is in more then one IAM group we want to sync * use full path to usermod binary * some more absolute paths to binaries that are normally not in the $PATH of cron * And use absolute path to groupadd as well since cron could not find it * fix typo * Allow usernames with a dash. Thanks for spotting this one @malytic * When checking the ssh key, make sure we transform the "save" username back to the original IAM "unsave" username. Fixes upstream issue #27
1 parent 17749fb commit 5831d02

6 files changed

+328
-60
lines changed

authorized_keys_command.sh

+21-1
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,32 @@ if [ -z "$1" ]; then
44
exit 1
55
fi
66

7+
# Assume a role before contacting AWS IAM to get users and keys.
8+
# This can be used if you define your users in one AWS account, while the EC2
9+
# instance you use this script runs in another.
10+
ASSUMEROLE=""
11+
12+
if [[ ! -z "${ASSUMEROLE}" ]]
13+
then
14+
STSCredentials=$(aws sts assume-role \
15+
--role-arn "${ASSUMEROLE}" \
16+
--role-session-name something \
17+
--query '[Credentials.SessionToken,Credentials.AccessKeyId,Credentials.SecretAccessKey]' \
18+
--output text)
19+
20+
AWS_ACCESS_KEY_ID=$(echo "${STSCredentials}" | awk '{print $2}')
21+
AWS_SECRET_ACCESS_KEY=$(echo "${STSCredentials}" | awk '{print $3}')
22+
AWS_SESSION_TOKEN=$(echo "${STSCredentials}" | awk '{print $1}')
23+
AWS_SECURITY_TOKEN=$(echo "${STSCredentials}" | awk '{print $1}')
24+
export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_SECURITY_TOKEN
25+
fi
26+
727
UnsaveUserName="$1"
828
UnsaveUserName=${UnsaveUserName//".plus."/"+"}
929
UnsaveUserName=${UnsaveUserName//".equal."/"="}
1030
UnsaveUserName=${UnsaveUserName//".comma."/","}
1131
UnsaveUserName=${UnsaveUserName//".at."/"@"}
1232

13-
aws iam list-ssh-public-keys --user-name "$UnsaveUserName" --query "SSHPublicKeys[?Status == 'Active'].[SSHPublicKeyId]" --output text | while read KeyId; do
33+
aws iam list-ssh-public-keys --user-name "$UnsaveUserName" --query "SSHPublicKeys[?Status == 'Active'].[SSHPublicKeyId]" --output text | while read -r KeyId; do
1434
aws iam get-ssh-public-key --user-name "$UnsaveUserName" --ssh-public-key-id "$KeyId" --encoding SSH --query "SSHPublicKey.SSHPublicKeyBody" --output text
1535
done

docs/multiawsaccount.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Use multiple AWS accounts
2+
3+
If you are using multiple AWS accounts (as you should be, read the best practices)
4+
you probably have one account with all the IAM users, and are using seperate accounts for
5+
your environments.
6+
Support for this is provided using the AssumeRole functionality in AWS.
7+
8+
## Setup IAM account with the IAM users
9+
10+
* Create a new role with type 'Role for Cross-Account Access'
11+
and select the option 'Provide access between AWS accounts you own'
12+
* Put the first 'ec2' account number in 'Account ID' and leave
13+
'Require MFA' unchecked
14+
* Skip attaching a policy (we will create our own later)
15+
* Review the new role and create it
16+
17+
Now we have to provide the correct access to this role.
18+
You can use the `iam_ssh_policy.json` as provided in the root of this repository
19+
20+
21+
## Setup IAM account with the ec2 instances
22+
23+
The EC2 role you use for launching the EC2 instances should have the policy
24+
as listed in `iam_crossaccount_policy.json` file. Replace the account id and role name
25+
in that file with the account id and role you created in the steps above.

iam_crossaccount_policy.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Version": "2012-10-17",
3+
"Statement": {
4+
"Effect": "Allow",
5+
"Action": "sts:AssumeRole",
6+
"Resource": "arn:aws:iam::<IAM ACCOUNT ID WITH IAM USERS>:role/<ROLE WITH IAM ACCESS>"
7+
}
8+
}

import_users.sh

+160-26
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,167 @@
11
#!/bin/bash
22

3+
# Which IAM groups have access to this instance
4+
# Comma seperated list of IAM groups. Leave empty for all available IAM users
5+
IAM_AUTHORIZED_GROUPS=""
6+
7+
# Special group to mark users as being synced by our script
8+
LOCAL_MARKER_GROUP="iam-synced-users"
9+
10+
# Give the users these local UNIX groups
11+
LOCAL_GROUPS=""
12+
313
# Specify an IAM group for users who should be given sudo privileges, or leave
414
# empty to not change sudo access, or give it the value '##ALL##' to have all
515
# users be given sudo rights.
6-
SudoersGroup=""
7-
[[ -z "${SudoersGroup}" ]] || [[ "${SudoersGroup}" == "##ALL##" ]] || Sudoers=$(
8-
aws iam get-group --group-name "${SudoersGroup}" --query "Users[].[UserName]" --output text
9-
);
10-
11-
aws iam list-users --query "Users[].[UserName]" --output text | while read User; do
12-
SaveUserName="$User"
13-
SaveUserName=${SaveUserName//"+"/".plus."}
14-
SaveUserName=${SaveUserName//"="/".equal."}
15-
SaveUserName=${SaveUserName//","/".comma."}
16-
SaveUserName=${SaveUserName//"@"/".at."}
17-
if ! grep "^$SaveUserName:" /etc/passwd > /dev/null; then
18-
/usr/sbin/useradd --create-home --shell /bin/bash "$SaveUserName"
19-
fi
20-
21-
if [[ ! -z "${SudoersGroup}" ]]; then
22-
# sudo will read each file in /etc/sudoers.d, skipping file names that end
23-
# in ‘~’ or contain a ‘.’ character to avoid causing problems with package
24-
# manager or editor temporary/backup files.
25-
SaveUserFileName=$(echo "$SaveUserName" | tr "." " ")
26-
SaveUserSudoFilePath="/etc/sudoers.d/$SaveUserFileName"
27-
if [[ "${SudoersGroup}" == "##ALL##" ]] || echo "$Sudoers" | grep "^$User\$" > /dev/null; then
28-
echo "$SaveUserName ALL=(ALL) NOPASSWD:ALL" > "$SaveUserSudoFilePath"
16+
SUDOERSGROUP=""
17+
18+
# Assume a role before contacting AWS IAM to get users and keys.
19+
# This can be used if you define your users in one AWS account, while the EC2
20+
# instance you use this script runs in another.
21+
ASSUMEROLE=""
22+
23+
function setup_aws_credentials() {
24+
local stscredentials
25+
if [[ ! -z "${ASSUMEROLE}" ]]
26+
then
27+
stscredentials=$(aws sts assume-role \
28+
--role-arn "${ASSUMEROLE}" \
29+
--role-session-name something \
30+
--query '[Credentials.SessionToken,Credentials.AccessKeyId,Credentials.SecretAccessKey]' \
31+
--output text)
32+
33+
AWS_ACCESS_KEY_ID=$(echo "${stscredentials}" | awk '{print $2}')
34+
AWS_SECRET_ACCESS_KEY=$(echo "${stscredentials}" | awk '{print $3}')
35+
AWS_SESSION_TOKEN=$(echo "${stscredentials}" | awk '{print $1}')
36+
AWS_SECURITY_TOKEN=$(echo "${stscredentials}" | awk '{print $1}')
37+
export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_SECURITY_TOKEN
38+
fi
39+
}
40+
41+
# Get all IAM users (optionally limited by IAM groups)
42+
function get_iam_users() {
43+
local group
44+
if [ -z "${IAM_AUTHORIZED_GROUPS}" ]
45+
then
46+
aws iam list-users \
47+
--query "Users[].[UserName]" \
48+
--output text \
49+
| sed "s/\r//g"
2950
else
30-
[[ ! -f "$SaveUserSudoFilePath" ]] || rm "$SaveUserSudoFilePath"
51+
for group in $(echo ${IAM_AUTHORIZED_GROUPS} | tr "," " "); do
52+
aws iam get-group \
53+
--group-name "${group}" \
54+
--query "Users[].[UserName]" \
55+
--output text \
56+
| sed "s/\r//g"
57+
done
3158
fi
32-
fi
33-
done
59+
}
60+
61+
# Get previously synced users
62+
function get_local_users() {
63+
/usr/bin/getent group ${LOCAL_MARKER_GROUP} \
64+
| cut -d : -f4- \
65+
| sed "s/,/ /g"
66+
}
67+
68+
function get_sudoers_users() {
69+
[[ -z "${SUDOERSGROUP}" ]] || [[ "${SUDOERSGROUP}" == "##ALL##" ]] ||
70+
aws iam get-group \
71+
--group-name "${SUDOERSGROUP}" \
72+
--query "Users[].[UserName]" \
73+
--output text
74+
}
75+
76+
# Create or update a local user based on info from the IAM group
77+
function create_or_update_local_user() {
78+
local iamusername
79+
local username
80+
local sudousers
81+
local localusergroups
82+
83+
iamusername="${1}"
84+
username="${2}"
85+
sudousers="${3}"
86+
localusergroups="${LOCAL_MARKER_GROUP}"
87+
88+
if [ ! -z "${LOCAL_GROUPS}" ]
89+
then
90+
localusergroups="${LOCAL_GROUPS},${LOCAL_MARKER_GROUP}"
91+
fi
92+
93+
id "${username}" >/dev/null 2>&1 \
94+
|| /usr/sbin/useradd --create-home --shell /bin/bash "${username}" \
95+
&& /bin/chown -R "${username}:${username}" "/home/${username}"
96+
/usr/sbin/usermod -G "${localusergroups}" "${username}"
97+
98+
# Should we add this user to sudo ?
99+
if [[ ! -z "${SUDOERSGROUP}" ]]
100+
then
101+
SaveUserFileName=$(echo "${username}" | tr "." " ")
102+
SaveUserSudoFilePath="/etc/sudoers.d/$SaveUserFileName"
103+
if [[ "${SUDOERSGROUP}" == "##ALL##" ]] || echo "${sudousers}" | grep "^${iamusername}\$" > /dev/null
104+
then
105+
echo "${SaveUserName} ALL=(ALL) NOPASSWD:ALL" > "${SaveUserSudoFilePath}"
106+
else
107+
[[ ! -f "${SaveUserSudoFilePath}" ]] || rm "${SaveUserSudoFilePath}"
108+
fi
109+
fi
110+
}
111+
112+
function delete_local_user() {
113+
/usr/sbin/usermod -L -s /sbin/nologin "${1}"
114+
/usr/bin/pkill -KILL -u "${1}"
115+
/usr/sbin/userdel -r "${1}"
116+
}
117+
118+
function clean_iam_username() {
119+
local clean_username="${1}"
120+
clean_username=${clean_username//"+"/".plus."}
121+
clean_username=${clean_username//"="/".equal."}
122+
clean_username=${clean_username//","/".comma."}
123+
clean_username=${clean_username//"@"/".at."}
124+
echo "${clean_username}"
125+
}
126+
127+
function sync_accounts() {
128+
if [ -z "${LOCAL_MARKER_GROUP}" ]
129+
then
130+
echo "Please specify a local group to mark imported users. eg iam-synced-users"
131+
exit 1
132+
fi
133+
134+
# Check if local marker group exists, if not, create it
135+
/usr/bin/getent group "${LOCAL_MARKER_GROUP}" >/dev/null 2>&1 || /usr/sbin/groupadd "${LOCAL_MARKER_GROUP}"
136+
137+
# setup the aws credentials if needed
138+
setup_aws_credentials
139+
140+
# declare and set some variables
141+
local iam_users
142+
local sudo_users
143+
local local_users
144+
local intersection
145+
local removed_users
146+
local user
147+
148+
iam_users=$(get_iam_users | sort | uniq)
149+
sudo_users=$(get_sudoers_users | sort | uniq)
150+
local_users=$(get_local_users | sort | uniq)
151+
152+
intersection=$(echo ${local_users} ${iam_users} | tr " " "\n" | sort | uniq -D | uniq)
153+
removed_users=$(echo ${local_users} ${intersection} | tr " " "\n" | sort | uniq -u)
154+
155+
# Add or update the users found in IAM
156+
for user in ${iam_users}; do
157+
SaveUserName=$(clean_iam_username "${user}")
158+
create_or_update_local_user "${user}" "${SaveUserName}" "$sudo_users"
159+
done
160+
161+
# Remove users no longer in the IAM group(s)
162+
for user in ${removed_users}; do
163+
delete_local_user "${user}"
164+
done
165+
}
166+
167+
sync_accounts

install.sh

+21-4
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
11
#!/bin/bash
22

3-
tmpdir=`mktemp -d`
3+
tmpdir=$(mktemp -d)
44

5-
cd $tmpdir
5+
cd "${tmpdir}" || exit 1
66

77
# yum install -y git # if necessary
88
# or download a tarball and decompress it instead
99
git clone https://github.com/widdix/aws-ec2-ssh.git
1010

11-
cd $tmpdir/aws-ec2-ssh
11+
cd "${tmpdir}/aws-ec2-ssh" || exit 1
1212

1313
cp authorized_keys_command.sh /opt/authorized_keys_command.sh
1414
cp import_users.sh /opt/import_users.sh
1515

16+
# To control which users are imported/synced, uncomment the line below
17+
# changing GROUPNAMES to a comma seperated list of IAM groups you want to sync.
18+
# You can specify 1 or more groups, comma seperated, without spaces.
19+
# If you leave it blank, all IAM users will be synced.
20+
#sudo sed -i 's/IAM_AUTHORIZED_GROUPS=""/IAM_AUTHORIZED_GROUPS="GROUPNAMES"/' /opt/import_users.sh
21+
1622
# To control which users are given sudo privileges, uncomment the line below
1723
# changing GROUPNAME to either the name of the IAM group for sudo users, or
1824
# to ##ALL## to give all users sudo access. If you leave it blank, no users will
1925
# be given sudo access.
20-
#sudo sed -i 's/SudoersGroup=""/SudoersGroup="GROUPNAME"/' /opt/import_users.sh
26+
#sudo sed -i 's/SUDOERSGROUP=""/SUDOERSGROUP="GROUPNAME"/' /opt/import_users.sh
27+
28+
# To control which local groups a user will get, uncomment the line belong
29+
# changing GROUPNAMES to a comma seperated list of local UNIX groups.
30+
# If you live it blank, this setting will be ignored
31+
#sudo sed -i 's/LOCAL_GROUPS=""/LOCAL_GROUPS="GROUPNAMES"/' /opt/import_users.sh
32+
33+
# If your IAM users are in another AWS account, put the AssumeRole ARN here.
34+
# replace the word ASSUMEROLEARN with the full arn. eg 'arn:aws:iam::$accountid:role/$role'
35+
# See docs/multiawsaccount.md on how to make this work
36+
#sudo sed -i 's/ASSUMEROLE=""/ASSUMEROLE="ASSUMEROLEARN"/' /opt/import_users.sh
37+
#sudo sed -i 's/ASSUMEROLE=""/ASSUMEROLE="ASSUMEROLEARN"/' /opt/authorized_keys_command.sh
2138

2239
sed -i 's:#AuthorizedKeysCommand none:AuthorizedKeysCommand /opt/authorized_keys_command.sh:g' /etc/ssh/sshd_config
2340
sed -i 's:#AuthorizedKeysCommandUser nobody:AuthorizedKeysCommandUser nobody:g' /etc/ssh/sshd_config

0 commit comments

Comments
 (0)