Changeset 117
- Timestamp:
- 05/14/05 23:46:29 (4 years ago)
- Files:
-
- fwc/trunk/fwc (modified) (26 diffs)
- fwc/trunk/Object.py (modified) (1 diff)
- fwc/trunk/Resolver.py (modified) (5 diffs)
- fwc/trunk/Rule.py (modified) (2 diffs)
- fwc/trunk/util.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
fwc/trunk/fwc
r115 r117 22 22 firewall = Firewall("localhost", resolver) 23 23 24 def to_list(value):25 if type(value) is not list: return [value]26 return value27 28 24 def check_port_protocol(context): 29 25 return 'protocol' in context and context['protocol'] in [ 'tcp', 'udp' ] … … 42 38 for o in resolver.get_objects('network'): 43 39 if o.description: 44 help[o.name] = o.description + "(%s)" % o.value40 help[o.name] = "%s (%s)" % (o.description, o.value) 45 41 else: 46 42 help[o.name] = o.value … … 85 81 # Commands 86 82 87 def insert_rule(context, action, source = [], sport = [], destination = [], dport = [], protocol = None, description = None, where = 'bottom', index = None, state = 'new', log = None ):83 def insert_rule(context, action, source = [], sport = [], destination = [], dport = [], protocol = None, description = None, where = 'bottom', index = None, state = 'new', log = None, reject_type = None, reject_subtype = None): 88 84 if log: 89 85 if log == 'log': … … 92 88 log = log[1] 93 89 if index != None: index = int(index) 94 firewall.add(Rule(action, to _list(source), to_list(sport), to_list(destination), to_list(dport), protocol, description, state, log), where, index)90 firewall.add(Rule(action, tolist(source), tolist(sport), tolist(destination), tolist(dport), protocol, description, state, log, reject_type, reject_subtype), where, index) 95 91 96 92 def remove_rule(context, rules): … … 114 110 cmd += " state ^B" + rule.state + "^B" 115 111 if rule.protocol: 116 cmd += " ^B" + rule.protocol + "^B" 112 try: 113 cmd += ' protocol ^B%s^B' % int(rule.protocol) 114 except: 115 cmd += " ^B" + rule.protocol + "^B" 117 116 if rule.source or rule.sport: 118 117 cmd += " from" … … 127 126 if rule.dport: 128 127 cmd += " port ^B" + ' '.join(rule.dport) + "^B" 128 if rule.reject_type: 129 cmd += " with ^B%s^B" % rule.reject_type 130 if rule.reject_subtype: 131 cmd += " ^B%s^B" % rule.reject_subtype 129 132 if rule.log: 130 133 cmd += " log" 131 134 if type(rule.log) is str: 132 cmd += " ^B'%s'^B" % rule.log135 cmd += " message ^B'%s'^B" % rule.log 133 136 if rule.description: 134 137 cmd += " description ^B'%s'^B" % rule.description … … 143 146 144 147 def list_objects(context, type = Resolver.get_object_types()): 145 for t in to _list(type):148 for t in tolist(type): 146 149 objects = resolver.get_objects(t) 147 for o in sorted(objects ):150 for o in sorted(objects, lambda a, b: cmp(a.name, b.name)): 148 151 str = "%s ^B%s^B ^B^6%s^N" % (o.type, o.name, o.value) 149 152 if o.description: str += " ^2'%s^N'" % o.description … … 158 161 placement_rules = { 159 162 'replace' : { 160 GROUP : 'Rule ordering.',163 GROUP : 10, 161 164 UNLESS_VAR : [ 'old', 'where' ], 162 165 VAR : 'where', … … 169 172 }, 170 173 'top|bottom' : { 171 GROUP : 'Rule ordering.',174 GROUP : 10, 172 175 UNLESS_VAR : 'where', 173 176 VAR : 'where', … … 179 182 }, 180 183 'before|after' : { 181 GROUP : 'Rule ordering.',184 GROUP : 10, 182 185 UNLESS_VAR : 'where', 183 186 VAR : 'where', … … 195 198 196 199 protocol_rules = { 197 'tcp|udp|icmp|ah|esp': {198 GROUP : 'Packet matching.',200 lambda ctx, foo: [x.name for x in resolver.get_objects('protocol')] : { 201 GROUP : 30, 199 202 UNLESS_VAR : 'protocol', 200 203 VAR : 'protocol', 201 HELP : { 202 'tcp' : 'Match TCP packets.', 203 'udp' : 'Match UDP packets.', 204 'icmp' : 'Match ICMP packets.', 205 'ah' : 'Match AH packets.', 206 'esp' : 'Match ESP packets.', 207 }, 204 HELP : lambda x: [(x.name, x.description or 'Protocol %s' % x.value) for x in resolver.get_objects('protocol')], 208 205 JUMP : RETURN, 206 }, 207 'protocol' : { 208 GROUP : 30, 209 HELP : 'Arbitrary IP protocol number.', 210 UNLESS_VAR : 'protocol', 211 '\d+' : { 212 HELP : ('<protocol>', 'IP protocol number.'), 213 JUMP : RETURN, 214 VAR : 'protocol', 215 }, 209 216 }, 210 217 } … … 212 219 # Grammar 213 220 cli = CLI({ 221 'nat' : { 222 MERGE : [ placement_rules, protocol_rules ], 223 GROUP : 10, 224 HELP : 'NAT matching packets.', 225 VAR : 'action', 226 'to' : { 227 HELP : 'Specify destination to NAT to.', 228 Object.NETWORK_PATTERN : { 229 VAR : 'nat_ip', 230 HELP : ('<ip>', 'NAT to IP address.'), 231 'if' : { 232 HELP : 'NAT on the following conditions.', 233 JUMP : 'commands', 234 }, 235 }, 236 }, 237 }, 214 238 'accept|drop|reject' : { 239 GROUP : 10, 215 240 IF : have_modifiable_firewall, 216 241 GLOBAL_LABEL : 'commands', 242 MERGE : [ placement_rules, protocol_rules ], 243 VAR : 'action', 244 ACTION : { 245 HELP : 'Add rule to firewall.', 246 # Only if a source or destination have actually been specified 247 #IF : lambda ctx: 'source' in ctx or 'destination' in ctx, 248 ACTION : insert_rule, 249 }, 250 HELP : { 251 'accept' : 'Add rule accepting matching packets.', 252 'drop' : 'Add rule dropping matching packets.', 253 'reject' : 'Add rule rejecting matching packets.', 254 }, 255 'with' : { 256 IF : lambda ctx: 'reject_type' not in ctx and ctx['action'] == 'reject', 257 HELP : 'Reject with the specified packet type.', 258 'tcp' : { 259 VAR : 'reject_type', 260 HELP : 'Reject with tcp... packet.', 261 'reset' : { 262 VAR : 'reject_subtype', 263 IF : lambda ctx: 'protocol' in ctx and ctx['protocol'] == 'tcp', 264 HELP : 'Reject with TCP reset.', 265 JUMP : 'commands', 266 }, 267 }, 268 'network|host|protocol|admin' : { 269 VAR : 'reject_type', 270 HELP : { 271 'network' : 'Reject with icmp-network... packet.', 272 'host' : 'Reject with icmp-host... packet.', 273 'protocol' : 'Reject with icmp-protocol... packet.', 274 'admin' : 'Reject with icmp-admin... packet.', 275 }, 276 'unreachable' : { 277 HELP : lambda ctx: { 'unreachable' : 'Reject with icmp-%s-unreachable.' % ctx['reject_type'] }, 278 IF : lambda ctx: ctx['reject_type'] != 'admin', 279 VAR : 'reject_subtype', 280 JUMP : 'commands', 281 }, 282 'prohibited' : { 283 HELP : lambda ctx: { 'prohibited' : 'Reject with icmp-%s-prohibited.' % ctx['reject_type'] }, 284 IF : lambda ctx: ctx['reject_type'] in ('network', 'host', 'admin'), 285 VAR : 'reject_subtype', 286 JUMP : 'commands', 287 }, 288 }, 289 290 }, 217 291 'state' : { 218 GROUP : 'Packet matching.',292 GROUP : 20, 219 293 RANGE : 1, 220 294 HELP : 'Match connections in this state (default: new).', … … 230 304 }, 231 305 }, 232 ACTION : {233 HELP : 'Add rule to firewall.',234 # Only if a source or destination have actually been specified235 #IF : lambda ctx: 'source' in ctx or 'destination' in ctx,236 ACTION : insert_rule,237 },238 MERGE : [ placement_rules, protocol_rules ],239 VAR : 'action',240 HELP : {241 'accept' : 'Add rule accepting matching packets.',242 'drop' : 'Add rule dropping matching packets.',243 'reject' : 'Add rule rejecting matching packets.',244 },245 306 'from' : { 246 GROUP : 'Packet matching.',307 GROUP : 20, 247 308 HELP : 'Match source network or port.', 248 309 IF : lambda ctx: ('protocol' in ctx and ctx['protocol'] in ['tcp', 'udp'] and not 'sport' in ctx) or not 'source' in ctx, … … 252 313 JUMP : 'commands', 253 314 JUMP_TO : 'source', 315 GROUP : 20, 254 316 'port' : { 317 GROUP : 20, 255 318 RANGE : 1, 256 319 HELP : 'Match source port.', … … 268 331 'port' : { 269 332 HELP : 'Match source port.', 333 GROUP : 20, 270 334 PORT : { 271 335 HELP : help_port, … … 280 344 }, 281 345 'description' : { 282 GROUP : 'Meta information.',283 346 '.+' : { 284 347 VAR : 'description', … … 290 353 }, 291 354 'to' : { 355 GROUP : 20, 292 356 IF : lambda ctx: ('protocol' in ctx and ctx['protocol'] in ['tcp', 'udp'] and not 'dport' in ctx) or not 'destination' in ctx, 293 357 UNLESS_VAR : [ 'dport', 'destination' ], 294 358 NETWORK : { 359 GROUP : 20, 295 360 LABEL : 'destination', 296 361 VAR : 'destination', … … 298 363 JUMP_TO : 'destination', 299 364 'port' : { 365 GROUP : 20, 300 366 PORT : { 301 367 LABEL : 'dport', … … 311 377 }, 312 378 'port' : { 379 GROUP : 20, 313 380 PORT : { 314 381 LABEL : 'dports', … … 325 392 'log' : { 326 393 VAR : 'log', 394 UNLESS_VAR : 'log', 327 395 HELP : "Log a message when this rule matches.", 328 '.+' : { 329 HELP : ('<message>', 'Custom log message.'), 330 JUMP : 'commands', 331 VAR : 'log', 396 'message' : { 397 HELP : 'Log with custom message.', 398 '.+' : { 399 HELP : ('<message>', 'Custom log message.'), 400 JUMP : 'commands', 401 VAR : 'log', 402 }, 332 403 }, 333 404 JUMP : 'commands', … … 342 413 }, 343 414 'object' : { 344 HELP : ' Manage rulesetobjects.',415 HELP : 'Ruleset manipulation objects.', 345 416 'import' : { 346 HELP : 'Import system default objects.', 417 LABEL : 'import', 418 '|'.join(Resolver.get_object_types()) : { 419 VAR : 'import_types', 420 HELP : lambda ctx: [(x, 'Import system %s objects' % x) for x in Resolver.get_object_types()], 421 ACTION : lambda x, import_types: resolver.populate_defaults(tolist(import_types)), 422 }, 423 HELP : 'Import system objects.', 347 424 ACTION : lambda x: resolver.populate_defaults(), 348 425 }, … … 388 465 'delete' : { 389 466 HELP : 'Delete a ruleset object.', 390 ' network|port': {467 '|'.join(Resolver.get_object_types()) : { 391 468 VAR : 'type', 392 469 HELP : { … … 402 479 ACTION : list_objects, 403 480 }, 404 ' network|port': {481 '|'.join(Resolver.get_object_types()) : { 405 482 VAR : 'type', 406 HELP : { 407 'network' : 'List network objects.', 408 'port' : 'List port objects.', 409 }, 483 HELP : lambda ctx: [(x, 'List %s objects' % x) for x in Resolver.get_object_types()], 410 484 ACTION : list_objects, 411 485 }, … … 413 487 }, 414 488 'delete' : { 489 GROUP : 10, 415 490 IF : have_modifiable_firewall, 416 491 HELP : 'Remove rule(s) from the ruleset.', … … 424 499 }, 425 500 'list' : { 501 GROUP : 10, 426 502 IF : have_firewall, 427 503 HELP : 'List ruleset.', 428 504 ACTION : list_ruleset, 429 505 }, 430 HELP : "Commands to manage the ruleset.",431 506 'move' : { 507 GROUP : 10, 432 508 IF : have_modifiable_firewall, 433 509 HELP : 'Move rule.', … … 514 590 def format_help(help): 515 591 length = 0 516 for h in help: 517 if len(h[0]) > length: 518 length = len(h[0]) 519 for h in help: 520 cprint(" ^B%s^B %s" % (h[0] + (length - len(h[0])) * ' ', h[1])) 592 for group in help: 593 for h in help[group]: 594 if len(h[0]) > length: 595 length = len(h[0]) 596 count = 0 597 for order, group in sorted(help, lambda a, b: cmp(a[0], b[0])): 598 if group: 599 cprint(" ^6%s^N" % group) 600 elif count: 601 print 602 for h in help[(order, group)]: 603 cprint(" ^B%s^B %s" % (h[0] + (length - len(h[0])) * ' ', h[1])) 604 count += 1 521 605 522 606 readline.set_completer(cli_completion) fwc/trunk/Object.py
r114 r117 3 3 NETWORK = 'network' 4 4 PORT = 'port' 5 PROTOCOL = 'protocol' 5 6 6 7 NETWORK_PATTERN = r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?:/\d{1,2})?' 7 8 PORT_PATTERN = r'\d{1,5}' 9 PROTOCOL_PATTERN = r'\d{1,3}' 8 10 9 11 # Regex matching a valid object name fwc/trunk/Resolver.py
r115 r117 3 3 from util import * 4 4 5 def get_object_types(): 6 return ('network', 'port', 'protocol') 7 5 8 class Resolver: 9 """ 10 The Resolver class handles the resolution of symbolic network object 11 names into actual network objects. This currently includes port objects 12 and network objects. 13 14 The canonical name for objects is in the form: 15 16 <type>:<name> 17 18 eg. port:ssh, network:swapoff.org, etc. 19 20 """ 6 21 class Error(Exception): pass 7 22 class InvalidObject(Error): pass … … 11 26 def __init__(self, resolver): 12 27 self.__resolver = resolver 13 self.__types = self.__resolver.get_object_types()28 self.__types = get_object_types() 14 29 self.__type = self.__types.pop() 15 30 self.__objectit = iter(self.__resolver.get_objects(self.__type)) … … 27 42 def __init__(self): 28 43 self.__objects = {} 29 for t in Resolver.get_object_types():44 for t in get_object_types(): 30 45 self.__objects[t] = {} 31 46 32 47 # Set up default objects 33 48 self.add_object(Object(Object.NETWORK, 'any', '0.0.0.0/0', 'Any network address')) 49 self.add_object(Object(Object.NETWORK, 'localhost', '127.0.0.1', 'Local host')) 34 50 self.add_object(Object(Object.PORT, 'any', '0-65535', 'Any port')) 51 # Default protocols 52 self.add_object(Object(Object.PROTOCOL, 'tcp', '6', 'Transmission Control Protocol')) 53 self.add_object(Object(Object.PROTOCOL, 'udp', '17', 'User Datagram Protocol')) 54 self.add_object(Object(Object.PROTOCOL, 'icmp', '1', 'Internet Control Message Protocol')) 35 55 36 56 def resolve_object(self, type, name): … … 42 62 return self.resolve_object('network', network) 43 63 44 def resolve_ip(self, ip):45 return self.resolve_object('ip', ip)46 47 64 def resolve_port(self, port): 48 65 return self.resolve_object('port', port) 66 67 def resolve_protocol(self, protocol): 68 return self.resolve_object('protocol', protocol) 49 69 50 70 def resolve(self, descriptor): … … 66 86 raise Resolver.InvalidObject("Object '%s:%s' not in ruleset" % (object.type, object.name)) 67 87 68 def populate_defaults(self ):88 def populate_defaults(self, types = get_object_types()): 69 89 """ Populate object database from system. """ 70 objects = 0 71 try: 72 for service in open("/etc/services"): 73 comment = '' 74 comment_start = service.find('#') 75 if comment_start != -1: 76 comment = service[comment_start + 1:].strip() 77 service = service[:comment_start] 78 service = service.strip() 79 if not service: continue 80 tokens = service.split() 81 name = tokens.pop(0) 82 port, proto = tokens.pop(0).split('/') 83 tokens.insert(0, name) 84 for token in tokens: 85 try: 86 self.add_object(Object(Object.PORT, token, port, comment)) 87 objects += 1 88 except Resolver.DuplicateObject: 89 eport = self.resolve_port(tokens[0]) 90 if eport.value != port: 91 warning("port object", tokens[0], "already exists with a different port number") 92 except IOError: 93 pass 94 info("added", objects, "port objects") 95 objects = 0 96 try: 97 for host in open("/etc/hosts"): 98 comment = '' 99 comment_start = host.find('#') 100 if comment_start != -1: 101 comment = host[comment_start + 1:].strip() 102 host = host[:comment_start].strip() 103 host = host.strip() 104 if not host: continue 105 ip, names = host.split(None, 1) 106 names = names.split() 107 for name in names: 108 try: 109 self.add_object(Object(Object.NETWORK, name, ip, comment)) 110 objects += 1 111 except Resolver.DuplicateObject: 112 enet = self.resolve_network(name) 113 if enet.value != ip: 114 warning("network object", tokens[0], "already exists with a different port number") 115 except IOError: 116 pass 117 info("added", objects, "network objects") 90 for type in types: 91 objects = 0 92 if type == Object.PORT: 93 try: 94 for service in open("/etc/services"): 95 comment = '' 96 comment_start = service.find('#') 97 if comment_start != -1: 98 comment = service[comment_start + 1:].strip() 99 service = service[:comment_start] 100 service = service.strip() 101 if not service: continue 102 tokens = service.split() 103 name = tokens.pop(0) 104 port, proto = tokens.pop(0).split('/') 105 tokens.insert(0, name) 106 for token in tokens: 107 try: 108 self.add_object(Object(Object.PORT, token, port, comment)) 109 objects += 1 110 except Resolver.DuplicateObject: 111 eport = self.resolve_port(tokens[0]) 112 if eport.value != port: 113 warning("port object", tokens[0], "already exists with a different port number") 114 except IOError: 115 pass 116 info("added", objects, "port objects") 117 elif type == Object.NETWORK: 118 try: 119 for host in open("/etc/hosts"): 120 comment = '' 121 comment_start = host.find('#') 122 if comment_start != -1: 123 comment = host[comment_start + 1:].strip() 124 host = host[:comment_start].strip() 125 host = host.strip() 126 if not host: continue 127 ip, names = host.split(None, 1) 128 names = names.split() 129 for name in names: 130 try: 131 self.add_object(Object(Object.NETWORK, name, ip, comment)) 132 objects += 1 133 except Resolver.DuplicateObject: 134 enet = self.resolve_network(name) 135 if enet.value != ip: 136 warning("network object", tokens[0], "already exists with a different port number") 137 except IOError: 138 pass 139 info("added", objects, "network objects") 140 elif type == Object.PROTOCOL: 141 try: 142 for protocol in open("/etc/protocols"): 143 comment = '' 144 comment_start = protocol.find('#') 145 if comment_start != -1: 146 comment = protocol[comment_start + 1:].strip() 147 protocol = protocol[:comment_start].strip() 148 protocol = protocol.strip() 149 if not protocol: continue 150 name, number, aliases = protocol.split(None, 2) 151 number = int(number) 152 if number < 0 or number > 255: continue 153 names = map(str.lower, [name] + aliases.split()) 154 for name in names: 155 try: 156 self.add_object(Object(Object.PROTOCOL, name, number, comment)) 157 objects += 1 158 except Resolver.DuplicateObject: 159 eprot = self.resolve_protocol(name) 160 if int(eprot.value) != number: 161 warning("protocol object", name, "already exists with a different protocol number", number) 162 except IOError: 163 pass 164 info("added", objects, "protocol objects") 118 165 119 @classmethod 120 def get_object_types(self): 121 return [ 'network', 'port' ] 166 @staticmethod 167 def get_object_types(): 168 global get_object_types 169 return get_object_types() 122 170 123 171 def get_objects(self, type): fwc/trunk/Rule.py
r112 r117 1 1 class Rule: 2 def __init__(self, action = None, source = [], sport = [], destination = [], dport = [], protocol = [], description = None, state = None, log = None ):2 def __init__(self, action = None, source = [], sport = [], destination = [], dport = [], protocol = [], description = None, state = None, log = None, reject_type = None, reject_subtype = None): 3 3 self.action = action 4 4 self.source = source … … 11 11 self.state = state 12 12 self.log = log 13 13 self.reject_type = reject_type 14 self.reject_subtype = reject_subtype fwc/trunk/util.py
r116 r117 813 813 """ Print a green notice prefixed by INF. """ 814 814 cprint("^2^BINF ^B" + ' '.join(map(str, args)) + '^N') 815 816 def tolist(o): 817 """ Convert a scalar object into a list or simply return the list/tuple. """ 818 if type(o) is list or type(o) is tuple: return list(o) 819 return [o]
