Blame cloudinit/config/cc_seed_random.py

Packit Service a04d08
# Copyright (C) 2013 Yahoo! Inc.
Packit Service a04d08
# Copyright (C) 2014 Canonical, Ltd
Packit Service a04d08
#
Packit Service a04d08
# Author: Joshua Harlow <harlowja@yahoo-inc.com>
Packit Service a04d08
# Author: Dustin Kirkland <kirkland@ubuntu.com>
Packit Service a04d08
# Author: Scott Moser <scott.moser@canonical.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
Seed Random
Packit Service a04d08
-----------
Packit Service a04d08
**Summary:** provide random seed data
Packit Service a04d08
Packit Service a04d08
Since all cloud instances started from the same image will produce very similar
Packit Service a04d08
data when they are first booted, as they are all starting with the same seed
Packit Service a04d08
for the kernel's entropy keyring. To avoid this, random seed data can be
Packit Service a04d08
provided to the instance either as a string or by specifying a command to run
Packit Service a04d08
to generate the data.
Packit Service a04d08
Packit Service a04d08
Configuration for this module is under the ``random_seed`` config key. The
Packit Service a04d08
``file`` key specifies the path to write the data to, defaulting to
Packit Service a04d08
``/dev/urandom``. Data can be passed in directly with ``data``, and may
Packit Service a04d08
optionally be specified in encoded form, with the encoding specified in
Packit Service a04d08
``encoding``.
Packit Service a04d08
Packit Service a04d08
.. note::
Packit Service a04d08
    when using a multiline value for ``data`` or specifying binary data, be
Packit Service a04d08
    sure to follow yaml syntax and use the ``|`` and ``!binary`` yaml format
Packit Service a04d08
    specifiers when appropriate
Packit Service a04d08
Packit Service a04d08
Instead of specifying a data string, a command can be run to generate/collect
Packit Service a04d08
the data to be written. The command should be specified as a list of args in
Packit Service a04d08
the ``command`` key. If a command is specified that cannot be run, no error
Packit Service a04d08
will be reported unless ``command_required`` is set to true.
Packit Service a04d08
Packit Service a04d08
For example, to use ``pollinate`` to gather data from a
Packit Service a04d08
remote entropy server and write it to ``/dev/urandom``, the following could be
Packit Service a04d08
used::
Packit Service a04d08
Packit Service a04d08
    random_seed:
Packit Service a04d08
        file: /dev/urandom
Packit Service a04d08
        command: ["pollinate", "--server=http://local.polinate.server"]
Packit Service a04d08
        command_required: true
Packit Service a04d08
Packit Service a04d08
**Internal name:** ``cc_seed_random``
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
    random_seed:
Packit Service a04d08
        file: <file>
Packit Service a04d08
        data: <random string>
Packit Service a04d08
        encoding: <raw/base64/b64/gzip/gz>
Packit Service a04d08
        command: [<cmd name>, <arg1>, <arg2>...]
Packit Service a04d08
        command_required: <true/false>
Packit Service a04d08
"""
Packit Service a04d08
Packit Service a04d08
import base64
Packit Service a04d08
import os
Packit Service 751c4a
from io import BytesIO
Packit Service a04d08
Packit Service a04d08
from cloudinit import log as logging
Packit Service a04d08
from cloudinit.settings import PER_INSTANCE
Packit Service 751c4a
from cloudinit import subp
Packit Service a04d08
from cloudinit import util
Packit Service a04d08
Packit Service a04d08
frequency = PER_INSTANCE
Packit Service a04d08
LOG = logging.getLogger(__name__)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def _decode(data, encoding=None):
Packit Service a04d08
    if not data:
Packit Service a04d08
        return b''
Packit Service a04d08
    if not encoding or encoding.lower() in ['raw']:
Packit Service a04d08
        return util.encode_text(data)
Packit Service a04d08
    elif encoding.lower() in ['base64', 'b64']:
Packit Service a04d08
        return base64.b64decode(data)
Packit Service a04d08
    elif encoding.lower() in ['gzip', 'gz']:
Packit Service a04d08
        return util.decomp_gzip(data, quiet=False, decode=None)
Packit Service a04d08
    else:
Packit Service a04d08
        raise IOError("Unknown random_seed encoding: %s" % (encoding))
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def handle_random_seed_command(command, required, env=None):
Packit Service a04d08
    if not command and required:
Packit Service a04d08
        raise ValueError("no command found but required=true")
Packit Service a04d08
    elif not command:
Packit Service a04d08
        LOG.debug("no command provided")
Packit Service a04d08
        return
Packit Service a04d08
Packit Service a04d08
    cmd = command[0]
Packit Service 751c4a
    if not subp.which(cmd):
Packit Service a04d08
        if required:
Packit Service a04d08
            raise ValueError(
Packit Service a04d08
                "command '{cmd}' not found but required=true".format(cmd=cmd))
Packit Service a04d08
        else:
Packit Service a04d08
            LOG.debug("command '%s' not found for seed_command", cmd)
Packit Service a04d08
            return
Packit Service 751c4a
    subp.subp(command, env=env, capture=False)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def handle(name, cfg, cloud, log, _args):
Packit Service a04d08
    mycfg = cfg.get('random_seed', {})
Packit Service a04d08
    seed_path = mycfg.get('file', '/dev/urandom')
Packit Service a04d08
    seed_data = mycfg.get('data', b'')
Packit Service a04d08
Packit Service a04d08
    seed_buf = BytesIO()
Packit Service a04d08
    if seed_data:
Packit Service a04d08
        seed_buf.write(_decode(seed_data, encoding=mycfg.get('encoding')))
Packit Service a04d08
Packit Service a04d08
    # 'random_seed' is set up by Azure datasource, and comes already in
Packit Service a04d08
    # openstack meta_data.json
Packit Service a04d08
    metadata = cloud.datasource.metadata
Packit Service a04d08
    if metadata and 'random_seed' in metadata:
Packit Service a04d08
        seed_buf.write(util.encode_text(metadata['random_seed']))
Packit Service a04d08
Packit Service a04d08
    seed_data = seed_buf.getvalue()
Packit Service a04d08
    if len(seed_data):
Packit Service a04d08
        log.debug("%s: adding %s bytes of random seed entropy to %s", name,
Packit Service a04d08
                  len(seed_data), seed_path)
Packit Service a04d08
        util.append_file(seed_path, seed_data)
Packit Service a04d08
Packit Service a04d08
    command = mycfg.get('command', None)
Packit Service a04d08
    req = mycfg.get('command_required', False)
Packit Service a04d08
    try:
Packit Service a04d08
        env = os.environ.copy()
Packit Service a04d08
        env['RANDOM_SEED_FILE'] = seed_path
Packit Service a04d08
        handle_random_seed_command(command=command, required=req, env=env)
Packit Service a04d08
    except ValueError as e:
Packit Service a04d08
        log.warning("handling random command [%s] failed: %s", command, e)
Packit Service a04d08
        raise e
Packit Service a04d08
Packit Service a04d08
# vi: ts=4 expandtab