Dan will be taking over this branch as I am working exclusively on UA client for the near term.
Items to fix/consider: - drop sso_* keys as timing of install won't pass 2FA since the 2fa code will likely change before the machine is configured - add a 'config' key under ubuntu-advantage to allow setting configuration options in /etc/ubuntu-advantage/uaclient.conf per https://github.com/CanonicalLtd/ubuntu-advantage-client/issues/226 Diff comments: > diff --git a/cloudinit/config/cc_ubuntu_advantage.py > b/cloudinit/config/cc_ubuntu_advantage.py > index 5e082bd..1a95766 100644 > --- a/cloudinit/config/cc_ubuntu_advantage.py > +++ b/cloudinit/config/cc_ubuntu_advantage.py > @@ -1,145 +1,170 @@ > -# Copyright (C) 2018 Canonical Ltd. > -# > # This file is part of cloud-init. See LICENSE file for license information. > > -"""Ubuntu advantage: manage ubuntu-advantage offerings from Canonical.""" > +"""ubuntu_advantage: Configure Ubuntu Advantage support entitlements""" > > -import sys > from textwrap import dedent > > -from cloudinit import log as logging > from cloudinit.config.schema import ( > get_schema_doc, validate_cloudconfig_schema) > +from cloudinit import log as logging > from cloudinit.settings import PER_INSTANCE > -from cloudinit.subp import prepend_base_command > from cloudinit import util > > > -distros = ['ubuntu'] > -frequency = PER_INSTANCE > +UA_URL = 'https://ubuntu.com/advantage' > > -LOG = logging.getLogger(__name__) > +distros = ['ubuntu'] > > schema = { > 'id': 'cc_ubuntu_advantage', > - 'name': 'Ubuntu Advantage', > - 'title': 'Install, configure and manage ubuntu-advantage offerings', > + 'name': 'UbuntuAdvantage', > + 'title': 'Configure Ubuntu Advantage support entitlements', > 'description': dedent("""\ > - This module provides configuration options to setup ubuntu-advantage > - subscriptions. > - > - .. note:: > - Both ``commands`` value can be either a dictionary or a list. If > - the configuration provided is a dictionary, the keys are only > used > - to order the execution of the commands and the dictionary is > - merged with any vendor-data ubuntu-advantage configuration > - provided. If a ``commands`` is provided as a list, any > vendor-data > - ubuntu-advantage ``commands`` are ignored. > - > - Ubuntu-advantage ``commands`` is a dictionary or list of > - ubuntu-advantage commands to run on the deployed machine. > - These commands can be used to enable or disable subscriptions to > - various ubuntu-advantage products. See 'man ubuntu-advantage' for > more > - information on supported subcommands. > - > - .. note:: > - Each command item can be a string or list. If the item is a list, > - 'ubuntu-advantage' can be omitted and it will automatically be > - inserted as part of the command. > + Attach machine to an existing Ubuntu Advantage support contract and > + enable or disable support entitlements such as livepatch, ESM, > + FIPS, FIPS Updates and CIS Audit tools. When attaching a machine to > + Advantage, one can either specify explicit entitlements to enable or > + rely on the entitlement default behavior. When no 'entitlements' list > + is provided, the default behavior enables both livepatch and esm on > + supported Ubuntu environments. > + When 'entitlements' list is present, any named entitlement will be > + enabled and all absent entitlements will remain disabled. > + Note when enabling FIPS or FIPS updates a reboot will occur after > + installation completes to ensure the machine is running the > + FIPS-compliant kernel. > """), > 'distros': distros, > 'examples': [dedent("""\ > - # Enable Extended Security Maintenance using your service auth token > - ubuntu-advantage: > - commands: > - 00: ubuntu-advantage enable-esm <token> > + # Attach the machine to a Ubuntu Advantage support contract with a > + # UA user token obtained from %s. > + # Default entitlemtents such as livepatch and esm will automatically > + # be enabled after detachment because no entitlements were specified. > + ubuntu_advantage: > + token: <ua_user_token> > + """ % UA_URL), dedent("""\ > + # Attach the machine to an Ubuntu Advantage support contract using > + # Ubuntu SSO with optional two-factor authentication. Default > + # entitlements such as livepatch and esm will be enabled if > applicable. > + ubuntu_advantage: > + sso_email: <sso_email> > + sso_password: <sso_password_hash> > + sso_twofactor: <2fa_code> # if the sso account requires 2fa Agreed. we should drop all 2FA because of that timeout/rollover with 2fa likely not being met by the time the machine is installed and up. > """), dedent("""\ > - # Enable livepatch by providing your livepatch token > + # Attach the machine to an Ubuntu Advantage support contract enabling > + # only fips and entitlements. Entitlementswill only be enabled if > + # the environment supports said entitlement. Otherwise warnings will > + # be logged for incompatible entitlements specified. > ubuntu-advantage: > - commands: > - 00: ubuntu-advantage enable-livepatch <livepatch-token> > - > - """), dedent("""\ > - # Convenience: the ubuntu-advantage command can be omitted when > - # specifying commands as a list and 'ubuntu-advantage' will > - # automatically be prepended. > - # The following commands are equivalent > - ubuntu-advantage: > - commands: > - 00: ['enable-livepatch', 'my-token'] > - 01: ['ubuntu-advantage', 'enable-livepatch', 'my-token'] > - 02: ubuntu-advantage enable-livepatch my-token > - 03: 'ubuntu-advantage enable-livepatch my-token' > + token: <ua_user_token> > + entitlements: > + - fips > + - esm > """)], > 'frequency': PER_INSTANCE, > 'type': 'object', > 'properties': { > - 'ubuntu-advantage': { > + 'ubuntu_advantage': { > 'type': 'object', > 'properties': { > - 'commands': { > - 'type': ['object', 'array'], # Array of strings or dict > + 'entitlements': { > + 'type': 'array', > 'items': { > - 'oneOf': [ > - {'type': 'array', 'items': {'type': 'string'}}, > - {'type': 'string'}] > + 'type': 'string', > + 'enum': ['esm', 'fips', 'fips-updates', 'livepatch', > + 'cis-audit'] > }, > - 'additionalItems': False, # Reject non-string & non-list > - 'minItems': 1, > - 'minProperties': 1, > + 'minItems': 0 > + }, > + 'sso_email': { > + 'type': 'string', > + 'description': 'SSO email for the UA account' > + }, > + 'sso_password': { > + 'type': 'string', > + 'description': 'Hashed SSO password for the UA account' > + }, > + 'sso_twofactor': { > + 'type': 'string', > + 'description': > + 'Optional Two-factor authentication code if' > + ' required on this UA account' > + }, > + 'token': { > + 'type': 'string', > + 'description': "A user-token obtained from %s." % UA_URL > } > }, > - 'additionalProperties': False, # Reject keys not in schema > - 'required': ['commands'] > + 'oneOf': [ # Either sso_* credentials or token, but not both > + {'required': ['sso_email', 'sso_password'], > + 'not': {'required': ['token']}}, > + {'required': ['token'], > + 'not': {'required': [ > + 'sso_email', 'sso_password', 'sso_twofactor']}} > + ], > + 'minProperties': 1, # Either token or sso_* creds must be > provided > + 'additionalProperties': False > } > } > } > > -# TODO schema for 'assertions' and 'commands' are too permissive at the > moment. > -# Once python-jsonschema supports schema draft 6 add support for arbitrary > -# object keys with 'patternProperties' constraint to validate string values. > - > __doc__ = get_schema_doc(schema) # Supplement python help() > > -UA_CMD = "ubuntu-advantage" > - > - > -def run_commands(commands): > - """Run the commands provided in ubuntu-advantage:commands config. > +LOG = logging.getLogger(__name__) > > - Commands are run individually. Any errors are collected and reported > - after attempting all commands. > > - @param commands: A list or dict containing commands to run. Keys of a > - dict will be used to order the commands provided as dict values. > - """ > - if not commands: > - return > - LOG.debug('Running user-provided ubuntu-advantage commands') > - if isinstance(commands, dict): > - # Sort commands based on dictionary key > - commands = [v for _, v in sorted(commands.items())] > - elif not isinstance(commands, list): > - raise TypeError( > - 'commands parameter was not a list or dict: {commands}'.format( > - commands=commands)) > - > - fixed_ua_commands = prepend_base_command('ubuntu-advantage', commands) > - > - cmd_failures = [] > - for command in fixed_ua_commands: > - shell = isinstance(command, str) > - try: > - util.subp(command, shell=shell, status_cb=sys.stderr.write) > - except util.ProcessExecutionError as e: > - cmd_failures.append(str(e)) > - if cmd_failures: > - msg = ( > - 'Failures running ubuntu-advantage commands:\n' > - '{cmd_failures}'.format( > - cmd_failures=cmd_failures)) > +def configure_ua(token=None, sso_email=None, sso_password=None, > + sso_twofactor=None, entitlements=None): > + """Call ua commandline client to attach or enable entitlements.""" > + sso_auth = any([sso_email, sso_password, sso_twofactor]) > + error = None > + if not any([token, sso_auth]): > + error = ('ubuntu_advantage: either token or sso_email and > sso_password' > + ' must be provided') > + elif token and sso_auth: > + error = ('ubuntu_advantage: token and sso credentials cannot both be' > + ' provided') > + elif sso_auth and not all([sso_email, sso_password]): > + error = ('ubuntu_advantage: both sso_email and sso_password are' > + ' required') > + if error: > + LOG.error(error) > + raise RuntimeError(error) > + attach_cmd = ['ua', 'attach'] > + if token: > + attach_cmd.append(token) > + else: > + attach_cmd.extend(['--email', sso_email, '--password', sso_password]) > + if sso_twofactor: > + attach_cmd.extend(['--otp', sso_twofactor]) > + > + entitlement_cmds = [] > + if entitlements is not None: # Entitlements explicitly enabled > + attach_cmd.append('--no-auto-enable') > + entitlement_cmds.extend( > + [['ua', 'enable', name] for name in entitlements]) > + > + msg = 'Attaching to Ubuntu Advantage. %s' % ' '.join(attach_cmd) > + if sso_password: > + msg = msg.replace(sso_password, '<REDACTED>') > + LOG.debug(msg) > + try: > + util.subp(attach_cmd) > + except util.ProcessExecutionError as e: > + error = str(e) > + if sso_password: > + error = error.replace(sso_password, '<REDACTED>') > + msg = 'Failure attaching ubuntu advantage:\n{error}'.format( > + error=error) > util.logexc(LOG, msg) > raise RuntimeError(msg) > + for cmd in entitlement_cmds: > + try: > + util.subp(cmd, capture=True) > + except util.ProcessExecutionError as e: > + msg = 'Failure enabling ubuntu advantage:\n{error}'.format( > + error=str(e)) > + util.logexc(LOG, msg) > + raise RuntimeError(msg) > > > def maybe_install_ua_tools(cloud): -- https://code.launchpad.net/~chad.smith/cloud-init/+git/cloud-init/+merge/362161 Your team cloud-init commiters is requested to review the proposed merge of ~chad.smith/cloud-init:feature/cc-uaclient into cloud-init:master. _______________________________________________ Mailing list: https://launchpad.net/~cloud-init-dev Post to : [email protected] Unsubscribe : https://launchpad.net/~cloud-init-dev More help : https://help.launchpad.net/ListHelp

