Пример #1
0
  def AddAccountsFromDatabase(self, account_db_path):
    """Add accounts from the database at |account_db_path| to self.

    Overrides previously loaded accounts.

    Args:
      account_db_path: path to file containing an account database.
    """
    raw_db = json_lib.ParseJsonFileWithComments(account_db_path)
    json_lib.AssertIsInstance(raw_db, dict, 'accounts database')

    # We don't mandate that an accounts database specify either field.
    raw_db.setdefault(USERS_KEY, [])
    raw_db.setdefault(GROUPS_KEY, [])
    user_list = json_lib.PopValueOfType(raw_db, USERS_KEY, list,
                                        'list of users in accounts database')
    group_list = json_lib.PopValueOfType(raw_db, GROUPS_KEY, list,
                                         'list of groups in accounts database')

    # We do mandate that the database contain only fields we know about.
    if raw_db:
      raise ValueError('Accounts database include unknown fields: %r' %
                       raw_db.keys())

    for user in user_list:
      json_lib.AssertIsInstance(
          user, dict, 'user specification in accounts database')
      self._AddUser(user)

    for group in group_list:
      json_lib.AssertIsInstance(
          group, dict, 'group specification in accounts database')
      self._AddGroup(group)
Пример #2
0
 def testAssertIsInstance(self):
   """Test that AssertIsInstance is correct."""
   self.assertRaises(ValueError, json_lib.AssertIsInstance,
                     tuple(), list, 'a bad value')
   self.assertRaises(ValueError, json_lib.AssertIsInstance,
                     1, float, 'a bad value')
   self.assertRaises(ValueError, json_lib.AssertIsInstance,
                     1, bool, 'a bad value')
   json_lib.AssertIsInstance([1], list, 'good value')
   json_lib.AssertIsInstance(True, bool, 'good value')
   json_lib.AssertIsInstance({'foo': 2}, dict, 'good value')
Пример #3
0
  def _FillInExecutableFromApp(self, wrapper, app):
    """Fill in the fields of a SandboxSpec.Executable object from |app|.

    Args:
      wrapper: instance of SandboxSpecWrapper.
      app: dictionary of information taken from the appc pod manifest.
    """
    sub_app = json_lib.GetValueOfType(
        app, KEY_APP_SUB_APP, dict, 'per app app dict')
    user = json_lib.GetValueOfType(
        sub_app, KEY_SUB_APP_USER, unicode, 'app dict user')
    group = json_lib.GetValueOfType(
        sub_app, KEY_SUB_APP_GROUP, unicode, 'app dict group')

    if not self._user_db.UserExists(user):
      raise ValueError('Found invalid username "%s"' % user)
    if not self._user_db.GroupExists(group):
      raise ValueError('Found invalid groupname "%s"' % group)

    cmd = json_lib.GetValueOfType(
        sub_app, KEY_SUB_APP_EXEC, list, 'app command line')
    if not cmd:
      raise ValueError('App command line must give the executable to run.')
    self._CheckAbsPathToExecutable(cmd[0])
    for cmd_piece in cmd:
      json_lib.AssertIsInstance(cmd_piece, unicode, 'app.exec fragment')

    port_list = sub_app.get(KEY_SUB_APP_PORTS, None)
    wrapper.AddExecutable(self._user_db.ResolveUsername(user),
                          self._user_db.ResolveGroupname(group),
                          cmd,
                          _GetPortList(PROTOCOL_TCP, port_list),
                          _GetPortList(PROTOCOL_UDP, port_list),
                          _ExtractLinuxCapNames(sub_app))
Пример #4
0
  def _FillInEndpointNamesFromAnnotations(self, wrapper, annotations):
    """Fill in the SandboxSpec endpoint_names field from |annotations|.

    An appc pod specification can contain a list of (mostly) arbitrary
    annotations that projects can use to add their own metadata fields.
    |annotations| is a list of dicts that each contain a name and value field,
    and this method looks for 'name' fields that are prefixed with
    ENDPOINT_NAME_ANNOTATION_PREFIX and treats the associated 'value' as the
    name of an endpoint that psyched will expect to be registered from within
    this sandbox.

    Args:
      wrapper: instance of SandboxSpecWrapper.
      annotations: list of dicts, each with a name and value field.
    """
    for annotation in annotations:
      json_lib.AssertIsInstance(annotation, dict, 'a single annotation')
      name = json_lib.GetValueOfType(
          annotation, KEY_ANNOTATION_NAME, unicode, 'annotation name')
      if not IsValidAcName(name):
        raise ValueError('Annotation name "%s" contains illegal characters.' %
                         name)
      if name.startswith(ENDPOINT_NAME_ANNOTATION_PREFIX):
        endpoint_name = json_lib.GetValueOfType(
            annotation, KEY_ANNOTATION_VALUE, unicode, 'endpoint name value')
        if not IsValidAcName(name):
          raise ValueError('Endpoint name "%s" contains illegal characters.' %
                           endpoint_name)
        wrapper.AddEndpointName(endpoint_name)
Пример #5
0
def _ExtractLinuxCapNames(app_dict):
  """Parses the set of Linux capabilities for an executable.

  Args:
    app_dict: dictionary defining an executable.

  Returns:
    List of names of Linux capabilities (e.g. ['CAP_CHOWN']).
  """
  if KEY_APP_ISOLATORS not in app_dict:
    return []

  isolator_list = json_lib.GetValueOfType(
      app_dict, KEY_APP_ISOLATORS, list,
      'list of isolators for application')
  linux_cap_isolators = []

  # Look for any isolators related to capability sets.
  for isolator in isolator_list:
    json_lib.AssertIsInstance(isolator, dict, 'isolator instance')
    isolator_name = json_lib.GetValueOfType(
        isolator, ISOLATOR_KEY_NAME, unicode, 'isolator name')
    if not isolator_name.startswith(ISOLATOR_NAME_PREFIX):
      continue
    if isolator_name != ISOLATOR_NAME_RETAIN_SET:
      raise ValueError('Capabilities may only be specified as %s' %
                       ISOLATOR_NAME_RETAIN_SET)
    linux_cap_isolators.append(isolator)

  # We may have only a single isolator.
  if len(linux_cap_isolators) > 1:
    raise ValueError('Found two lists of Linux caps for an executable')
  if not linux_cap_isolators:
    return []

  value = json_lib.GetValueOfType(
      linux_cap_isolators[0], ISOLATOR_KEY_VALUE, dict,
      'Linux cap isolator value')
  caps = json_lib.GetValueOfType(
      value, ISOLATOR_KEY_VALUE_SET, list, 'Linux cap isolator set')
  for cap in caps:
    json_lib.AssertIsInstance(cap, unicode, 'Linux capability in set.')

  return caps
Пример #6
0
  def GetSandboxSpec(self, appc_contents, sandbox_spec_name):
    """Create a SandboxSpec encoding the information in an appc pod manifest.

    Args:
      appc_contents: string contents of an appc pod manifest
      sandbox_spec_name: string unique name of this sandbox.

    Returns:
      an instance of SandboxSpec.
    """
    wrapper = SandboxSpecWrapper()
    overlay_name = None

    app_list = json_lib.GetValueOfType(
        appc_contents, KEY_APPS_LIST, list, 'app list')
    for app in app_list:
      json_lib.AssertIsInstance(app, dict, 'app')

      # Aid debugging of problems in specific apps.
      app_name = json_lib.GetValueOfType(
          app, KEY_APP_NAME, unicode, 'app name')
      if not IsValidAcName(app_name):
        raise ValueError('Application name "%s" contains illegal characters.' %
                         app_name)
      logging.debug('Processing application "%s".', app_name)

      # Get the name of the image, check that it's consistent other image names.
      image = json_lib.GetValueOfType(
          app, KEY_APP_IMAGE, dict, 'image specification for app')
      image_name = json_lib.GetValueOfType(
          image, KEY_APP_IMAGE_NAME, unicode, 'image name')
      if not IsValidAcName(image_name):
        raise ValueError('Image name "%s" contains illegal characters.' %
                         image_name)

      if overlay_name and overlay_name != image_name:
        raise ValueError(
            'All elements of "apps" must have the same image.name.')
      overlay_name = image_name

      # Add the executable corresponding to this app to our SandboxSpec.
      self._FillInExecutableFromApp(wrapper, app)

    if not overlay_name:
      raise ValueError('Overlays must declare at least one app')

    annotation_list = json_lib.GetValueOfType(
        appc_contents, KEY_ANNOTATIONS_LIST, list, 'list of all annotations')
    self._FillInEndpointNamesFromAnnotations(wrapper, annotation_list)

    wrapper.SetName(sandbox_spec_name)
    return wrapper.sandbox_spec
    def _AddGroup(self, group_spec):
        """Add a group to this account database based on |group_spec|.

    Args:
      group_spec: dict of information from an accounts database.
          This fragment is expected to have been parsed from
          developer supplied JSON and will be type checked.
    """
        # By default, groups don't get a fixed GID.
        group_spec.setdefault(GROUP_FIXED_ID_KEY, False)
        # By default, groups don't get a password.
        group_spec.setdefault(GROUP_PASSWORD_KEY, u'!')
        # By default, groups are not defunct.
        group_spec.setdefault(GROUP_DEFUNCT_KEY, False)

        name = json_lib.PopValueOfType(group_spec, GROUP_NAME_KEY,
                                       six.text_type,
                                       'groupname from group spec')
        password = json_lib.PopValueOfType(group_spec, GROUP_PASSWORD_KEY,
                                           six.text_type,
                                           'password for group %s' % name)
        gid = json_lib.PopValueOfType(group_spec, GROUP_ID_KEY, int,
                                      'gid for group %s' % name)
        users = json_lib.PopValueOfType(group_spec, GROUP_USERS_KEY, list,
                                        'users in group %s' % name)
        is_fixed_id = json_lib.PopValueOfType(
            group_spec, GROUP_FIXED_ID_KEY, bool,
            'whether GID for group %s is fixed' % name)
        is_defunct = json_lib.PopValueOfType(
            group_spec, GROUP_DEFUNCT_KEY, bool,
            'whether group %s is defunct' % name)

        for username in users:
            json_lib.AssertIsInstance(username, six.text_type,
                                      'user in group %s' % name)

        if group_spec:
            raise ValueError('Unexpected keys in group spec for group %s: %r' %
                             (name, group_spec.keys()))

        self.groups[name] = Group(name=name,
                                  password=password,
                                  gid=gid,
                                  users=users,
                                  is_fixed_id=is_fixed_id,
                                  is_defunct=is_defunct)
Пример #8
0
def _GetPortList(desired_protocol, appc_port_list):
  """Get the list of ports opened for |desired_protocol| from |appc_port_list|.

  Args:
    desired_protocol: one of VALID_PROTOCOLS.
    appc_port_list: list of port specifications from a appc pod manifest.

  Returns:
    Instance of PortSpec.
  """
  # The port specification is optional.
  if appc_port_list is None:
    return PortSpec(False, [])

  json_lib.AssertIsInstance(appc_port_list, list, 'port specification list')

  allow_all = False
  port_list = []
  for port_dict in appc_port_list:
    json_lib.AssertIsInstance(port_dict, dict, 'port specification')
    port_dict = copy.deepcopy(port_dict)

    # By default, we open a single specified port.
    port_dict.setdefault(PORT_SPEC_COUNT, 1)
    # By default, don't set socket activated.
    port_dict.setdefault(PORT_SPEC_SOCKET_ACTIVATED, False)

    # We don't actually use the port name, but it's handy for documentation
    # and standard adherence to enforce its existence.
    port_name = json_lib.PopValueOfType(
        port_dict, PORT_SPEC_NAME, unicode, 'port name')
    logging.debug('Validating appc specifcation of "%s"', port_name)
    port = json_lib.PopValueOfType(port_dict, PORT_SPEC_PORT, int, 'port')
    protocol = json_lib.PopValueOfType(
        port_dict, PORT_SPEC_PROTOCOL, unicode, 'protocol')

    count = json_lib.PopValueOfType(
        port_dict, PORT_SPEC_COUNT, int, 'port range count')

    # We also don't use the socketActivated flag, but we should tolerate safe
    # values.
    socket_activated = json_lib.PopValueOfType(
        port_dict, PORT_SPEC_SOCKET_ACTIVATED, bool, 'socket activated flag')

    # Validate everything before acting on it.
    if protocol not in VALID_PROTOCOLS:
      raise ValueError('Port protocol must be in %r, not "%s"' %
                       (VALID_PROTOCOLS, protocol))
    if protocol != desired_protocol:
      continue

    if socket_activated != False:
      raise ValueError('No support for socketActivated==True in %s' % port_name)

    if port_dict:
      raise ValueError('Unknown keys found in port spec %s: %r' %
                       (port_name, port_dict.keys()))

    if port == -1:
      # Remember that we're going to return that all ports are opened, but
      # continue validating all the remaining specifications.
      allow_all = True
      continue

    # Now we know it's not the wildcard port, and that we've never declared
    # a wildcard for this protocol.
    port = remote_access.NormalizePort(port)

    if count < 1:
      raise ValueError('May only specify positive port ranges for %s' %
                       port_name)
    if port + count >= 65536:
      raise ValueError('Port range extends past max port number for %s' %
                       port_name)

    for effective_port in xrange(port, port + count):
      port_list.append(effective_port)

  return PortSpec(allow_all, port_list)