|
Packit Service |
a04d08 |
# Copyright (C) 2009-2010 Canonical Ltd.
|
|
Packit Service |
a04d08 |
# Copyright (C) 2012, 2013 Hewlett-Packard Development Company, L.P.
|
|
Packit Service |
a04d08 |
#
|
|
Packit Service |
a04d08 |
# Author: Scott Moser <scott.moser@canonical.com>
|
|
Packit Service |
a04d08 |
# Author: Juerg Haefliger <juerg.haefliger@hp.com>
|
|
Packit Service |
a04d08 |
#
|
|
Packit Service |
a04d08 |
# This file is part of cloud-init. See LICENSE file for license information.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Set Passwords
|
|
Packit Service |
a04d08 |
-------------
|
|
Packit Service |
a04d08 |
**Summary:** Set user passwords and enable/disable SSH password authentication
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
This module consumes three top-level config keys: ``ssh_pwauth``, ``chpasswd``
|
|
Packit Service |
a04d08 |
and ``password``.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
The ``ssh_pwauth`` config key determines whether or not sshd will be configured
|
|
Packit Service |
a04d08 |
to accept password authentication. True values will enable password auth,
|
|
Packit Service |
a04d08 |
false values will disable password auth, and the literal string ``unchanged``
|
|
Packit Service |
a04d08 |
will leave it unchanged. Setting no value will also leave the current setting
|
|
Packit Service |
a04d08 |
on-disk unchanged.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
The ``chpasswd`` config key accepts a dictionary containing either or both of
|
|
Packit Service |
a04d08 |
``expire`` and ``list``.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
If the ``list`` key is provided, it should contain a list of
|
|
Packit Service |
a04d08 |
``username:password`` pairs. This can be either a YAML list (of strings), or a
|
|
Packit Service |
a04d08 |
multi-line string with one pair per line. Each user will have the
|
|
Packit Service |
a04d08 |
corresponding password set. A password can be randomly generated by specifying
|
|
Packit Service |
a04d08 |
``RANDOM`` or ``R`` as a user's password. A hashed password, created by a tool
|
|
Packit Service |
a04d08 |
like ``mkpasswd``, can be specified; a regex
|
|
Packit Service |
a04d08 |
(``r'\\$(1|2a|2y|5|6)(\\$.+){2}'``) is used to determine if a password value
|
|
Packit Service |
a04d08 |
should be treated as a hash.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
.. note::
|
|
Packit Service |
a04d08 |
The users specified must already exist on the system. Users will have been
|
|
Packit Service |
a04d08 |
created by the ``cc_users_groups`` module at this point.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
By default, all users on the system will have their passwords expired (meaning
|
|
Packit Service |
a04d08 |
that they will have to be reset the next time the user logs in). To disable
|
|
Packit Service |
a04d08 |
this behaviour, set ``expire`` under ``chpasswd`` to a false value.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
If a ``list`` of user/password pairs is not specified under ``chpasswd``, then
|
|
Packit Service |
a04d08 |
the value of the ``password`` config key will be used to set the default user's
|
|
Packit Service |
a04d08 |
password.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
**Internal name:** ``cc_set_passwords``
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
**Module frequency:** per instance
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
**Supported distros:** all
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
**Config keys**::
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
ssh_pwauth: <yes/no/unchanged>
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
password: password1
|
|
Packit Service |
a04d08 |
chpasswd:
|
|
Packit Service |
a04d08 |
expire: <true/false>
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
chpasswd:
|
|
Packit Service |
a04d08 |
list: |
|
|
Packit Service |
a04d08 |
user1:password1
|
|
Packit Service |
a04d08 |
user2:RANDOM
|
|
Packit Service |
a04d08 |
user3:password3
|
|
Packit Service |
a04d08 |
user4:R
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
##
|
|
Packit Service |
a04d08 |
# or as yaml list
|
|
Packit Service |
a04d08 |
##
|
|
Packit Service |
a04d08 |
chpasswd:
|
|
Packit Service |
a04d08 |
list:
|
|
Packit Service |
a04d08 |
- user1:password1
|
|
Packit Service |
a04d08 |
- user2:RANDOM
|
|
Packit Service |
a04d08 |
- user3:password3
|
|
Packit Service |
a04d08 |
- user4:R
|
|
Packit Service |
a04d08 |
- user4:$6$rL..$ej...
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
import re
|
|
Packit Service |
a04d08 |
import sys
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
from cloudinit.distros import ug_util
|
|
Packit Service |
a04d08 |
from cloudinit import log as logging
|
|
Packit Service |
a04d08 |
from cloudinit.ssh_util import update_ssh_config
|
|
Packit Service |
751c4a |
from cloudinit import subp
|
|
Packit Service |
a04d08 |
from cloudinit import util
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
from string import ascii_letters, digits
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG = logging.getLogger(__name__)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# We are removing certain 'painful' letters/numbers
|
|
Packit Service |
a04d08 |
PW_SET = (''.join([x for x in ascii_letters + digits
|
|
Packit Service |
a04d08 |
if x not in 'loLOI01']))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def handle_ssh_pwauth(pw_auth, service_cmd=None, service_name="ssh"):
|
|
Packit Service |
a04d08 |
"""Apply sshd PasswordAuthentication changes.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@param pw_auth: config setting from 'pw_auth'.
|
|
Packit Service |
a04d08 |
Best given as True, False, or "unchanged".
|
|
Packit Service |
a04d08 |
@param service_cmd: The service command list (['service'])
|
|
Packit Service |
a04d08 |
@param service_name: The name of the sshd service for the system.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@return: None"""
|
|
Packit Service |
a04d08 |
cfg_name = "PasswordAuthentication"
|
|
Packit Service |
a04d08 |
if service_cmd is None:
|
|
Packit Service |
a04d08 |
service_cmd = ["service"]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if util.is_true(pw_auth):
|
|
Packit Service |
a04d08 |
cfg_val = 'yes'
|
|
Packit Service |
a04d08 |
elif util.is_false(pw_auth):
|
|
Packit Service |
a04d08 |
cfg_val = 'no'
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
751c4a |
bmsg = "Leaving SSH config '%s' unchanged." % cfg_name
|
|
Packit Service |
a04d08 |
if pw_auth is None or pw_auth.lower() == 'unchanged':
|
|
Packit Service |
a04d08 |
LOG.debug("%s ssh_pwauth=%s", bmsg, pw_auth)
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
LOG.warning("%s Unrecognized value: ssh_pwauth=%s", bmsg, pw_auth)
|
|
Packit Service |
a04d08 |
return
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
updated = update_ssh_config({cfg_name: cfg_val})
|
|
Packit Service |
a04d08 |
if not updated:
|
|
Packit Service |
751c4a |
LOG.debug("No need to restart SSH service, %s not updated.", cfg_name)
|
|
Packit Service |
a04d08 |
return
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if 'systemctl' in service_cmd:
|
|
Packit Service |
a04d08 |
cmd = list(service_cmd) + ["restart", service_name]
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
cmd = list(service_cmd) + [service_name, "restart"]
|
|
Packit Service |
751c4a |
subp.subp(cmd)
|
|
Packit Service |
751c4a |
LOG.debug("Restarted the SSH daemon.")
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def handle(_name, cfg, cloud, log, args):
|
|
Packit Service |
a04d08 |
if len(args) != 0:
|
|
Packit Service |
a04d08 |
# if run from command line, and give args, wipe the chpasswd['list']
|
|
Packit Service |
a04d08 |
password = args[0]
|
|
Packit Service |
a04d08 |
if 'chpasswd' in cfg and 'list' in cfg['chpasswd']:
|
|
Packit Service |
a04d08 |
del cfg['chpasswd']['list']
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
password = util.get_cfg_option_str(cfg, "password", None)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
expire = True
|
|
Packit Service |
a04d08 |
plist = None
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if 'chpasswd' in cfg:
|
|
Packit Service |
a04d08 |
chfg = cfg['chpasswd']
|
|
Packit Service |
a04d08 |
if 'list' in chfg and chfg['list']:
|
|
Packit Service |
a04d08 |
if isinstance(chfg['list'], list):
|
|
Packit Service |
a04d08 |
log.debug("Handling input for chpasswd as list.")
|
|
Packit Service |
a04d08 |
plist = util.get_cfg_option_list(chfg, 'list', plist)
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
log.debug("Handling input for chpasswd as multiline string.")
|
|
Packit Service |
a04d08 |
plist = util.get_cfg_option_str(chfg, 'list', plist)
|
|
Packit Service |
a04d08 |
if plist:
|
|
Packit Service |
a04d08 |
plist = plist.splitlines()
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
expire = util.get_cfg_option_bool(chfg, 'expire', expire)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if not plist and password:
|
|
Packit Service |
a04d08 |
(users, _groups) = ug_util.normalize_users_groups(cfg, cloud.distro)
|
|
Packit Service |
a04d08 |
(user, _user_config) = ug_util.extract_default(users)
|
|
Packit Service |
a04d08 |
if user:
|
|
Packit Service |
a04d08 |
plist = ["%s:%s" % (user, password)]
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
log.warning("No default or defined user to change password for.")
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
errors = []
|
|
Packit Service |
a04d08 |
if plist:
|
|
Packit Service |
a04d08 |
plist_in = []
|
|
Packit Service |
a04d08 |
hashed_plist_in = []
|
|
Packit Service |
a04d08 |
hashed_users = []
|
|
Packit Service |
a04d08 |
randlist = []
|
|
Packit Service |
a04d08 |
users = []
|
|
Packit Service |
a04d08 |
# N.B. This regex is included in the documentation (i.e. the module
|
|
Packit Service |
a04d08 |
# docstring), so any changes to it should be reflected there.
|
|
Packit Service |
a04d08 |
prog = re.compile(r'\$(1|2a|2y|5|6)(\$.+){2}')
|
|
Packit Service |
a04d08 |
for line in plist:
|
|
Packit Service |
a04d08 |
u, p = line.split(':', 1)
|
|
Packit Service |
a04d08 |
if prog.match(p) is not None and ":" not in p:
|
|
Packit Service |
a04d08 |
hashed_plist_in.append(line)
|
|
Packit Service |
a04d08 |
hashed_users.append(u)
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
# in this else branch, we potentially change the password
|
|
Packit Service |
a04d08 |
# hence, a deviation from .append(line)
|
|
Packit Service |
a04d08 |
if p == "R" or p == "RANDOM":
|
|
Packit Service |
a04d08 |
p = rand_user_password()
|
|
Packit Service |
a04d08 |
randlist.append("%s:%s" % (u, p))
|
|
Packit Service |
a04d08 |
plist_in.append("%s:%s" % (u, p))
|
|
Packit Service |
a04d08 |
users.append(u)
|
|
Packit Service |
a04d08 |
ch_in = '\n'.join(plist_in) + '\n'
|
|
Packit Service |
a04d08 |
if users:
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
log.debug("Changing password for %s:", users)
|
|
Packit Service |
a04d08 |
chpasswd(cloud.distro, ch_in)
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
a04d08 |
errors.append(e)
|
|
Packit Service |
a04d08 |
util.logexc(
|
|
Packit Service |
a04d08 |
log, "Failed to set passwords with chpasswd for %s", users)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
hashed_ch_in = '\n'.join(hashed_plist_in) + '\n'
|
|
Packit Service |
a04d08 |
if hashed_users:
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
log.debug("Setting hashed password for %s:", hashed_users)
|
|
Packit Service |
a04d08 |
chpasswd(cloud.distro, hashed_ch_in, hashed=True)
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
a04d08 |
errors.append(e)
|
|
Packit Service |
a04d08 |
util.logexc(
|
|
Packit Service |
a04d08 |
log, "Failed to set hashed passwords with chpasswd for %s",
|
|
Packit Service |
a04d08 |
hashed_users)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if len(randlist):
|
|
Packit Service |
a04d08 |
blurb = ("Set the following 'random' passwords\n",
|
|
Packit Service |
a04d08 |
'\n'.join(randlist))
|
|
Packit Service |
a04d08 |
sys.stderr.write("%s\n%s\n" % blurb)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if expire:
|
|
Packit Service |
a04d08 |
expired_users = []
|
|
Packit Service |
a04d08 |
for u in users:
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
cloud.distro.expire_passwd(u)
|
|
Packit Service |
a04d08 |
expired_users.append(u)
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
a04d08 |
errors.append(e)
|
|
Packit Service |
a04d08 |
util.logexc(log, "Failed to set 'expire' for %s", u)
|
|
Packit Service |
a04d08 |
if expired_users:
|
|
Packit Service |
a04d08 |
log.debug("Expired passwords for: %s users", expired_users)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
handle_ssh_pwauth(
|
|
Packit Service |
a04d08 |
cfg.get('ssh_pwauth'), service_cmd=cloud.distro.init_cmd,
|
|
Packit Service |
a04d08 |
service_name=cloud.distro.get_option('ssh_svcname', 'ssh'))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if len(errors):
|
|
Packit Service |
a04d08 |
log.debug("%s errors occured, re-raising the last one", len(errors))
|
|
Packit Service |
a04d08 |
raise errors[-1]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
def rand_user_password(pwlen=20):
|
|
Packit Service |
a04d08 |
return util.rand_str(pwlen, select_from=PW_SET)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def chpasswd(distro, plist_in, hashed=False):
|
|
Packit Service |
751c4a |
if util.is_BSD():
|
|
Packit Service |
a04d08 |
for pentry in plist_in.splitlines():
|
|
Packit Service |
a04d08 |
u, p = pentry.split(":")
|
|
Packit Service |
a04d08 |
distro.set_passwd(u, p, hashed=hashed)
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
cmd = ['chpasswd'] + (['-e'] if hashed else [])
|
|
Packit Service |
751c4a |
subp.subp(cmd, plist_in)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# vi: ts=4 expandtab
|