Browse Source

Add ability to set user password in uamlite

Change-Id: I4ecc556f02f973289a9dc019e2b73552f5d966fc
Craig Anderson 11 months ago
parent
commit
77be3585ba

+ 3
- 3
divingbell/templates/bin/_ethtool.sh.tpl View File

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

+ 1
- 1
divingbell/templates/bin/_mounts.sh.tpl View File

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

+ 1
- 1
divingbell/templates/bin/_sysctl.sh.tpl View File

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

+ 55
- 15
divingbell/templates/bin/_uamlite.sh.tpl View File

@@ -27,6 +27,7 @@ builtin_acct='ubuntu'
27 27
 add_user(){
28 28
   die_if_null "${user_name}" ", 'user_name' env var not initialized"
29 29
   : ${user_sudo:=false}
30
+  : ${user_crypt_passwd:=*}
30 31
 
31 32
   # Create user if user does not already exist
32 33
   getent passwd ${user_name} && \
@@ -41,10 +42,31 @@ add_user(){
41 42
     log.INFO "User '${user_name}' has been unexpired"
42 43
   fi
43 44
 
45
+  # Exclude case where user should not have a password set
46
+  if [ "${user_crypt_passwd}" != '*' ]; then
47
+    local user_has_passwd=true
48
+  fi
49
+  # Set user password if current password does not match desired password
50
+  local crypt_passwd="$(getent shadow ${user_name} | cut -d':' -f2)"
51
+  if [ "${crypt_passwd}" != "${user_crypt_passwd}" ]; then
52
+    usermod -p "${user_crypt_passwd}" ${user_name}
53
+    if [ "${user_has_passwd}" = 'true' ]; then
54
+      log.INFO "User '${user_name}' password set successfully"
55
+    else
56
+      log.INFO "User '${user_name}' password removed successfully"
57
+    fi
58
+  else
59
+    if [ "${user_has_passwd}" = 'true' ]; then
60
+      log.INFO "No change required to password for user '${user_name}'"
61
+    else
62
+      log.INFO "User '${user_name}' has no password, and none was requested"
63
+    fi
64
+  fi
65
+
44 66
   # Add sudoers entry if requested for user
45 67
   if [ "${user_sudo}" = 'true' ]; then
46 68
     # Add sudoers entry if it does not already exist
47
-    user_sudo_file=/etc/sudoers.d/${keyword}-${user_name}-sudo
69
+    local user_sudo_file=/etc/sudoers.d/${keyword}-${user_name}-sudo
48 70
     if [ -f "${user_sudo_file}" ] ; then
49 71
       log.INFO "User '${user_name}' already added to sudoers: ${user_sudo_file}"
50 72
     else
@@ -56,15 +78,21 @@ add_user(){
56 78
     log.INFO "User '${user_name}' was not requested sudo access"
57 79
   fi
58 80
 
81
+  if [ "${user_has_passwd}" = "true" ] && \
82
+     [ "${user_sudo}" = "true" ] && \
83
+     [ "${user_name}" != "${builtin_acct}" ]; then
84
+    expire_builtin_acct_passwd_vote=true
85
+  fi
86
+
59 87
   curr_userlist="${curr_userlist}${user_name}"$'\n'
60 88
 }
61 89
 
62 90
 add_sshkeys(){
63 91
   die_if_null "${user_name}" ", 'user_name' env var not initialized"
64
-  user_sshkeys="$@"
92
+  local user_sshkeys="$@"
65 93
 
66
-  sshkey_dir="/home/${user_name}/.ssh"
67
-  sshkey_file="${sshkey_dir}/authorized_keys"
94
+  local sshkey_dir="/home/${user_name}/.ssh"
95
+  local sshkey_file="${sshkey_dir}/authorized_keys"
68 96
   if [ -z "${user_sshkeys}" ]; then
69 97
     log.INFO "User '${user_name}' has no SSH keys defined"
70 98
     if [ -f "${sshkey_file}" ]; then
@@ -72,11 +100,11 @@ add_sshkeys(){
72 100
       log.INFO "User '${user_name}' has had its authorized_keys file wiped"
73 101
     fi
74 102
   else
75
-    sshkey_file_contents='# NOTE: This file is managed by divingbell'$'\n'
103
+    local sshkey_file_contents='# NOTE: This file is managed by divingbell'$'\n'
76 104
     for sshkey in "$@"; do
77 105
       sshkey_file_contents="${sshkey_file_contents}${sshkey}"$'\n'
78 106
     done
79
-    write_file=false
107
+    local write_file=false
80 108
     if [ -f "${sshkey_file}" ]; then
81 109
       if [ "$(cat "${sshkey_file}")" = \
82 110
            "$(echo "${sshkey_file_contents}" | head -n-1)" ]; then
@@ -98,33 +126,44 @@ add_sshkeys(){
98 126
 
99 127
     # In the event that the user specifies ssh keys for the built-in account and
100 128
     # no others, do not expire the built-in account
101
-    if [ "${user_name}" != "${builtin_acct}" ]; then
102
-      expire_builtin_acct=true
129
+    if [ "${user_sudo}" = "true" ] && \
130
+       [ "${user_name}" != "${builtin_acct}" ]; then
131
+      expire_builtin_acct_ssh_vote=true
103 132
     fi
104 133
   fi
105
-
106 134
 }
107 135
 
108 136
 {{- if hasKey .Values.conf "uamlite" }}
109 137
 {{- if hasKey .Values.conf.uamlite "purge_expired_users" }}
110
-purge_expired_users={{ .Values.conf.uamlite.purge_expired_users | quote }}
138
+purge_expired_users={{ .Values.conf.uamlite.purge_expired_users | squote }}
111 139
 {{- end }}
112 140
 {{- if hasKey .Values.conf.uamlite "users" }}
113 141
 {{- range $item := .Values.conf.uamlite.users }}
114 142
   {{- range $key, $value := . }}
115
-    {{ $key }}={{ $value | quote }} \
143
+    {{- if eq $key "user_crypt_passwd" }}
144
+      {{/* supported crypt types are 2a (blowfish), 1 (md5), 5 (sha-256), and 6 (sha-512) */}}
145
+      {{- if not (or (regexMatch "\\$2a\\$.*\\$.*" $value) (regexMatch "\\$[156]\\$.*\\$.*" $value)) }}
146
+        {{- 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") }}
147
+      {{- end }}
148
+    {{- end }}
149
+    {{ $key }}={{ $value | squote }} \
116 150
   {{- end }}
117 151
   add_user
118 152
 
119 153
   {{- range $key, $value := . }}
120
-    {{ $key }}={{ $value | quote }} \
154
+    {{ $key }}={{ $value | squote }} \
121 155
   {{- end }}
122
-  add_sshkeys {{ range $ssh_key := .user_sshkeys }}{{ $ssh_key | quote }} {{end}}
156
+  {{- if hasKey . "user_sshkeys" }}
157
+  {{- if not (eq (first .user_sshkeys) "Unmanaged") }}
158
+  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 }}
159
+{{- end }}
160
+{{- else }}
161
+  add_sshkeys
162
+{{- end }}
123 163
 {{- end }}
124 164
 {{- end }}
125 165
 {{- end }}
126 166
 
127
-# TODO: This should be done before applying new settings rather than after
128 167
 # Expire any previously defined users that are no longer defined
129 168
 if [ -n "$(getent passwd | grep ${keyword} | cut -d':' -f1)" ]; then
130 169
   users="$(getent passwd | grep ${keyword} | cut -d':' -f1)"
@@ -163,7 +202,8 @@ fi
163 202
 if [ -n "${builtin_acct}" ] && [ -n "$(getent passwd ${builtin_acct})" ]; then
164 203
   # Disable built-in account as long as there was at least one account defined
165 204
   # in this chart with a ssh key present
166
-  if [ "${expire_builtin_acct}" = "true" ]; then
205
+  if [ "${expire_builtin_acct_passwd_vote}" = "true" ] && \
206
+     [ "${expire_builtin_acct_ssh_vote}" = "true" ]; then
167 207
     if [ "$(chage -l ${builtin_acct} | grep 'Account expires' | cut -d':' -f2 |
168 208
           tr -d '[:space:]')" = "never" ]; then
169 209
       usermod --expiredate 1 ${builtin_acct}

+ 48
- 1
divingbell/tools/gate/test.sh View File

@@ -41,6 +41,7 @@ USERNAME2_SUDO=false
41 41
 USERNAME2_SSHKEY1="ssh-rsa xyz456 comment"
42 42
 USERNAME2_SSHKEY2="ssh-rsa qwe789 comment"
43 43
 USERNAME2_SSHKEY3="ssh-rsa rfv000 comment"
44
+USERNAME2_CRYPT_PASSWD='$6$AF.NLpphOJjMVTYC$GD6wyUTy9vIgatoMbtTDYcVtEJqh/Mrx3BRetVstMsNodSyn3ZFIZOMRePpRpGbFArnAxgkL1PtQxsZHCgtFn/'
44 45
 USERNAME3=userthree
45 46
 USERNAME3_SUDO=true
46 47
 USERNAME4=userfour
@@ -556,6 +557,16 @@ _test_ssh_keys(){
556 557
   fi
557 558
 }
558 559
 
560
+_test_user_passwd(){
561
+  username=$1
562
+  crypt_passwd="$2"
563
+
564
+  if [ "$crypt_passwd" != "$(getent shadow $username | cut -d':' -f2)" ]; then
565
+    echo "Error: User '$username' passwd did not match expected val '$crypt_passwd'"
566
+    return 1
567
+  fi
568
+}
569
+
559 570
 test_uamlite(){
560 571
   # Test the first set of values
561 572
   local overrides_yaml=${LOGS_SUBDIR}/${FUNCNAME}-set1.yaml
@@ -568,6 +579,7 @@ test_uamlite(){
568 579
       - ${USERNAME1_SSHKEY1}
569 580
     - user_name: ${USERNAME2}
570 581
       user_sudo: ${USERNAME2_SUDO}
582
+      user_crypt_passwd: ${USERNAME2_CRYPT_PASSWD}
571 583
       user_sshkeys:
572 584
       - ${USERNAME2_SSHKEY1}
573 585
       - ${USERNAME2_SSHKEY2}
@@ -580,17 +592,21 @@ test_uamlite(){
580 592
   _test_user_enabled ${USERNAME1} true
581 593
   _test_sudo_enabled ${USERNAME1} ${USERNAME1_SUDO}
582 594
   _test_ssh_keys     ${USERNAME1} "${USERNAME1_SSHKEY1}"
595
+  _test_user_passwd  ${USERNAME1} '*'
583 596
   _test_user_enabled ${USERNAME2} true
584 597
   _test_sudo_enabled ${USERNAME2} ${USERNAME2_SUDO}
585 598
   _test_ssh_keys     ${USERNAME2} "${USERNAME2_SSHKEY1}"
586 599
   _test_ssh_keys     ${USERNAME2} "${USERNAME2_SSHKEY2}"
587 600
   _test_ssh_keys     ${USERNAME2} "${USERNAME2_SSHKEY3}"
601
+  _test_user_passwd  ${USERNAME2} ${USERNAME2_CRYPT_PASSWD}
588 602
   _test_user_enabled ${USERNAME3} true
589 603
   _test_sudo_enabled ${USERNAME3} ${USERNAME3_SUDO}
590 604
   _test_ssh_keys     ${USERNAME3} false
605
+  _test_user_passwd  ${USERNAME3} '*'
591 606
   _test_user_enabled ${USERNAME4} true
592 607
   _test_sudo_enabled ${USERNAME4} ${USERNAME4_SUDO}
593 608
   _test_ssh_keys     ${USERNAME4} false
609
+  _test_user_passwd  ${USERNAME4} '*'
594 610
   echo '[SUCCESS] uamlite test1 passed successfully' >> "${TEST_RESULTS}"
595 611
 
596 612
   # Test an updated set of values
@@ -619,17 +635,21 @@ test_uamlite(){
619 635
   _test_user_enabled ${USERNAME1} true
620 636
   _test_sudo_enabled ${USERNAME1} ${uname1_sudo}
621 637
   _test_ssh_keys     ${USERNAME1} false
638
+  _test_user_passwd  ${USERNAME1} '*'
622 639
   _test_user_enabled ${USERNAME2} true
623 640
   _test_sudo_enabled ${USERNAME2} ${uname2_sudo}
624 641
   _test_ssh_keys     ${USERNAME2} "${USERNAME2_SSHKEY1}"
625 642
   _test_ssh_keys     ${USERNAME2} "${USERNAME2_SSHKEY2}"
643
+  _test_user_passwd  ${USERNAME2} '*'
626 644
   _test_user_enabled ${USERNAME3} true
627 645
   _test_sudo_enabled ${USERNAME3} ${uname3_sudo}
628 646
   _test_ssh_keys     ${USERNAME3} "${USERNAME1_SSHKEY1}"
629 647
   _test_ssh_keys     ${USERNAME3} "${USERNAME2_SSHKEY3}"
648
+  _test_user_passwd  ${USERNAME3} '*'
630 649
   _test_user_enabled ${USERNAME4} true
631 650
   _test_sudo_enabled ${USERNAME4} ${USERNAME4_SUDO}
632 651
   _test_ssh_keys     ${USERNAME4} false
652
+  _test_user_passwd  ${USERNAME4} '*'
633 653
   echo '[SUCCESS] uamlite test2 passed successfully' >> "${TEST_RESULTS}"
634 654
 
635 655
   # Test revert/rollback functionality
@@ -646,7 +666,7 @@ test_uamlite(){
646 666
   echo '[SUCCESS] uamlite test3 passed successfully' >> "${TEST_RESULTS}"
647 667
 
648 668
   # Test purge users flag
649
-  overrides_yaml=${LOGS_SUBDIR}/${FUNCNAME}-set3.yaml
669
+  overrides_yaml=${LOGS_SUBDIR}/${FUNCNAME}-set4.yaml
650 670
   echo "conf:
651 671
   uamlite:
652 672
     purge_expired_users: true" > "${overrides_yaml}"
@@ -657,6 +677,33 @@ test_uamlite(){
657 677
   _test_user_purged ${USERNAME3}
658 678
   _test_user_purged ${USERNAME4}
659 679
   echo '[SUCCESS] uamlite test4 passed successfully' >> "${TEST_RESULTS}"
680
+
681
+  # Test invalid password
682
+  overrides_yaml=${LOGS_SUBDIR}/${FUNCNAME}-set5.yaml
683
+  user2_crypt_passwd_invalid='plaintextPassword'
684
+  echo "conf:
685
+  uamlite:
686
+    users:
687
+    - user_name: ${USERNAME2}
688
+      user_crypt_passwd: ${user2_crypt_passwd_invalid}" > "${overrides_yaml}"
689
+  install_base "--values=${overrides_yaml}" 2>&1 | grep 'BAD PASSWORD' || \
690
+    (echo "[FAIL] uamlite test5 did not receive expected 'BAD PASSWORD' error" && exit 1)
691
+  echo '[SUCCESS] uamlite test5 passed successfully' >> "${TEST_RESULTS}"
692
+
693
+  # Test invalid SSH key
694
+  overrides_yaml=${LOGS_SUBDIR}/${FUNCNAME}-set6.yaml
695
+  user2_bad_sshkey='AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmT key-comment'
696
+  echo "conf:
697
+  uamlite:
698
+    users:
699
+    - user_name: ${USERNAME2}
700
+      user_sshkeys:
701
+      - ${USERNAME2_SSHKEY1}
702
+      - ${user2_bad_sshkey}
703
+      - ${USERNAME2_SSHKEY3}" > "${overrides_yaml}"
704
+  install_base "--values=${overrides_yaml}" 2>&1 | grep 'BAD SSH KEY' || \
705
+    (echo "[FAIL] uamlite test6 did not receive expected 'BAD SSH KEY' error" && exit 1)
706
+  echo '[SUCCESS] uamlite test6 passed successfully' >> "${TEST_RESULTS}"
660 707
 }
661 708
 
662 709
 # test daemonset value overrides for hosts and labels

+ 62
- 15
docs/source/index.rst View File

@@ -17,9 +17,6 @@
17 17
 Divingbell
18 18
 ==========
19 19
 
20
-What is it?
21
------------
22
-
23 20
 Divingbell is a lightweight solution for:
24 21
 1. Bare metal configuration management for a few very targeted use cases
25 22
 2. Bare metal package manager orchestration
@@ -66,10 +63,6 @@ fashion: the idempotent automation for each daemonset will only re-run when
66 63
 Armada spawns/respawns the container, or if information relevant to the host
67 64
 changes in the configmap.
68 65
 
69
-For upgrades, a decision was taken not to use any of the built-in Kubernetes
70
-update strategies such as RollingUpdate. Instead, we are putting this on
71
-Armada to handle the orchestration of how to do upgrades (e.g., rack by rack).
72
-
73 66
 Daemonset configs
74 67
 -----------------
75 68
 
@@ -123,19 +116,73 @@ access. Ex::
123 116
         purge_expired_users: false
124 117
         users:
125 118
         - user_name: testuser
126
-          user_sudo: True
119
+          user_crypt_passwd: $6$...
120
+          user_sudo: true
127 121
           user_sshkeys:
128 122
           - ssh-rsa AAAAB3N... key1-comment
129 123
           - ssh-rsa AAAAVY6... key2-comment
130 124
 
131
-An update to the chart with revmoed users will result in those user's accounts
132
-being expired, preventing those users any access through those accounts. This
133
-does not delete their home directory or any other files, and provides UID
134
-consistency in the event the same account gets re-added later, and they regain
135
-access to their files again.
125
+Setting user passwords
126
+""""""""""""""""""""""
127
+
128
+Including ``user_crypt_passwd`` to set a user password is optional.
129
+
130
+If setting a password for the user, the chart expects the password to be
131
+encrypted with SHA-512 and formatted in the way that ``crypt`` library expects.
132
+Run the following command to generate the needed encrypted password from the
133
+plaintext password::
134
+
135
+    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.')"
136
+
137
+Use the output of the above command as the ``user_crypt_passwd`` for the user.
138
+(Credit to `unix.stackexchange.com <https://unix.stackexchange.com/questions/81240/manually-generate-password-for-etc-shadow>`_.)
139
+If the password is not formatted how crypt expects, the chart will throw an
140
+error and fail to render.
141
+
142
+At least one user must be defined with a password and sudo in order for the
143
+built-in ``ubuntu`` account to be disabled. This is because in a situation where
144
+network access is unavailable, console username/password access will be the only
145
+login option.
146
+
147
+Setting user sudo
148
+"""""""""""""""""
149
+
150
+Including ``user_sudo`` to set user sudo access is optional. The default value
151
+is ``false``.
152
+
153
+At least one user must be defined with sudo access in order for the built-in
154
+``ubuntu`` account to be disabled.
155
+
156
+SSH keys
157
+""""""""
158
+
159
+Including ``user_sshkeys`` for defining one or more user SSH keys is optional.
160
+
161
+The chart will throw an error and fail to render if the SSH key is not one of
162
+the following formats:
163
+
164
+- dsa (ssh-dss ...)
165
+- ecdsa (ecdsa-...)
166
+- ed25519 (ssh-ed25519 ...)
167
+- rsa (ssh-rsa ...)
168
+
169
+Setting ``user_sshkeys`` to ``[ Unmanaged ]`` will instruct divingbell not to
170
+manage the user's authorized_keys file.
171
+
172
+At least one user must be defined with an SSH key and sudo in order for the
173
+built-in ``ubuntu`` account to be disabled.
174
+
175
+Purging expired users
176
+"""""""""""""""""""""
177
+
178
+Including the ``purge_expired_users`` key-value pair is optional. The default
179
+value is ``false``.
136 180
 
137
-However, if it is desired to purge expired and removed accounts and their home
138
-directories, this may be done by the ``purge_expired_users`` option to ``true``.
181
+This option must be set to ``true`` if it is desired to purge expired accounts
182
+and remove their home directories. Otherwise, removed accounts are expired (so
183
+users cannot login) but their home directories remain intact, in order to
184
+maintain UID consistency (in the event the same accounts gets re-added later,
185
+they regain access to their home directory files without UID mismatching).
139 186
 
140 187
 Node specific configurations
141 188
 ----------------------------

Loading…
Cancel
Save