예제 #1
0
    def load_local_groups(self, config):
        """
        Load local groups from config file
        Save and return SecurityGroups object

        :param config:
        :rtype : object
        """
        lg.debug("Loading local configuragion")
        self.config = config
        yaml.add_constructor('!include', self._yaml_include)

        try:
            with open(config, 'r') as fp:
                conf = yaml.load(fp)
        except IOError as e:
            # Error while loading file
            raise InvalidConfiguration("Can't read config file %s: %s" %
                                       (config, e))
        except yaml.YAMLError as e:
            # Error while parsing YAML
            if hasattr(e, 'problem_mark'):
                mark = e.problem_mark
                raise InvalidConfiguration(
                    "Can't parse config file %s: error at line %s, column %s" %
                    (config, mark.line + 1, mark.column + 1))
            else:
                raise InvalidConfiguration("Can't parse config file %s: %s" %
                                           (config, e))

        # Remove include keys
        conf = self._fix_include(conf)

        lg.debug("Loading local groups")
        for name, group in conf.iteritems():
            # Initialize SGroup object
            sgroup = SGroup(
                name, None
                if not group.has_key('description') else group['description'])

            if group.has_key('rules'):
                for rule in group['rules']:
                    if rule.has_key('groups'):
                        # For each group, create separate rule
                        # multiple groups are used only to simplify configuration
                        for group in rule['groups']:
                            rule['groups'] = [group]

                            srule = SRule(owner_id=self.owner_id, **rule)
                            sgroup.add_rule(srule)
                    else:
                        # No groups, initialize SRule object
                        srule = SRule(**rule)
                        # Add it into group
                        sgroup.add_rule(srule)

            self.groups[name] = sgroup

        return self.groups
예제 #2
0
    def _check_configuration(self):
        """
        Check configuration
        """
        if self.port and not isinstance(self.port, int):
            raise InvalidConfiguration('Port must be integer for rule %s not %s' % (self.name, type(self.port).__name__))

        if self.port_from and not isinstance(self.port_from, int):
            raise InvalidConfiguration('Parameter port_from must be integer for rule %s not %s' % (self.name, type(self.port_from).__name__))

        if self.port_to and not isinstance(self.port_to, int):
            raise InvalidConfiguration('Parameter port_to must be integer for rule %s not %s' % (self.name, type(self.port_to).__name__))

        if self.protocol and self.protocol not in ['tcp', 'udp', 'icmp', '-1', '6', '17']:
            raise InvalidConfiguration('Protocol must be tcp (6), udp (17) or icmp (-1), not %s for rule %s' % (self.protocol, self.name))
예제 #3
0
    def load_local_groups(self, config, mode):
        """
        Load local groups from config file
        Save and return SecurityGroups object

        :param config:
        :rtype : object
        """
        lg.debug("Loading local configuragion")
        self.config = config
        yaml.add_constructor('!include', self._yaml_include)
        yaml.add_constructor('!include_dir', self._yaml_include_dir)

        try:
            with open(config, 'r') as fp:
                conf = yaml.load(fp)
        except IOError as e:
            # Error while loading file
            raise InvalidConfiguration("Can't read config file %s: %s" %
                                       (config, e))
        except yaml.YAMLError as e:
            # Error while parsing YAML
            if hasattr(e, 'problem_mark'):
                mark = e.problem_mark
                raise InvalidConfiguration(
                    "Can't parse config file %s: error at line %s, column %s" %
                    (config, mark.line + 1, mark.column + 1))
            else:
                raise InvalidConfiguration("Can't parse config file %s: %s" %
                                           (config, e))
        # Empty config file is considered invalid
        if not conf:
            raise InvalidConfiguration("Config file is empty")

        # Remove include keys
        conf = self._fix_include(conf)

        lg.debug("Loading local groups")
        if isinstance(conf, dict):
            for name, group in conf.iteritems():
                if not self.only_groups or name in self.only_groups:
                    # Initialize SGroup object
                    self.groups[name] = self._load_sgroup(name,
                                                          group,
                                                          check_mode=mode)

        return self.groups
예제 #4
0
    def _load_sgroup(self, name, group, check_mode='strict'):
        """
        Return SGroup object with assigned rules
        """
        # Test len of name and description
        if len(name) > 255:
            raise InvalidConfiguration(
                "Name of group '%s' is longer than 255 chars" % (name))
        if group.has_key('description') and len(group['description']) > 255:
            raise InvalidConfiguration(
                "Description of group '%s' is longer than 255 chars" % (name))

        # Test name and description according to spec
        if check_mode == 'strict':
            allowed = '^[a-zA-Z0-9_\- ]+$'
        if check_mode == 'ascii':
            allowed = '^[\x20-\x7E]+$'
        if check_mode == 'vpc':
            allowed = '^[a-zA-Z0-9 ._\-:/()#,@[\]+=&;{}!$*]+$'

        if group.has_key('description') and not re.match(
                allowed, group['description']):
            raise InvalidConfiguration(
                "Description of group '%s' is not valid in %s check_mode" %
                (name, check_mode))

        if not re.match(allowed, name):
            raise InvalidConfiguration(
                "Name of group '%s' is not valid in %s check_mode" %
                (name, check_mode))

        sgroup = SGroup(
            name,
            description=None
            if 'description' not in group.keys() else group['description'],
            vpc_id=None if 'vpc_id' not in group.keys() else group['vpc_id'])

        # Dive into group's rules and create srule objects
        if group.has_key('rules'):
            for rule in group['rules']:
                self._validate(rule, sgroup.name)
                self._load_rule(sgroup, rule)

        return sgroup
예제 #5
0
 def _yaml_include(self, loader, node):
     """
     Include another yaml file from main file
     This is usually done by registering !include tag
     """
     filepath = "%s%s" % ('%s/' % os.path.dirname(self.config) \
                             if os.path.dirname(self.config) else '',
                          node.value)
     try:
         with open(filepath, 'r') as inputfile:
             return yaml.load(inputfile)
     except IOError as e:
         raise InvalidConfiguration("Can't include config file %s: %s" %
                                    (filepath, e))
예제 #6
0
 def check_validity(self):
     """
     Checks if groups mentioned in the rules are defined
     and if ip address is in the correct format (including mask)
     """
     for name, ref in self.groups.iteritems():
         for rule in ref.rules:
             for group in rule.groups:
                 if not self.has_group(group['name']):
                     raise InvalidConfiguration(
                         "Group %s referenced by "
                         "group %s does not exist in the config file" %
                         (group['name'], name))
             if rule.cidr:
                 for ip in rule.cidr:
                     if not re.match(
                             '^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.'
                             '([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.'
                             '([01]?\\d\\d?|2[0-4]\\d|25[0-5])\/(\\d|[1-2]\\d|3[0-2])$',
                             ip):
                         raise InvalidConfiguration(
                             "Wrong format of ip address '%s' "
                             "in the config file" % ip)
예제 #7
0
 def _yaml_include(self, loader, node):
     """
     Include another yaml file from main file
     This is usually done by registering !include tag
     """
     filepath = node.value
     if not os.path.exists(filepath):
         filepath = self._add_config_prefix(node.value)
     try:
         with open(filepath, 'r') as inputfile:
             return yaml.load(inputfile)
     except IOError as e:
         raise InvalidConfiguration("Can't include config file %s: %s" %
                                    (filepath, e))
예제 #8
0
    def _validate(self, rule, group_name):
        """
        Validate given rule
        """
        def _validate_port(item):
            return ('port' in item
                    or ('port_to' in item and 'port_from' in item))

        for keyword in rule:
            # validate used keywords
            if keyword not in [
                    'cidr', 'groups', 'port', 'protocol', 'port_from',
                    'port_to', 'to'
            ]:
                raise InvalidConfiguration("Unknown keyword: %s in group: %s" %
                                           (keyword, group_name))
            # either port or port_from & port_to is mandatory

        # you can specify a rule like this
        #- cidr: [a.b.c.d / 32]
        #  groups: [sg-group-1]
        #  to:
        #  - {port: 100, protocol: tcp}
        #  - {port: 101, protocol: udp}
        #  - {port: 102, protocol: tcp}
        #
        # that means, every rule from 'to' section has to be checked for validity too
        if 'to' in rule:
            rules_array_ok = all(
                _validate_port(rule_array) for rule_array in rule['to'])
        else:
            rules_array_ok = False

        if not (_validate_port(rule) or rules_array_ok):
            raise InvalidConfiguration(
                "Either port or (port_from, port_to) must be in rule: %s in group: %s"
                % (rule, group_name))
예제 #9
0
    def __init__(self, owner_id=None, port=None, port_from=None, port_to=None, groups=None, protocol='tcp', cidr=None, srule_object=None, egress=False):
        """
        Initialize variables
        """
        global ec2
        ec2 = sgmanager.ec2

        # Set rule id
        self._ids = self._ids.next()
        self.object = srule_object

        self.protocol = protocol
        self.port = port
        self.port_from = port_from
        self.port_to = port_to
        self.owner_id = owner_id

        self.egress = egress

        # All ports allowed but only port_to supplied -> complete port range by setting port_from
        if not self.port_from and self.port_to:
            self.port_from = 1

        # Single port can't be supplied together with port range
        if self.port and self.port_from and self.port_to:
            raise InvalidConfiguration('Single port and port range supplied for rule %s' % self._ids)

        self.groups = []
        self.group = None

        # Check validity of groups parameter
        if groups:
            if not isinstance(groups, list):
                # Convert to list so it can be full dict structure or simple group name
                groups = [ groups ]

            # Unify format for granted group permissions
            # it has to contain id and group owner (account id)
            for group in groups:
                if isinstance(group, dict) and len(group) == 1:
                    # There's only one element in dict (suppose it's name),
                    # convert the dict to unify it
                    try:
                        group = group['name']
                    except Exception:
                        raise InvalidConfiguration("Group definition doesn't contain name, rule %s" % self._ids)

                if not isinstance(group, dict):
                    # Empty owner and id, only name supplied, prepare full
                    # structure
                    self.groups.append({
                        'name' : group,
                        'owner': owner_id,
                        'id'   : None,
                    })
                else:
                    # ..otherwise suppose we already have required structure
                    self.groups.append(group)

        # cidr should be None if we have groups
        if groups:
            self.cidr = None
        elif not cidr:
            # No cidr, no groups, allowed from everywhere
            self.cidr = ['0.0.0.0/0']
        else:
            # We have cidr and no groups, unify structure
            # convert string cidr to list
            if cidr and not isinstance(cidr, list):
                self.cidr = [ cidr ]
            else:
                self.cidr = cidr

        self.name = self._generate_name()
        self._check_configuration()

        lg.debug('Loaded rule %s' % self.name)