"Fossies" - the Fresh Open Source Software Archive

Member "barbican-12.0.0/barbican/cmd/barbican_manage.py" (14 Apr 2021, 18818 Bytes) of package /linux/misc/openstack/barbican-12.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "barbican_manage.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 11.0.0_vs_12.0.0.

    1 #!/usr/bin/env python
    2 # Copyright 2010-2015 OpenStack LLC.
    3 # All Rights Reserved.
    4 #
    5 # Licensed under the Apache License, Version 2.0 (the "License");
    6 # you may not use this file except in compliance with the License.
    7 # You may obtain a copy of the License at
    8 #
    9 #    http://www.apache.org/licenses/LICENSE-2.0
   10 #
   11 # Unless required by applicable law or agreed to in writing, software
   12 # distributed under the License is distributed on an "AS IS" BASIS,
   13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   14 # implied.
   15 # See the License for the specific language governing permissions and
   16 # limitations under the License.
   17 
   18 """
   19     CLI interface for barbican management
   20 """
   21 
   22 import argparse
   23 import sys
   24 
   25 from oslo_config import cfg
   26 from oslo_log import log as logging
   27 
   28 from barbican.cmd import pkcs11_kek_rewrap as pkcs11_rewrap
   29 from barbican.common import config
   30 from barbican.model import clean
   31 from barbican.model.migration import commands
   32 from barbican.model import sync
   33 from barbican.plugin.crypto import p11_crypto
   34 from barbican.plugin.crypto import pkcs11
   35 import barbican.version
   36 
   37 CONF = cfg.CONF
   38 LOG = logging.getLogger(__name__)
   39 
   40 
   41 # Decorators for actions
   42 def args(*args, **kwargs):
   43     def _decorator(func):
   44         func.__dict__.setdefault('args', []).insert(0, (args, kwargs))
   45         return func
   46     return _decorator
   47 
   48 
   49 class DbCommands(object):
   50     """Class for managing barbican database"""
   51 
   52     description = "Subcommands for managing barbican database"
   53 
   54     clean_description = "Clean up soft deletions in the database"
   55 
   56     @args('--db-url', '-d', metavar='<db-url>', dest='dburl',
   57           help='barbican database URL')
   58     @args('--min-days', '-m', metavar='<min-days>', dest='min_days', type=int,
   59           default=90, help='minimum number of days to keep soft deletions. '
   60           'default is %(default)s days.')
   61     @args('--verbose', '-V', action='store_true', dest='verbose',
   62           default=False, help='Show verbose information about the clean up.')
   63     @args('--log-file', '-L', metavar='<log-file>', type=str, default=None,
   64           dest='log_file', help='Set log file location. '
   65           'Default value for log_file can be found in barbican.conf')
   66     @args('--clean-unassociated-projects', '-p', action='store_true',
   67           dest='do_clean_unassociated_projects', default=False,
   68           help='Remove projects that have no '
   69                'associated resources.')
   70     @args('--soft-delete-expired-secrets', '-e', action='store_true',
   71           dest='do_soft_delete_expired_secrets', default=False,
   72           help='Soft delete secrets that are expired.')
   73     def clean(self, conf, dburl=None, min_days=None, verbose=None,
   74               log_file=None, do_clean_unassociated_projects=None,
   75               do_soft_delete_expired_secrets=None):
   76         """Clean soft deletions in the database"""
   77         if dburl is None:
   78             dburl = CONF.sql_connection
   79         if log_file is None:
   80             log_file = CONF.log_file
   81 
   82         clean.clean_command(
   83             sql_url=dburl,
   84             min_num_days=min_days,
   85             do_clean_unassociated_projects=do_clean_unassociated_projects,
   86             do_soft_delete_expired_secrets=do_soft_delete_expired_secrets,
   87             verbose=verbose,
   88             log_file=log_file)
   89 
   90     revision_description = "Create a new database version file"
   91 
   92     @args('--db-url', '-d', metavar='<db-url>', dest='dburl',
   93           help='barbican database URL')
   94     @args('--message', '-m', metavar='<message>', default='DB change',
   95           help='the message for the DB change')
   96     @args('--autogenerate', action="store_true", dest='autogen',
   97           default=False, help='autogenerate from models')
   98     def revision(self, conf, dburl=None, message=None, autogen=None):
   99         """Process the 'revision' Alembic command."""
  100         if dburl is None:
  101             commands.generate(autogenerate=autogen, message=str(message),
  102                               sql_url=CONF.sql_connection)
  103         else:
  104             commands.generate(autogenerate=autogen, message=str(message),
  105                               sql_url=str(dburl))
  106 
  107     upgrade_description = "Upgrade to a future database version"
  108 
  109     @args('--db-url', '-d', metavar='<db-url>', dest='dburl',
  110           help='barbican database URL')
  111     @args('--version', '-v', metavar='<version>', default='head',
  112           help='the version to upgrade to, or else '
  113           'the latest/head if not specified.')
  114     def upgrade(self, conf, dburl=None, version=None):
  115         """Process the 'upgrade' Alembic command."""
  116         if dburl is None:
  117             commands.upgrade(to_version=str(version),
  118                              sql_url=CONF.sql_connection)
  119         else:
  120             commands.upgrade(to_version=str(version), sql_url=str(dburl))
  121 
  122     history_description = "Show database changset history"
  123 
  124     @args('--db-url', '-d', metavar='<db-url>', dest='dburl',
  125           help='barbican database URL')
  126     @args('--verbose', '-V', action='store_true', dest='verbose',
  127           default=False, help='Show full information about the revisions.')
  128     def history(self, conf, dburl=None, verbose=None):
  129         if dburl is None:
  130             commands.history(verbose, sql_url=CONF.sql_connection)
  131         else:
  132             commands.history(verbose, sql_url=str(dburl))
  133 
  134     current_description = "Show current revision of database"
  135 
  136     @args('--db-url', '-d', metavar='<db-url>', dest='dburl',
  137           help='barbican database URL')
  138     @args('--verbose', '-V', action='store_true', dest='verbose',
  139           default=False, help='Show full information about the revisions.')
  140     def current(self, conf, dburl=None, verbose=None):
  141         if dburl is None:
  142             commands.current(verbose, sql_url=CONF.sql_connection)
  143         else:
  144             commands.current(verbose, sql_url=str(dburl))
  145     sync_secret_stores_description = ("Sync secret_stores with "  # nosec
  146                                       "barbican.conf")
  147 
  148     @args('--db-url', '-d', metavar='<db-url>', dest='dburl',
  149           help='barbican database URL')
  150     @args('--verbose', '-V', action='store_true', dest='verbose',
  151           default=False, help='Show verbose information about the clean up.')
  152     @args('--log-file', '-L', metavar='<log-file>', type=str, default=None,
  153           dest='log_file',
  154           help='Set log file location. '
  155                'Default value for log_file can be found in barbican.conf')
  156     def sync_secret_stores(self, conf, dburl=None, verbose=None,
  157                            log_file=None):
  158         """Sync secret_stores table with barbican.conf"""
  159         if dburl is None:
  160             dburl = CONF.sql_connection
  161         if log_file is None:
  162             log_file = CONF.log_file
  163 
  164         sync.sync_secret_stores(
  165             sql_url=dburl,
  166             verbose=verbose,
  167             log_file=log_file)
  168 
  169 
  170 class HSMCommands(object):
  171     """Class for managing HSM/pkcs11 plugin"""
  172 
  173     _CKK_AES = 'CKK_AES'
  174 
  175     description = "Subcommands for managing HSM/PKCS11"
  176 
  177     check_mkek_description = "Checks if a MKEK label is available"
  178 
  179     @args('--library-path', metavar='<library-path>', dest='libpath',
  180           help='Path to vendor PKCS#11 library')
  181     @args('--slot-id', metavar='<slot-id>', dest='slotid',
  182           help='HSM Slot ID containing Token to be used.')
  183     @args('--passphrase', metavar='<passphrase>',
  184           help='Password (PIN) to login to PKCS#11 Token')
  185     @args('--label', '-L', metavar='<label>',
  186           help='The label of the Master Key Encryption Key')
  187     @args('--hmac-wrap-mechanism', metavar='<hmac key wrap mechanism>',
  188           dest='hmacwrap',
  189           help='HMAC Key wrap mechanism')
  190     def check_mkek(self, conf, passphrase=None, libpath=None, slotid=None,
  191                    label=None, hmacwrap=None):
  192         self._create_pkcs11_session(conf, passphrase, libpath, slotid,
  193                                     hmacwrap)
  194         if label is None:
  195             label = conf.p11_crypto_plugin.mkek_label
  196         handle = self.pkcs11.get_key_handle(self._CKK_AES, label, self.session)
  197         self.pkcs11.return_session(self.session)
  198         if not handle:
  199             print("Label {label} is not set.".format(label=label))
  200             sys.exit(1)
  201         print("Key labeled {} found!".format(label))
  202 
  203     gen_mkek_description = "Generates a new MKEK"
  204 
  205     @args('--library-path', metavar='<library-path>', dest='libpath',
  206           help='Path to vendor PKCS11 library')
  207     @args('--slot-id', metavar='<slot-id>', dest='slotid',
  208           help='HSM Slot ID containing Token to be used.')
  209     @args('--passphrase', metavar='<passphrase>',
  210           help='Password (PIN) to login to PKCS#11 Token')
  211     @args('--label', '-L', metavar='<label>',
  212           help='The label of the Master Key Encryption Key')
  213     @args('--length', '-l', metavar='<length>',
  214           help='The length in bytes of the Master Key Encryption Key'
  215                ' (default is 32)')
  216     @args('--hmac-wrap-mechanism', metavar='<hmac key wrap mechanism>',
  217           dest='hmacwrap',
  218           help='HMAC Key wrap mechanism, default is CKM_SHA256_HMAC')
  219     def gen_mkek(self, conf, passphrase=None, libpath=None, slotid=None,
  220                  label=None, length=None, hmacwrap=None):
  221         CKM_AES_KEY_GEN = 'CKM_AES_KEY_GEN'
  222         self._create_pkcs11_session(conf, passphrase, libpath, slotid,
  223                                     hmacwrap)
  224         if label is None:
  225             label = conf.p11_crypto_plugin.mkek_label or 'primarymkek'
  226         self._verify_label_does_not_exist(self._CKK_AES, label, self.session)
  227         if length is None:
  228             length = conf.p11_crypto_plugin.mkek_length or 32
  229         if type(length) is not int:
  230             length = int(length)
  231         self.pkcs11.generate_key(self._CKK_AES, length, CKM_AES_KEY_GEN,
  232                                  self.session, label,
  233                                  encrypt=True, wrap=True, master_key=True)
  234         self.pkcs11.return_session(self.session)
  235         print("MKEK successfully generated!")
  236 
  237     check_hmac_description = "Checks if a HMAC key label is available"
  238 
  239     @args('--library-path', metavar='<library-path>', dest='libpath',
  240           help='Path to vendor PKCS#11 library')
  241     @args('--slot-id', metavar='<slot-id>', dest='slotid',
  242           help='HSM Slot ID containing Token to be used.')
  243     @args('--passphrase', metavar='<passphrase>',
  244           help='Password (PIN) to login to PKCS#11 Token')
  245     @args('--label', '-L', metavar='<label>',
  246           help='The label of the Master HMAC key')
  247     @args('--key-type', '-t', metavar='<key type>', dest='keytype',
  248           help='The HMAC Key Type (e.g. CKK_AES)')
  249     @args('--hmac-wrap-mechanism', metavar='<hmac key wrap mechanism>',
  250           dest='hmacwrap',
  251           help='HMAC Key wrap mechanism')
  252     def check_hmac(self, conf, passphrase=None, libpath=None, slotid=None,
  253                    label=None, keytype=None, hmacwrap=None):
  254         self._create_pkcs11_session(conf, passphrase, libpath, slotid,
  255                                     hmacwrap)
  256         if label is None:
  257             label = conf.p11_crypto_plugin.hmac_label
  258         if keytype is None:
  259             keytype = conf.p11_crypto_plugin.hmac_key_type
  260         handle = self.pkcs11.get_key_handle(keytype, label, self.session)
  261         self.pkcs11.return_session(self.session)
  262         if not handle:
  263             print("Label {label} is not set.".format(label=label))
  264             sys.exit(1)
  265         print("Key labeled {} found!".format(label))
  266 
  267     gen_hmac_description = "Generates a new HMAC key"
  268 
  269     @args('--library-path', metavar='<library-path>', dest='libpath',
  270           help='Path to vendor PKCS11 library')
  271     @args('--slot-id', metavar='<slot-id>', dest='slotid',
  272           help='HSM Slot ID containing Token to be used.')
  273     @args('--passphrase', metavar='<passphrase>',
  274           help='Password (PIN) to login to PKCS#11 Token')
  275     @args('--label', '-L', metavar='<label>',
  276           help='The label of the Master HMAC Key')
  277     @args('--key-type', '-t', metavar='<key type>', dest='keytype',
  278           help='The HMAC Key Type (e.g. CKK_AES)')
  279     @args('--length', '-l', metavar='<length>',
  280           help='The length in bytes of the Master HMAC Key (default is 32)')
  281     @args('--mechanism', '-m', metavar='<mechanism>',
  282           help='The HMAC Key Generation mechanism')
  283     @args('--hmac-wrap-mechanism', metavar='<hmac key wrap mechanism>',
  284           dest='hmacwrap',
  285           help='HMAC Key wrap mechanism, default is CKM_SHA256_HMAC')
  286     def gen_hmac(self, conf, passphrase=None, libpath=None, slotid=None,
  287                  label=None, keytype=None, mechanism=None, length=None,
  288                  hmacwrap=None):
  289         self._create_pkcs11_session(conf, passphrase, libpath, slotid,
  290                                     hmacwrap)
  291         if label is None:
  292             label = conf.p11_crypto_plugin.hmac_label or 'primaryhmac'
  293         if keytype is None:
  294             keytype = conf.p11_crypto_plugin.hmac_key_type
  295         self._verify_label_does_not_exist(keytype, label, self.session)
  296 
  297         if length is None:
  298             # barbican.conf doesn't have an HMAC length
  299             length = 32  # bytes
  300         elif type(length) is not int:
  301             length = int(length)
  302         if mechanism is None:
  303             mechanism = conf.p11_crypto_plugin.hmac_keygen_mechanism
  304         self.pkcs11.generate_key(keytype, length, mechanism, self.session,
  305                                  label, sign=True, master_key=True)
  306         self.pkcs11.return_session(self.session)
  307         print("HMAC successfully generated!")
  308 
  309     rewrap_pkek_description = "Re-wrap project MKEKs"
  310 
  311     @args('--dry-run', action="store_true", dest='dryrun', default=False,
  312           help='Displays changes that will be made (Non-destructive)')
  313     def rewrap_pkek(self, conf, dryrun=None):
  314         rewrapper = pkcs11_rewrap.KekRewrap(pkcs11_rewrap.CONF)
  315         rewrapper.execute(dryrun)
  316         rewrapper.pkcs11.return_session(rewrapper.hsm_session)
  317 
  318     def _create_pkcs11_session(self, conf, passphrase, libpath, slotid,
  319                                hmacwrap):
  320         if passphrase is None:
  321             passphrase = conf.p11_crypto_plugin.login
  322         if libpath is None:
  323             libpath = conf.p11_crypto_plugin.library_path
  324         if slotid is None:
  325             slotid = conf.p11_crypto_plugin.slot_id
  326         elif type(slotid) is not int:
  327             slotid = int(slotid)
  328         if hmacwrap is None:
  329             hmacwrap = conf.p11_crypto_plugin.hmac_keywrap_mechanism
  330 
  331         self.pkcs11 = pkcs11.PKCS11(
  332             library_path=libpath, login_passphrase=passphrase,
  333             rw_session=True, slot_id=slotid,
  334             encryption_mechanism='CKM_AES_CBC',
  335             hmac_keywrap_mechanism=hmacwrap,
  336             token_serial_number=conf.p11_crypto_plugin.token_serial_number,
  337             token_labels=conf.p11_crypto_plugin.token_labels
  338         )
  339         self.session = self.pkcs11.get_session()
  340 
  341     def _verify_label_does_not_exist(self, key_type, label, session):
  342         key_handle = self.pkcs11.get_key_handle(key_type, label, session)
  343         if key_handle:
  344             print("The label {label} already exists!".format(label=label))
  345             sys.exit(1)
  346 
  347 
  348 CATEGORIES = {
  349     'db': DbCommands,
  350     'hsm': HSMCommands,
  351 }
  352 
  353 
  354 # Modifying similar code from nova/cmd/manage.py
  355 def methods_of(obj):
  356     """Get all callable methods of an object that don't start with underscore
  357 
  358     returns a list of tuples of the form (method_name, method)
  359     """
  360 
  361     result = []
  362     for fn in dir(obj):
  363         if callable(getattr(obj, fn)) and not fn.startswith('_'):
  364             result.append((fn, getattr(obj, fn),
  365                           getattr(obj, fn + '_description', None)))
  366     return result
  367 
  368 
  369 # Shamelessly taking same code from nova/cmd/manage.py
  370 def add_command_parsers(subparsers):
  371     """Add subcommand parser to oslo_config object"""
  372 
  373     for category in CATEGORIES:
  374         command_object = CATEGORIES[category]()
  375 
  376         desc = getattr(command_object, 'description', None)
  377         parser = subparsers.add_parser(category, description=desc)
  378         parser.set_defaults(command_object=command_object)
  379 
  380         category_subparsers = parser.add_subparsers(dest='action')
  381 
  382         for (action, action_fn, action_desc) in methods_of(command_object):
  383             parser = category_subparsers.add_parser(action,
  384                                                     description=action_desc)
  385 
  386             action_kwargs = []
  387             for args, kwargs in getattr(action_fn, 'args', []):
  388                 # Assuming dest is the arg name without the leading
  389                 # hyphens if no dest is supplied
  390                 kwargs.setdefault('dest', args[0][2:])
  391                 if kwargs['dest'].startswith('action_kwarg_'):
  392                     action_kwargs.append(
  393                         kwargs['dest'][len('action_kwarg_'):])
  394                 else:
  395                     action_kwargs.append(kwargs['dest'])
  396                     kwargs['dest'] = 'action_kwarg_' + kwargs['dest']
  397 
  398                 parser.add_argument(*args, **kwargs)
  399 
  400             parser.set_defaults(action_fn=action_fn)
  401             parser.set_defaults(action_kwargs=action_kwargs)
  402 
  403             parser.add_argument('action_args', nargs='*',
  404                                 help=argparse.SUPPRESS)
  405 
  406 
  407 # Define subcommand category
  408 category_opt = cfg.SubCommandOpt('category',
  409                                  title='Command categories',
  410                                  help='Available categories',
  411                                  handler=add_command_parsers)
  412 
  413 
  414 def main():
  415     """Parse options and call the appropriate class/method."""
  416     CONF = config.new_config()
  417     CONF.register_cli_opt(category_opt)
  418     p11_crypto.register_opts(CONF)
  419 
  420     try:
  421         logging.register_options(CONF)
  422         logging.setup(CONF, "barbican-manage")
  423         cfg_files = cfg.find_config_files(project='barbican')
  424 
  425         CONF(args=sys.argv[1:],
  426              project='barbican',
  427              prog='barbican-manage',
  428              version=barbican.version.__version__,
  429              default_config_files=cfg_files)
  430 
  431     except RuntimeError as e:
  432         sys.exit("ERROR: %s" % e)
  433 
  434     # find sub-command and its arguments
  435     fn = CONF.category.action_fn
  436     fn_args = [arg.decode('utf-8') for arg in CONF.category.action_args]
  437     fn_kwargs = {}
  438     for k in CONF.category.action_kwargs:
  439         v = getattr(CONF.category, 'action_kwarg_' + k)
  440         if v is None:
  441             continue
  442         if isinstance(v, bytes):
  443             v = v.decode('utf-8')
  444         fn_kwargs[k] = v
  445 
  446     # call the action with the remaining arguments
  447     try:
  448         return fn(CONF, *fn_args, **fn_kwargs)
  449     except Exception as e:
  450         sys.exit("ERROR: %s" % e)
  451 
  452 
  453 if __name__ == '__main__':
  454     main()