Merge "Add ability to set user password in uamlite"

This commit is contained in:
Craig Anderson 2018-05-10 16:34:44 +00:00 committed by Gerrit Code Review
commit fcd97ef51c
6 changed files with 170 additions and 36 deletions

View File

@ -152,9 +152,9 @@ WantedBy=multi-user.target"
{{- range $iface, $unused := .Values.conf.ethtool }}
{{- range $ethtool_key, $ethtool_val := . }}
device={{ $iface | quote }} \
user_key={{ $ethtool_key | quote }} \
user_val={{ $ethtool_val | quote }} \
device={{ $iface | squote }} \
user_key={{ $ethtool_key | squote }} \
user_val={{ $ethtool_val | squote }} \
add_ethtool_param
{{- end }}
{{- end }}

View File

@ -107,7 +107,7 @@ WantedBy=local-fs.target"
{{- range .Values.conf.mounts }}
{{- range $key, $value := . }}
{{ $key }}={{ $value | quote }} \
{{ $key }}={{ $value | squote }} \
{{- end }}
add_mounts_param
{{- end }}

View File

@ -90,7 +90,7 @@ add_sysctl_param(){
}
{{- range $key, $value := .Values.conf.sysctl }}
add_sysctl_param {{ $key | quote }} {{ $value | quote }}
add_sysctl_param {{ $key | squote }} {{ $value | squote }}
{{- end }}
# Revert any previously applied sysctl settings which are now absent

View File

@ -27,6 +27,7 @@ builtin_acct='ubuntu'
add_user(){
die_if_null "${user_name}" ", 'user_name' env var not initialized"
: ${user_sudo:=false}
: ${user_crypt_passwd:=*}
# Create user if user does not already exist
getent passwd ${user_name} && \
@ -41,10 +42,31 @@ add_user(){
log.INFO "User '${user_name}' has been unexpired"
fi
# Exclude case where user should not have a password set
if [ "${user_crypt_passwd}" != '*' ]; then
local user_has_passwd=true
fi
# Set user password if current password does not match desired password
local crypt_passwd="$(getent shadow ${user_name} | cut -d':' -f2)"
if [ "${crypt_passwd}" != "${user_crypt_passwd}" ]; then
usermod -p "${user_crypt_passwd}" ${user_name}
if [ "${user_has_passwd}" = 'true' ]; then
log.INFO "User '${user_name}' password set successfully"
else
log.INFO "User '${user_name}' password removed successfully"
fi
else
if [ "${user_has_passwd}" = 'true' ]; then
log.INFO "No change required to password for user '${user_name}'"
else
log.INFO "User '${user_name}' has no password, and none was requested"
fi
fi
# Add sudoers entry if requested for user
if [ "${user_sudo}" = 'true' ]; then
# Add sudoers entry if it does not already exist
user_sudo_file=/etc/sudoers.d/${keyword}-${user_name}-sudo
local user_sudo_file=/etc/sudoers.d/${keyword}-${user_name}-sudo
if [ -f "${user_sudo_file}" ] ; then
log.INFO "User '${user_name}' already added to sudoers: ${user_sudo_file}"
else
@ -56,15 +78,21 @@ add_user(){
log.INFO "User '${user_name}' was not requested sudo access"
fi
if [ "${user_has_passwd}" = "true" ] && \
[ "${user_sudo}" = "true" ] && \
[ "${user_name}" != "${builtin_acct}" ]; then
expire_builtin_acct_passwd_vote=true
fi
curr_userlist="${curr_userlist}${user_name}"$'\n'
}
add_sshkeys(){
die_if_null "${user_name}" ", 'user_name' env var not initialized"
user_sshkeys="$@"
local user_sshkeys="$@"
sshkey_dir="/home/${user_name}/.ssh"
sshkey_file="${sshkey_dir}/authorized_keys"
local sshkey_dir="/home/${user_name}/.ssh"
local sshkey_file="${sshkey_dir}/authorized_keys"
if [ -z "${user_sshkeys}" ]; then
log.INFO "User '${user_name}' has no SSH keys defined"
if [ -f "${sshkey_file}" ]; then
@ -72,11 +100,11 @@ add_sshkeys(){
log.INFO "User '${user_name}' has had its authorized_keys file wiped"
fi
else
sshkey_file_contents='# NOTE: This file is managed by divingbell'$'\n'
local sshkey_file_contents='# NOTE: This file is managed by divingbell'$'\n'
for sshkey in "$@"; do
sshkey_file_contents="${sshkey_file_contents}${sshkey}"$'\n'
done
write_file=false
local write_file=false
if [ -f "${sshkey_file}" ]; then
if [ "$(cat "${sshkey_file}")" = \
"$(echo "${sshkey_file_contents}" | head -n-1)" ]; then
@ -98,33 +126,44 @@ add_sshkeys(){
# In the event that the user specifies ssh keys for the built-in account and
# no others, do not expire the built-in account
if [ "${user_name}" != "${builtin_acct}" ]; then
expire_builtin_acct=true
if [ "${user_sudo}" = "true" ] && \
[ "${user_name}" != "${builtin_acct}" ]; then
expire_builtin_acct_ssh_vote=true
fi
fi
}
{{- if hasKey .Values.conf "uamlite" }}
{{- if hasKey .Values.conf.uamlite "purge_expired_users" }}
purge_expired_users={{ .Values.conf.uamlite.purge_expired_users | quote }}
purge_expired_users={{ .Values.conf.uamlite.purge_expired_users | squote }}
{{- end }}
{{- if hasKey .Values.conf.uamlite "users" }}
{{- range $item := .Values.conf.uamlite.users }}
{{- range $key, $value := . }}
{{ $key }}={{ $value | quote }} \
{{- if eq $key "user_crypt_passwd" }}
{{/* supported crypt types are 2a (blowfish), 1 (md5), 5 (sha-256), and 6 (sha-512) */}}
{{- if not (or (regexMatch "\\$2a\\$.*\\$.*" $value) (regexMatch "\\$[156]\\$.*\\$.*" $value)) }}
{{- fail (print "BAD PASSWORD FOR '" $item.user_name "': The 'user_crypt_passwd' specified for '" $item.user_name "' does not pass regex checks. Ensure that the supplied user password is encoded per divingbell documentation at https://divingbell.readthedocs.io/en/latest/#uamlite") }}
{{- end }}
{{- end }}
{{ $key }}={{ $value | squote }} \
{{- end }}
add_user
{{- range $key, $value := . }}
{{ $key }}={{ $value | quote }} \
{{ $key }}={{ $value | squote }} \
{{- end }}
add_sshkeys {{ range $ssh_key := .user_sshkeys }}{{ $ssh_key | quote }} {{end}}
{{- if hasKey . "user_sshkeys" }}
{{- if not (eq (first .user_sshkeys) "Unmanaged") }}
add_sshkeys {{ range $ssh_key := .user_sshkeys }}{{ if not (or (regexMatch "ssh-dss .*" $ssh_key) (regexMatch "ecdsa-.*" $ssh_key) (regexMatch "ssh-ed25519 .*" $ssh_key) (regexMatch "ssh-rsa .*" $ssh_key)) }}{{ fail (print "BAD SSH KEY FOR '" $item.user_name "': One of the 'user_sshkeys' specified for '" $item.user_name "' does not pass regex checks: '" $ssh_key "'. Ensure that the supplied user SSH keys are supported/formatted per divingbell documentation at https://divingbell.readthedocs.io/en/latest/#uamlite") }}{{ else }}{{ $ssh_key | squote }}{{ end }} {{ end }}
{{- end }}
{{- else }}
add_sshkeys
{{- end }}
{{- end }}
{{- end }}
{{- end }}
# TODO: This should be done before applying new settings rather than after
# Expire any previously defined users that are no longer defined
if [ -n "$(getent passwd | grep ${keyword} | cut -d':' -f1)" ]; then
users="$(getent passwd | grep ${keyword} | cut -d':' -f1)"
@ -163,7 +202,8 @@ fi
if [ -n "${builtin_acct}" ] && [ -n "$(getent passwd ${builtin_acct})" ]; then
# Disable built-in account as long as there was at least one account defined
# in this chart with a ssh key present
if [ "${expire_builtin_acct}" = "true" ]; then
if [ "${expire_builtin_acct_passwd_vote}" = "true" ] && \
[ "${expire_builtin_acct_ssh_vote}" = "true" ]; then
if [ "$(chage -l ${builtin_acct} | grep 'Account expires' | cut -d':' -f2 |
tr -d '[:space:]')" = "never" ]; then
usermod --expiredate 1 ${builtin_acct}

View File

@ -41,6 +41,7 @@ USERNAME2_SUDO=false
USERNAME2_SSHKEY1="ssh-rsa xyz456 comment"
USERNAME2_SSHKEY2="ssh-rsa qwe789 comment"
USERNAME2_SSHKEY3="ssh-rsa rfv000 comment"
USERNAME2_CRYPT_PASSWD='$6$AF.NLpphOJjMVTYC$GD6wyUTy9vIgatoMbtTDYcVtEJqh/Mrx3BRetVstMsNodSyn3ZFIZOMRePpRpGbFArnAxgkL1PtQxsZHCgtFn/'
USERNAME3=userthree
USERNAME3_SUDO=true
USERNAME4=userfour
@ -556,6 +557,16 @@ _test_ssh_keys(){
fi
}
_test_user_passwd(){
username=$1
crypt_passwd="$2"
if [ "$crypt_passwd" != "$(getent shadow $username | cut -d':' -f2)" ]; then
echo "Error: User '$username' passwd did not match expected val '$crypt_passwd'"
return 1
fi
}
test_uamlite(){
# Test the first set of values
local overrides_yaml=${LOGS_SUBDIR}/${FUNCNAME}-set1.yaml
@ -568,6 +579,7 @@ test_uamlite(){
- ${USERNAME1_SSHKEY1}
- user_name: ${USERNAME2}
user_sudo: ${USERNAME2_SUDO}
user_crypt_passwd: ${USERNAME2_CRYPT_PASSWD}
user_sshkeys:
- ${USERNAME2_SSHKEY1}
- ${USERNAME2_SSHKEY2}
@ -580,17 +592,21 @@ test_uamlite(){
_test_user_enabled ${USERNAME1} true
_test_sudo_enabled ${USERNAME1} ${USERNAME1_SUDO}
_test_ssh_keys ${USERNAME1} "${USERNAME1_SSHKEY1}"
_test_user_passwd ${USERNAME1} '*'
_test_user_enabled ${USERNAME2} true
_test_sudo_enabled ${USERNAME2} ${USERNAME2_SUDO}
_test_ssh_keys ${USERNAME2} "${USERNAME2_SSHKEY1}"
_test_ssh_keys ${USERNAME2} "${USERNAME2_SSHKEY2}"
_test_ssh_keys ${USERNAME2} "${USERNAME2_SSHKEY3}"
_test_user_passwd ${USERNAME2} ${USERNAME2_CRYPT_PASSWD}
_test_user_enabled ${USERNAME3} true
_test_sudo_enabled ${USERNAME3} ${USERNAME3_SUDO}
_test_ssh_keys ${USERNAME3} false
_test_user_passwd ${USERNAME3} '*'
_test_user_enabled ${USERNAME4} true
_test_sudo_enabled ${USERNAME4} ${USERNAME4_SUDO}
_test_ssh_keys ${USERNAME4} false
_test_user_passwd ${USERNAME4} '*'
echo '[SUCCESS] uamlite test1 passed successfully' >> "${TEST_RESULTS}"
# Test an updated set of values
@ -619,17 +635,21 @@ test_uamlite(){
_test_user_enabled ${USERNAME1} true
_test_sudo_enabled ${USERNAME1} ${uname1_sudo}
_test_ssh_keys ${USERNAME1} false
_test_user_passwd ${USERNAME1} '*'
_test_user_enabled ${USERNAME2} true
_test_sudo_enabled ${USERNAME2} ${uname2_sudo}
_test_ssh_keys ${USERNAME2} "${USERNAME2_SSHKEY1}"
_test_ssh_keys ${USERNAME2} "${USERNAME2_SSHKEY2}"
_test_user_passwd ${USERNAME2} '*'
_test_user_enabled ${USERNAME3} true
_test_sudo_enabled ${USERNAME3} ${uname3_sudo}
_test_ssh_keys ${USERNAME3} "${USERNAME1_SSHKEY1}"
_test_ssh_keys ${USERNAME3} "${USERNAME2_SSHKEY3}"
_test_user_passwd ${USERNAME3} '*'
_test_user_enabled ${USERNAME4} true
_test_sudo_enabled ${USERNAME4} ${USERNAME4_SUDO}
_test_ssh_keys ${USERNAME4} false
_test_user_passwd ${USERNAME4} '*'
echo '[SUCCESS] uamlite test2 passed successfully' >> "${TEST_RESULTS}"
# Test revert/rollback functionality
@ -646,7 +666,7 @@ test_uamlite(){
echo '[SUCCESS] uamlite test3 passed successfully' >> "${TEST_RESULTS}"
# Test purge users flag
overrides_yaml=${LOGS_SUBDIR}/${FUNCNAME}-set3.yaml
overrides_yaml=${LOGS_SUBDIR}/${FUNCNAME}-set4.yaml
echo "conf:
uamlite:
purge_expired_users: true" > "${overrides_yaml}"
@ -657,6 +677,33 @@ test_uamlite(){
_test_user_purged ${USERNAME3}
_test_user_purged ${USERNAME4}
echo '[SUCCESS] uamlite test4 passed successfully' >> "${TEST_RESULTS}"
# Test invalid password
overrides_yaml=${LOGS_SUBDIR}/${FUNCNAME}-set5.yaml
user2_crypt_passwd_invalid='plaintextPassword'
echo "conf:
uamlite:
users:
- user_name: ${USERNAME2}
user_crypt_passwd: ${user2_crypt_passwd_invalid}" > "${overrides_yaml}"
install_base "--values=${overrides_yaml}" 2>&1 | grep 'BAD PASSWORD' || \
(echo "[FAIL] uamlite test5 did not receive expected 'BAD PASSWORD' error" && exit 1)
echo '[SUCCESS] uamlite test5 passed successfully' >> "${TEST_RESULTS}"
# Test invalid SSH key
overrides_yaml=${LOGS_SUBDIR}/${FUNCNAME}-set6.yaml
user2_bad_sshkey='AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmT key-comment'
echo "conf:
uamlite:
users:
- user_name: ${USERNAME2}
user_sshkeys:
- ${USERNAME2_SSHKEY1}
- ${user2_bad_sshkey}
- ${USERNAME2_SSHKEY3}" > "${overrides_yaml}"
install_base "--values=${overrides_yaml}" 2>&1 | grep 'BAD SSH KEY' || \
(echo "[FAIL] uamlite test6 did not receive expected 'BAD SSH KEY' error" && exit 1)
echo '[SUCCESS] uamlite test6 passed successfully' >> "${TEST_RESULTS}"
}
# test daemonset value overrides for hosts and labels

View File

@ -17,9 +17,6 @@
Divingbell
==========
What is it?
-----------
Divingbell is a lightweight solution for:
1. Bare metal configuration management for a few very targeted use cases
2. Bare metal package manager orchestration
@ -66,10 +63,6 @@ fashion: the idempotent automation for each daemonset will only re-run when
Armada spawns/respawns the container, or if information relevant to the host
changes in the configmap.
For upgrades, a decision was taken not to use any of the built-in Kubernetes
update strategies such as RollingUpdate. Instead, we are putting this on
Armada to handle the orchestration of how to do upgrades (e.g., rack by rack).
Daemonset configs
-----------------
@ -123,19 +116,73 @@ access. Ex::
purge_expired_users: false
users:
- user_name: testuser
user_sudo: True
user_crypt_passwd: $6$...
user_sudo: true
user_sshkeys:
- ssh-rsa AAAAB3N... key1-comment
- ssh-rsa AAAAVY6... key2-comment
An update to the chart with revmoed users will result in those user's accounts
being expired, preventing those users any access through those accounts. This
does not delete their home directory or any other files, and provides UID
consistency in the event the same account gets re-added later, and they regain
access to their files again.
Setting user passwords
""""""""""""""""""""""
However, if it is desired to purge expired and removed accounts and their home
directories, this may be done by the ``purge_expired_users`` option to ``true``.
Including ``user_crypt_passwd`` to set a user password is optional.
If setting a password for the user, the chart expects the password to be
encrypted with SHA-512 and formatted in the way that ``crypt`` library expects.
Run the following command to generate the needed encrypted password from the
plaintext password::
python3 -c "from getpass import getpass; from crypt import *; p=getpass(); print('\n'+crypt(p, METHOD_SHA512)) if p==getpass('Please repeat: ') else print('\nPassword mismatch.')"
Use the output of the above command as the ``user_crypt_passwd`` for the user.
(Credit to `unix.stackexchange.com <https://unix.stackexchange.com/questions/81240/manually-generate-password-for-etc-shadow>`_.)
If the password is not formatted how crypt expects, the chart will throw an
error and fail to render.
At least one user must be defined with a password and sudo in order for the
built-in ``ubuntu`` account to be disabled. This is because in a situation where
network access is unavailable, console username/password access will be the only
login option.
Setting user sudo
"""""""""""""""""
Including ``user_sudo`` to set user sudo access is optional. The default value
is ``false``.
At least one user must be defined with sudo access in order for the built-in
``ubuntu`` account to be disabled.
SSH keys
""""""""
Including ``user_sshkeys`` for defining one or more user SSH keys is optional.
The chart will throw an error and fail to render if the SSH key is not one of
the following formats:
- dsa (ssh-dss ...)
- ecdsa (ecdsa-...)
- ed25519 (ssh-ed25519 ...)
- rsa (ssh-rsa ...)
Setting ``user_sshkeys`` to ``[ Unmanaged ]`` will instruct divingbell not to
manage the user's authorized_keys file.
At least one user must be defined with an SSH key and sudo in order for the
built-in ``ubuntu`` account to be disabled.
Purging expired users
"""""""""""""""""""""
Including the ``purge_expired_users`` key-value pair is optional. The default
value is ``false``.
This option must be set to ``true`` if it is desired to purge expired accounts
and remove their home directories. Otherwise, removed accounts are expired (so
users cannot login) but their home directories remain intact, in order to
maintain UID consistency (in the event the same accounts gets re-added later,
they regain access to their home directory files without UID mismatching).
Node specific configurations
----------------------------