def __str__(self): """Render the output of the PF policy into config.""" target = [] pretty_platform = '%s%s' % (self._PLATFORM[0].upper(), self._PLATFORM[1:]) for (header, filter_name, filter_type, terms) in self.pf_policies: # Add comments for this filter target.append('# %s %s Policy' % (pretty_platform, header.FilterName(self._PLATFORM))) # reformat long text comments, if needed comments = aclgenerator.WrapWords(header.comment, 70) if comments and comments[0]: for line in comments: target.append('# %s' % line) target.append('#') # add the p4 tags target.extend(aclgenerator.AddRepositoryTags('# ')) target.append('# ' + filter_type) # add the terms for term in terms: term_str = str(term) if term_str: target.append(term_str) target.append('') return '\n'.join(target)
def __str__(self): """Render the output of the PF policy into config.""" target = [] pretty_platform = '%s%s' % (self._PLATFORM[0].upper(), self._PLATFORM[1:]) # Create address table. for name in sorted(self.address_book): entries = ',\\\n'.join(str(x) for x in sorted(self.address_book[name], key=int)) target.append('table <%s> {%s}' % (name, entries)) # pylint: disable=unused-variable for (header, filter_name, filter_type, terms) in self.pf_policies: # Add comments for this filter target.append('# %s %s Policy' % (pretty_platform, header.FilterName(self._PLATFORM))) # reformat long text comments, if needed comments = aclgenerator.WrapWords(header.comment, 70) if comments and comments[0]: for line in comments: target.append('# %s' % line) target.append('#') # add the p4 tags target.extend(aclgenerator.AddRepositoryTags('# ')) target.append('# ' + filter_type) # add the terms for term in terms: term_str = str(term) if term_str: target.append(term_str) target.append('') return '\n'.join(target)
def __str__(self): target = [] pretty_platform = '%s%s' % (self._PLATFORM[0].upper(), self._PLATFORM[1:]) if self._RENDER_PREFIX: target.append(self._RENDER_PREFIX) for (header, filter_name, filter_type, default_action, terms) in self.iptables_policies: # Add comments for this filter target.append('# %s %s Policy' % (pretty_platform, header.FilterName(self._PLATFORM))) # reformat long text comments, if needed comments = aclgenerator.WrapWords(header.comment, 70) if comments and comments[0]: for line in comments: target.append('# %s' % line) target.append('#') # add the p4 tags target.extend(aclgenerator.AddRepositoryTags('# ')) target.append('# ' + filter_type) if filter_name in self._GOOD_FILTERS: if default_action: target.append(self._DEFAULTACTION_FORMAT % (filter_name, default_action)) elif self._PLATFORM == 'speedway': # always specify the default filter states for speedway, # if default action policy not specified for iptables, do nothing. target.append(self._DEFAULTACTION_FORMAT % (filter_name, self._DEFAULT_ACTION)) else: # Custom chains have no concept of default policy. target.append(self._DEFAULTACTION_FORMAT_CUSTOM_CHAIN % filter_name) # add the terms for term in terms: term_str = str(term) if term_str: target.append(term_str) if self._RENDER_SUFFIX: target.append(self._RENDER_SUFFIX) target.append('') return '\n'.join(target)
def __str__(self): """Render config output from this term object.""" # Verify platform specific terms. Skip whole term if platform does not # match. if self.term.platform: if 'srx' not in self.term.platform: return '' if self.term.platform_exclude: if 'srx' in self.term.platform_exclude: return '' ret_str = [] # COMMENTS comment_max_width = 68 if self.term.owner: self.term.comment.append('Owner: %s' % self.term.owner) comments = aclgenerator.WrapWords(self.term.comment, comment_max_width) if comments and comments[0]: ret_str.append(JuniperSRX.INDENT * 3 + '/*') for line in comments: ret_str.append(JuniperSRX.INDENT * 3 + line) ret_str.append(JuniperSRX.INDENT * 3 + '*/') ret_str.append(JuniperSRX.INDENT * 3 + 'policy ' + self.term.name + ' {') ret_str.append(JuniperSRX.INDENT * 4 + 'match {') # SOURCE-ADDRESS if self.term.source_address: saddr_check = set() for saddr in self.term.source_address: saddr_check.add(saddr.parent_token) saddr_check = sorted(saddr_check) source_address_string = '' for addr in saddr_check: source_address_string += addr + ' ' ret_str.append(JuniperSRX.INDENT * 5 + 'source-address [ ' + source_address_string + '];') else: ret_str.append(JuniperSRX.INDENT * 5 + 'source-address any;') # DESTINATION-ADDRESS if self.term.destination_address: daddr_check = [] for daddr in self.term.destination_address: daddr_check.append(daddr.parent_token) daddr_check = set(daddr_check) daddr_check = list(daddr_check) daddr_check.sort() destination_address_string = '' for addr in daddr_check: destination_address_string += addr + ' ' ret_str.append(JuniperSRX.INDENT * 5 + 'destination-address [ ' + destination_address_string + '];') else: ret_str.append(JuniperSRX.INDENT * 5 + 'destination-address any;') # APPLICATION if (not self.term.source_port and not self.term.destination_port and not self.term.icmp_type and not self.term.protocol): ret_str.append(JuniperSRX.INDENT * 5 + 'application any;') else: ret_str.append(JuniperSRX.INDENT * 5 + 'application ' + self.term.name + '-app;') ret_str.append(JuniperSRX.INDENT * 4 + '}') # ACTIONS for action in self.term.action: ret_str.append(JuniperSRX.INDENT * 4 + 'then {') ret_str.append(JuniperSRX.INDENT * 5 + self._ACTIONS.get(str(action)) + ';') # LOGGING if self.term.logging: ret_str.append(JuniperSRX.INDENT * 5 + 'log {') ret_str.append(JuniperSRX.INDENT * 6 + 'session-init;') ret_str.append(JuniperSRX.INDENT * 5 + '}') ret_str.append(JuniperSRX.INDENT * 4 + '}') ret_str.append(JuniperSRX.INDENT * 3 + '}') # OPTIONS if self.term.option: raise SRXOptionError( 'Options are not implemented yet, please remove ' + 'from term %s' % self.term.name) # VERBATIM if self.term.verbatim: raise SRXVerbatimError( 'Verbatim is not implemented, please remove ' + 'the offending term %s.' % self.term.name) return '\n'.join(ret_str)
def __str__(self): """Render config output from this term object.""" # Verify platform specific terms. Skip whole term if platform does not # match. if self.term.platform: if self._PLATFORM not in self.term.platform: return '' if self.term.platform_exclude: if self._PLATFORM in self.term.platform_exclude: return '' ret_str = [] self._SetDefaultAction() # Create a new term ret_str.append('\n# term %s' % self.term.name) comments = aclgenerator.WrapWords(self.term.comment, 80) # append comments to output if comments and comments[0]: for line in comments: ret_str.append('# %s' % str(line)) if str(self.term.action[0]) not in self._ACTION_TABLE: raise aclgenerator.UnsupportedFilterError('%s %s %s %s' % ( '\n', self.term.name, self.term.action[0], 'action not currently supported.')) if self.direction and str(self.direction) not in self._DIRECTION_TABLE: raise aclgenerator.UnsupportedFilterError('%s %s %s %s' % ( '\n', self.term.name, self.term.direction, 'direction not currently supported.')) # protocol if self.term.protocol: protocol = self.term.protocol else: protocol = [] if self.term.protocol_except: raise aclgenerator.UnsupportedFilterError('%s %s %s' % ( '\n', self.term.name, 'protocol_except logic not currently supported.')) # source address term_saddrs = self._CheckAddressAf(self.term.source_address) if not term_saddrs: logging.debug(self.NO_AF_LOG_ADDR.substitute(term=self.term.name, direction='source', af=self.af)) return '' term_saddr = self._GenerateAddrStatement( term_saddrs, self.term.source_address_exclude) # destination address term_daddrs = self._CheckAddressAf(self.term.destination_address) if not term_daddrs: logging.debug(self.NO_AF_LOG_ADDR.substitute(term=self.term.name, direction='destination', af=self.af)) return '' term_daddr = self._GenerateAddrStatement( term_daddrs, self.term.destination_address_exclude) # ports source_port = [] destination_port = [] if self.term.source_port: source_port = self._GeneratePortStatement(self.term.source_port) if self.term.destination_port: destination_port = self._GeneratePortStatement(self.term.destination_port) # icmp-type icmp_types = [''] if self.term.icmp_type: if self.af != 'mixed': af = self.af elif protocol == ['icmp']: af = 'inet' elif protocol == ['icmpv6']: af = 'inet6' else: raise aclgenerator.UnsupportedFilterError('%s %s %s' % ( '\n', self.term.name, 'icmp protocol is not defined or not supported.')) icmp_types = self.NormalizeIcmpTypes( self.term.icmp_type, protocol, af) # options tcp_flags_set = [] tcp_flags_check = [] for next_opt in [str(x) for x in self.term.option]: for next_flag in self._TCP_FLAGS_TABLE: if next_opt.find(next_flag) == 0: if protocol != ['tcp']: raise aclgenerator.UnsupportedFilterError('%s %s %s' % ( '\n', self.term.name, 'tcp flags may only be specified with tcp protocol.')) tcp_flags_set.append(self._TCP_FLAGS_TABLE.get(next_flag)) tcp_flags_check.append(self._TCP_FLAGS_TABLE.get(next_flag)) # If tcp-established is set, override any of the flags above with the # S/SA flags. Issue an error if flags are specified with 'established'. for opt in [str(x) for x in self.term.option]: if opt == 'established' or opt == 'tcp-established': if tcp_flags_set or tcp_flags_check: raise aclgenerator.UnsupportedFilterError('%s %s %s' % ( '\n', self.term.name, 'tcp flags may not be specified with tcp-established.')) # We need to set 'flags A/A' for established regardless of whether or # not we're stateful: # - if we stateful, the default is 'flags S/SA' which prevent writing # rules for reply packets. # - if we're stateless, this is the only way to do it. if not protocol or 'tcp' in protocol: tcp_flags_set.append(self._TCP_FLAGS_TABLE.get('ack')) tcp_flags_check.append(self._TCP_FLAGS_TABLE.get('ack')) # The default behavior of pf is 'keep state flags S/SA'. If we're not # stateless, and if flags have not been specified explicitly via options, # append that here. Note that pf allows appending flags for udp and icmp; # they are just ignored, as long as TCP is in the proto. This lets you # doing things like 'proto { tcp udp icmp } flags S/SA' and have the flags # only applied to the tcp bits that match. However, the policy description # language prohibits setting flags on non-TCP, since it doesn't make sense # on all platforms. if ((not protocol or protocol == ['tcp']) and self.stateful and not tcp_flags_set and not tcp_flags_check): tcp_flags_set.append(self._TCP_FLAGS_TABLE.get('syn')) tcp_flags_check.append(self._TCP_FLAGS_TABLE.get('syn')) tcp_flags_check.append(self._TCP_FLAGS_TABLE.get('ack')) ret_str.extend(self._FormatPart( self.term.action[0], self.direction, self.term.logging, self.af, protocol, term_saddr, source_port, term_daddr, destination_port, tcp_flags_set, tcp_flags_check, icmp_types, self.options, self.stateful,)) return '\n'.join(str(v) for v in ret_str if v is not '')
def __str__(self): # Verify platform specific terms. Skip whole term if platform does not # match. if self.term.platform: if self._PLATFORM not in self.term.platform: return '' if self.term.platform_exclude: if self._PLATFORM in self.term.platform_exclude: return '' ret_str = [] # Don't render icmpv6 protocol terms under inet, or icmp under inet6 if ((self.af == 'inet6' and 'icmp' in self.term.protocol) or (self.af == 'inet' and 'icmpv6' in self.term.protocol)): ret_str.append('# Term %s' % self.term.name) ret_str.append('# not rendered due to protocol/AF mismatch.') return '\n'.join(ret_str) # Term verbatim output - this will skip over most normal term # creation code by returning early. Warnings provided in policy.py if self.term.verbatim: for next_verbatim in self.term.verbatim: if next_verbatim.value[0] == self._PLATFORM: ret_str.append(str(next_verbatim.value[1])) return '\n'.join(ret_str) # We don't support these keywords for filtering, so unless users # put in a "verbatim:: iptables" statement, any output we emitted # would misleadingly suggest that we applied their filters. # Instead, we fail loudly. if self.term.ether_type: raise UnsupportedFilterError( '\n%s %s %s %s' % ('ether_type unsupported by', self._PLATFORM, '\nError in term', self.term.name)) if self.term.address: raise UnsupportedFilterError( '\n%s %s %s %s %s' % ('address unsupported by', self._PLATFORM, '- specify source or dest', '\nError in term:', self.term.name)) if self.term.port: raise UnsupportedFilterError( '\n%s %s %s %s %s' % ('port unsupported by', self._PLATFORM, '- specify source or dest', '\nError in term:', self.term.name)) # Create a new term ret_str.append('-N %s' % self.term_name) # New term if self._PREJUMP_FORMAT: ret_str.append(self._PREJUMP_FORMAT % (self.filter, self.term_name)) if self.term.owner: self.term.comment.append('Owner: %s' % self.term.owner) # reformat long comments, if needed # # iptables allows individual comments up to 256 chars. # But our generator will limit a single comment line to < 120, using: # max = 119 - 27 (static chars in comment command) - [length of term name] comment_max_width = 92 - len(self.term_name) if comment_max_width < 40: comment_max_width = 40 comments = aclgenerator.WrapWords(self.term.comment, comment_max_width) # append comments to output if comments and comments[0]: for line in comments: if not line: continue # iptables-restore does not like 0-length comments. # term comments ret_str.append('-A %s -m comment --comment "%s"' % (self.term_name, str(line))) # if terms does not specify action, use filter default action if not self.term.action: self.term.action[0].value = self.default_action # Unsupported configuration; in the case of 'accept' or 'next', we # skip the rule. In other cases, we blow up (raise an exception) # to ensure that this is not considered valid configuration. if self.term.source_prefix or self.term.destination_prefix: if str(self.term.action[0]) not in set(['accept', 'next']): raise UnsupportedFilterError( '%s %s %s %s %s %s %s %s' % ('\nTerm', self.term.name, 'has action', str(self.term.action[0]), 'with source_prefix or destination_prefix,', ' which is unsupported in', self._PLATFORM, 'iptables output.')) return ('# skipped %s due to source or destination prefix rule' % self.term.name) # protocol if self.term.protocol: protocol = self.term.protocol else: protocol = ['all'] if self.term.protocol_except: raise UnsupportedFilterError( '%s %s %s' % ('\n', self.term.name, 'protocol_except logic not currently supported.')) # source address term_saddr = self.term.source_address exclude_saddr = self.term.source_address_exclude term_saddr_excluded = [] if not term_saddr: term_saddr = [self._all_ips] if exclude_saddr: term_saddr_excluded.extend( nacaddr.ExcludeAddrs(term_saddr, exclude_saddr)) # destination address term_daddr = self.term.destination_address exclude_daddr = self.term.destination_address_exclude term_daddr_excluded = [] if not term_daddr: term_daddr = [self._all_ips] if exclude_daddr: term_daddr_excluded.extend( nacaddr.ExcludeAddrs(term_daddr, exclude_daddr)) # Just to be safe, always have a result of at least 1 to avoid * by zero # returning incorrect results (10src*10dst=100, but 10src*0dst=0, not 10) bailout_count = len(exclude_saddr) + len(exclude_daddr) + ( (len(self.term.source_address) or 1) * (len(self.term.destination_address) or 1)) exclude_count = ((len(term_saddr_excluded) or 1) * (len(term_daddr_excluded) or 1)) # Use bailout jumps for excluded addresses if it results in fewer output # lines than nacaddr.ExcludeAddrs() method. if exclude_count < bailout_count: exclude_saddr = [] exclude_daddr = [] if term_saddr_excluded: term_saddr = term_saddr_excluded if term_daddr_excluded: term_daddr = term_daddr_excluded # With many sources and destinations, iptables needs to generate the # cartesian product of sources and destinations. If there are no # exclude rules, this can instead be written as exclude [0/0 - # srcs], exclude [0/0 - dsts]. v4_src_count = len([x for x in term_saddr if x.version == 4]) v4_dst_count = len([x for x in term_daddr if x.version == 4]) v6_src_count = len([x for x in term_saddr if x.version == 6]) v6_dst_count = len([x for x in term_daddr if x.version == 6]) num_pairs = v4_src_count * v4_dst_count + v6_src_count * v6_dst_count if num_pairs > 100: new_exclude_source = nacaddr.ExcludeAddrs([self._all_ips], term_saddr) new_exclude_dest = nacaddr.ExcludeAddrs([self._all_ips], term_daddr) # Invert the shortest list that does not already have exclude addresses if len(new_exclude_source) < len( new_exclude_dest) and not exclude_saddr: if len(new_exclude_source) + len(term_daddr) < num_pairs: exclude_saddr = new_exclude_source term_saddr = [self._all_ips] elif not exclude_daddr: if len(new_exclude_dest) + len(term_saddr) < num_pairs: exclude_daddr = new_exclude_dest term_daddr = [self._all_ips] # ports source_port = [] destination_port = [] if self.term.source_port: source_port = self.term.source_port if self.term.destination_port: destination_port = self.term.destination_port # icmp-types icmp_types = [''] if self.term.icmp_type: icmp_types = self.NormalizeIcmpTypes(self.term.icmp_type, protocol, self.af) source_interface = '' if self.term.source_interface: source_interface = self.term.source_interface destination_interface = '' if self.term.destination_interface: destination_interface = self.term.destination_interface log_hits = False if self.term.logging: # Iptables sends logs to hosts configured syslog log_hits = True # options tcp_flags = [] tcp_track_options = [] for next_opt in [str(x) for x in self.term.option]: # # Sanity checking and high-ports are added as appropriate in # pre-processing that is done in __str__ within class Iptables. # Option established will add destination port high-ports if protocol # contains only tcp, udp or both. This is done earlier in class Iptables. # if ((next_opt.find('established') == 0 or next_opt.find('tcp-established') == 0) and 'ESTABLISHED' not in [x.strip() for x in self.options]): if next_opt.find( 'tcp-established') == 0 and protocol != ['tcp']: raise TcpEstablishedError('%s %s %s' % ( '\noption tcp-established can only be applied for proto tcp.', '\nError in term:', self.term.name)) if self.trackstate: # Use nf_conntrack to track state -- works with any proto self.options.append('-m state --state ESTABLISHED,RELATED') elif protocol == ['tcp']: # Simple established-only rule for TCP: Must have ACK field # (SYN/ACK or subsequent ACK), or RST and no other flags. tcp_track_options = [(['ACK'], ['ACK']), (['SYN', 'FIN', 'ACK', 'RST'], ['RST'])] # Iterate through flags table, and create list of tcp-flags to append for next_flag in self._TCP_FLAGS_TABLE: if next_opt.find(next_flag) == 0: tcp_flags.append(self._TCP_FLAGS_TABLE.get(next_flag)) if next_opt in self._KNOWN_OPTIONS_MATCHERS: self.options.append(self._KNOWN_OPTIONS_MATCHERS[next_opt]) if self.term.packet_length: # Policy format is "#-#", but iptables format is "#:#" self.options.append('-m length --length %s' % self.term.packet_length.replace('-', ':')) if self.term.fragment_offset: self.options.append('-m u32 --u32 4&0x1FFF=%s' % self.term.fragment_offset.replace('-', ':')) for saddr in exclude_saddr: ret_str.extend( self._FormatPart(self.af, '', saddr, '', '', '', '', '', '', '', '', '', '', self._ACTION_TABLE.get('next'))) for daddr in exclude_daddr: ret_str.extend( self._FormatPart(self.af, '', '', '', daddr, '', '', '', '', '', '', '', '', self._ACTION_TABLE.get('next'))) for saddr in term_saddr: for daddr in term_daddr: for icmp in icmp_types: for proto in protocol: for tcp_matcher in tcp_track_options or (([], []), ): ret_str.extend( self._FormatPart( self.af, str(proto), saddr, source_port, daddr, destination_port, self.options, tcp_flags, icmp, tcp_matcher, source_interface, destination_interface, log_hits, self._ACTION_TABLE.get( str(self.term.action[0])))) if self._POSTJUMP_FORMAT: ret_str.append(self._POSTJUMP_FORMAT % (self.filter, self.term_name)) return '\n'.join(str(v) for v in ret_str if v is not '')
def __str__(self): # Verify platform specific terms. Skip whole term if platform does not # match. if self.term.platform: if 'cisco' not in self.term.platform: return '' if self.term.platform_exclude: if 'cisco' in self.term.platform_exclude: return '' ret_str = [] # Term verbatim output - this will skip over normal term creation # code by returning early. Warnings provided in policy.py. if self.term.verbatim: for next_verbatim in self.term.verbatim: if next_verbatim.value[0] == 'cisco': ret_str.append(str(next_verbatim.value[1])) return '\n'.join(ret_str) v4_addresses = [ x for x in self.term.address if type(x) != nacaddr.IPv6 ] if self.filter_name.isdigit(): ret_str.append('access-list %s remark %s' % (self.filter_name, self.term.name)) comment_max_width = 70 comments = aclgenerator.WrapWords(self.term.comment, comment_max_width) if comments and comments[0]: for comment in comments: ret_str.append('access-list %s remark %s' % (self.filter_name, comment)) action = _ACTION_TABLE.get(str(self.term.action[0])) if v4_addresses: for addr in v4_addresses: if addr.prefixlen == 32: ret_str.append('access-list %s %s %s%s' % (self.filter_name, action, addr.ip, self.logstring)) else: ret_str.append('access-list %s %s %s %s%s' % (self.filter_name, action, addr.network, addr.hostmask, self.logstring)) else: ret_str.append( 'access-list %s %s %s%s' % (self.filter_name, action, 'any', self.logstring)) else: ret_str.append('remark ' + self.term.name) comment_max_width = 70 comments = aclgenerator.WrapWords(self.term.comment, comment_max_width) if comments and comments[0]: for comment in comments: ret_str.append('remark ' + str(comment)) action = _ACTION_TABLE.get(str(self.term.action[0])) if v4_addresses: for addr in v4_addresses: if addr.prefixlen == 32: ret_str.append(' %s %s%s' % (action, addr.ip, self.logstring)) else: ret_str.append(' %s %s %s%s' % (action, addr.network, addr.hostmask, self.logstring)) else: ret_str.append(' %s %s%s' % (action, 'any', self.logstring)) return '\n'.join(ret_str)
def __str__(self): # Verify platform specific terms. Skip whole term if platform does not # match. if self.term.platform: if 'cisco' not in self.term.platform: return '' if self.term.platform_exclude: if 'cisco' in self.term.platform_exclude: return '' source_address_dict = {} destination_address_dict = {} ret_str = ['\n'] ret_str.append('remark %s' % self.term.name) comment_max_width = 70 comments = aclgenerator.WrapWords(self.term.comment, comment_max_width) if comments and comments[0]: for comment in comments: ret_str.append('remark %s' % str(comment)) # Term verbatim output - this will skip over normal term creation # code by returning early. Warnings provided in policy.py. if self.term.verbatim: for next_verbatim in self.term.verbatim: if next_verbatim.value[0] == 'cisco': ret_str.append(str(next_verbatim.value[1])) return '\n'.join(ret_str) # protocol if not self.term.protocol: protocol = ['ip'] else: # pylint: disable-msg=C6402 protocol = map(self.PROTO_MAP.get, self.term.protocol, self.term.protocol) # pylint: enable-msg=C6402 # addresses source_address = self.term.source_address if not self.term.source_address: source_address = [nacaddr.IPv4('0.0.0.0/0', token='ANY')] source_address_dict[source_address[0].parent_token] = True destination_address = self.term.destination_address if not self.term.destination_address: destination_address = [nacaddr.IPv4('0.0.0.0/0', token='ANY')] destination_address_dict[destination_address[0].parent_token] = True # ports source_port = [()] destination_port = [()] if self.term.source_port: source_port = self.term.source_port if self.term.destination_port: destination_port = self.term.destination_port for saddr in source_address: for daddr in destination_address: for sport in source_port: for dport in destination_port: for proto in protocol: ret_str.append( self._TermletToStr( _ACTION_TABLE.get(str( self.term.action[0])), proto, saddr, sport, daddr, dport)) return '\n'.join(ret_str)
def __str__(self): # Verify platform specific terms. Skip whole term if platform does not # match. if self.term.platform: if self._PLATFORM not in self.term.platform: return '' if self.term.platform_exclude: if self._PLATFORM in self.term.platform_exclude: return '' ret_str = [] # Don't render icmpv6 protocol terms under inet, or icmp under inet6 if ((self.af == 'inet6' and 'icmp' in self.term.protocol) or (self.af == 'inet' and 'icmpv6' in self.term.protocol)): logging.debug( self.NO_AF_LOG_PROTO.substitute(term=self.term.name, proto=self.term.protocol, af=self.af)) return '' # Term verbatim output - this will skip over most normal term # creation code by returning early. Warnings provided in policy.py if self.term.verbatim: for next_verbatim in self.term.verbatim: if next_verbatim.value[0] == self._PLATFORM: ret_str.append(str(next_verbatim.value[1])) return '\n'.join(ret_str) # We don't support these keywords for filtering, so unless users # put in a "verbatim:: iptables" statement, any output we emitted # would misleadingly suggest that we applied their filters. # Instead, we fail loudly. if self.term.ether_type: raise UnsupportedFilterError( '\n%s %s %s %s' % ('ether_type unsupported by', self._PLATFORM, '\nError in term', self.term.name)) if self.term.address: raise UnsupportedFilterError( '\n%s %s %s %s %s' % ('address unsupported by', self._PLATFORM, '- specify source or dest', '\nError in term:', self.term.name)) if self.term.port: raise UnsupportedFilterError( '\n%s %s %s %s %s' % ('port unsupported by', self._PLATFORM, '- specify source or dest', '\nError in term:', self.term.name)) # Create a new term self._SetDefaultAction() if self._TERM_FORMAT: ret_str.append(self._TERM_FORMAT.substitute(term=self.term_name)) if self._PREJUMP_FORMAT: ret_str.append( self._PREJUMP_FORMAT.substitute(filter=self.filter, term=self.term_name)) if self.term.owner: self.term.comment.append('Owner: %s' % self.term.owner) # reformat long comments, if needed # # iptables allows individual comments up to 256 chars. # But our generator will limit a single comment line to < 120, using: # max = 119 - 27 (static chars in comment command) - [length of term name] comment_max_width = 92 - len(self.term_name) if comment_max_width < 40: comment_max_width = 40 comments = aclgenerator.WrapWords(self.term.comment, comment_max_width) # append comments to output if comments and comments[0]: for line in comments: if not line: continue # iptables-restore does not like 0-length comments. # term comments ret_str.append( self._COMMENT_FORMAT.substitute(filter=self.filter, term=self.term_name, comment=str(line))) # Unsupported configuration; in the case of 'accept' or 'next', we # skip the rule. In other cases, we blow up (raise an exception) # to ensure that this is not considered valid configuration. if self.term.source_prefix or self.term.destination_prefix: if str(self.term.action[0]) not in set(['accept', 'next']): raise UnsupportedFilterError( '%s %s %s %s %s %s %s %s' % ('\nTerm', self.term.name, 'has action', str(self.term.action[0]), 'with source_prefix or destination_prefix,', ' which is unsupported in', self._PLATFORM, 'iptables output.')) return ('# skipped %s due to source or destination prefix rule' % self.term.name) # protocol if self.term.protocol: protocol = self.term.protocol else: protocol = ['all'] if self.term.protocol_except: raise UnsupportedFilterError( '%s %s %s' % ('\n', self.term.name, 'protocol_except logic not currently supported.')) (term_saddr, exclude_saddr, term_daddr, exclude_daddr) = self._CalculateAddresses( self.term.source_address, self.term.source_address_exclude, self.term.destination_address, self.term.destination_address_exclude) if not term_saddr: logging.debug( self.NO_AF_LOG_ADDR.substitute(term=self.term.name, direction='source', af=self.af)) return '' if not term_daddr: logging.debug( self.NO_AF_LOG_ADDR.substitute(term=self.term.name, direction='destination', af=self.af)) return '' # ports source_port = [] destination_port = [] if self.term.source_port: source_port = self.term.source_port if self.term.destination_port: destination_port = self.term.destination_port # icmp-types icmp_types = [''] if self.term.icmp_type: icmp_types = self.NormalizeIcmpTypes(self.term.icmp_type, protocol, self.af) source_interface = '' if self.term.source_interface: source_interface = self.term.source_interface destination_interface = '' if self.term.destination_interface: destination_interface = self.term.destination_interface log_hits = False if self.term.logging: # Iptables sends logs to hosts configured syslog log_hits = True # options tcp_flags = [] tcp_track_options = [] for next_opt in [str(x) for x in self.term.option]: # # Sanity checking and high-ports are added as appropriate in # pre-processing that is done in __str__ within class Iptables. # Option established will add destination port high-ports if protocol # contains only tcp, udp or both. This is done earlier in class Iptables. # if ((next_opt.find('established') == 0 or next_opt.find('tcp-established') == 0) and 'ESTABLISHED' not in [x.strip() for x in self.options]): if next_opt.find( 'tcp-established') == 0 and protocol != ['tcp']: raise TcpEstablishedError('%s %s %s' % ( '\noption tcp-established can only be applied for proto tcp.', '\nError in term:', self.term.name)) if self.trackstate: # Use nf_conntrack to track state -- works with any proto self.options.append('-m state --state ESTABLISHED,RELATED') elif protocol == ['tcp']: # Simple established-only rule for TCP: Must have ACK field # (SYN/ACK or subsequent ACK), or RST and no other flags. tcp_track_options = [(['ACK'], ['ACK']), (['SYN', 'FIN', 'ACK', 'RST'], ['RST'])] # Iterate through flags table, and create list of tcp-flags to append for next_flag in self._TCP_FLAGS_TABLE: if next_opt.find(next_flag) == 0: tcp_flags.append(self._TCP_FLAGS_TABLE.get(next_flag)) if next_opt in self._KNOWN_OPTIONS_MATCHERS: self.options.append(self._KNOWN_OPTIONS_MATCHERS[next_opt]) if self.term.packet_length: # Policy format is "#-#", but iptables format is "#:#" self.options.append('-m length --length %s' % self.term.packet_length.replace('-', ':')) if self.term.fragment_offset: self.options.append('-m u32 --u32 4&0x1FFF=%s' % self.term.fragment_offset.replace('-', ':')) for saddr in exclude_saddr: ret_str.extend( self._FormatPart('', saddr, '', '', '', '', '', '', '', '', '', '', self._ACTION_TABLE.get('next'))) for daddr in exclude_daddr: ret_str.extend( self._FormatPart('', '', '', daddr, '', '', '', '', '', '', '', '', self._ACTION_TABLE.get('next'))) for saddr in term_saddr: for daddr in term_daddr: for icmp in icmp_types: for proto in protocol: for tcp_matcher in tcp_track_options or (([], []), ): ret_str.extend( self._FormatPart( str(proto), saddr, source_port, daddr, destination_port, self.options, tcp_flags, icmp, tcp_matcher, source_interface, destination_interface, log_hits, self._ACTION_TABLE.get( str(self.term.action[0])))) if self._POSTJUMP_FORMAT: ret_str.append( self._POSTJUMP_FORMAT.substitute(filter=self.filter, term=self.term_name)) return '\n'.join(str(v) for v in ret_str if v is not '')