| 16 | | CONF_DIR = "/etc/fwcrc" |
|---|
| 17 | | |
|---|
| 18 | | # Globally useful stuff |
|---|
| 19 | | |
|---|
| 20 | | resolver = Resolver() |
|---|
| 21 | | firewalls = {} |
|---|
| 22 | | firewall = None |
|---|
| 23 | | firewall = Firewall("localhost", resolver) |
|---|
| 24 | | |
|---|
| 25 | | def check_port_protocol(context): |
|---|
| 26 | | return 'protocol' in context and context['protocol'] in [ 'tcp', 'udp' ] |
|---|
| 27 | | |
|---|
| 28 | | def have_firewall(context): |
|---|
| 29 | | """ Return true if the current firewall is have_firewall. """ |
|---|
| 30 | | return firewall |
|---|
| 31 | | |
|---|
| 32 | | def have_modifiable_firewall(context): |
|---|
| 33 | | return firewall |
|---|
| 34 | | |
|---|
| 35 | | # Help extractors |
|---|
| 36 | | |
|---|
| 37 | | def help_object(context, type): |
|---|
| 38 | | help = { '<%s>' % type : '%s object.' % type.title() } |
|---|
| 39 | | for o in resolver.get_objects(type): |
|---|
| 40 | | if o.description: |
|---|
| 41 | | help[o.name] = '%s (%s)' % (o.description, o.value) |
|---|
| 42 | | else: |
|---|
| 43 | | help[o.name] = '%s object (%s)' % (type.title(), o.value) |
|---|
| 44 | | return help |
|---|
| 45 | | |
|---|
| 46 | | def help_network(context): |
|---|
| 47 | | help = { '<network>' : 'Network address.'} |
|---|
| 48 | | for o in resolver.get_objects('network'): |
|---|
| 49 | | if o.description: |
|---|
| 50 | | help[o.name] = "%s (%s)" % (o.description, o.value) |
|---|
| 51 | | else: |
|---|
| 52 | | help[o.name] = o.value |
|---|
| 53 | | return help |
|---|
| 54 | | |
|---|
| 55 | | def help_port(context): |
|---|
| 56 | | help = { '<port>' : 'Port.'} |
|---|
| 57 | | for o in resolver.get_objects('port'): |
|---|
| 58 | | text = o.description or "Port object %s" % o.name |
|---|
| 59 | | help[o.name] = text + " (%s)" % o.value |
|---|
| 60 | | return help |
|---|
| 61 | | |
|---|
| 62 | | def help_firewall(context): |
|---|
| 63 | | help = {} |
|---|
| 64 | | for f in firewalls: |
|---|
| 65 | | help[f] = "Firewall %s" % firewalls[f].name |
|---|
| 66 | | return help |
|---|
| 67 | | |
|---|
| 68 | | # Command validators |
|---|
| 69 | | |
|---|
| 70 | | def NETWORK(context, str): |
|---|
| 71 | | return re.match(Object.NETWORK_PATTERN, str) \ |
|---|
| 72 | | or str in help_object(context, 'network') |
|---|
| 73 | | |
|---|
| 74 | | def PORT(context, str): |
|---|
| 75 | | return re.match(Object.PORT_PATTERN, str) \ |
|---|
| 76 | | or str in help_object(context, 'port') |
|---|
| 77 | | |
|---|
| 78 | | def PORT_RANGE(context, str): |
|---|
| 79 | | range = str.split('-') |
|---|
| 80 | | return PORT(context, range[0]) and (len(range) < 2 or len(range) > 1 and PORT(context, range[1])) |
|---|
| 81 | | |
|---|
| 82 | | def RULE(context, str): |
|---|
| 83 | | try: |
|---|
| 84 | | return int(str) >= 0 and int(str) < len(firewall.get_rules()) |
|---|
| 85 | | except: |
|---|
| 86 | | return False |
|---|
| 87 | | |
|---|
| 88 | | def FIREWALL(context, str): |
|---|
| 89 | | return str in help_firewall(context) |
|---|
| 90 | | |
|---|
| 91 | | # Commands |
|---|
| 92 | | |
|---|
| 93 | | 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): |
|---|
| 94 | | if log: |
|---|
| 95 | | if log == 'log': |
|---|
| 96 | | log = True |
|---|
| 97 | | else: |
|---|
| 98 | | log = log[1] |
|---|
| 99 | | if index != None: index = int(index) |
|---|
| 100 | | firewall.add(Rule(action, tolist(source), tolist(sport), tolist(destination), tolist(dport), protocol, description, state, log, reject_type, reject_subtype), where, index) |
|---|
| 101 | | |
|---|
| 102 | | def remove_rule(context, rules): |
|---|
| 103 | | firewall.remove(map(int, rules)) |
|---|
| 104 | | |
|---|
| 105 | | def move_rule(context, old, where = 'top', index = None): |
|---|
| 106 | | if index and old == index: return |
|---|
| 107 | | try: |
|---|
| 108 | | old = int(old) |
|---|
| 109 | | new = firewall.add(firewall.get_rule(old), where, index and int(index) or None) |
|---|
| 110 | | if new < old: old += 1 |
|---|
| 111 | | firewall.remove(old) |
|---|
| 112 | | except TypeError, IndexError: |
|---|
| 113 | | error("Invalid move") |
|---|
| 114 | | raise |
|---|
| 115 | | |
|---|
| 116 | | def list_ruleset(context): |
|---|
| 117 | | for ruleno, rule in enumerate(firewall.get_rules()): |
|---|
| 118 | | cmd = "^B%s^B" % rule.action |
|---|
| 119 | | if rule.state != 'new': |
|---|
| 120 | | cmd += " state ^B" + rule.state + "^B" |
|---|
| 121 | | if rule.protocol: |
|---|
| 122 | | try: |
|---|
| 123 | | cmd += ' protocol ^B%s^B' % int(rule.protocol) |
|---|
| 124 | | except: |
|---|
| 125 | | cmd += " ^B" + rule.protocol + "^B" |
|---|
| 126 | | if rule.source or rule.sport: |
|---|
| 127 | | cmd += " from" |
|---|
| 128 | | if rule.source: |
|---|
| 129 | | cmd += " ^B" + ' '.join(rule.source) + "^B" |
|---|
| 130 | | if rule.sport: |
|---|
| 131 | | cmd += " port ^B" + ' '.join(rule.sport) + "^B" |
|---|
| 132 | | if rule.destination or rule.dport: |
|---|
| 133 | | cmd += " to" |
|---|
| 134 | | if rule.destination: |
|---|
| 135 | | cmd += " ^B" + ' '.join(rule.destination) + "^B" |
|---|
| 136 | | if rule.dport: |
|---|
| 137 | | cmd += " port ^B" + ' '.join(rule.dport) + "^B" |
|---|
| 138 | | if rule.reject_type: |
|---|
| 139 | | cmd += " with ^B%s^B" % rule.reject_type |
|---|
| 140 | | if rule.reject_subtype: |
|---|
| 141 | | cmd += " ^B%s^B" % rule.reject_subtype |
|---|
| 142 | | if rule.log: |
|---|
| 143 | | cmd += " log" |
|---|
| 144 | | if type(rule.log) is str: |
|---|
| 145 | | cmd += " message ^B'%s'^B" % rule.log |
|---|
| 146 | | if rule.description: |
|---|
| 147 | | cmd += " description ^B'%s'^B" % rule.description |
|---|
| 148 | | cprint("^B%3i:^B %s" % (ruleno, cmd)) |
|---|
| 149 | | |
|---|
| 150 | | def create_object(context, type, name, value, description = None): |
|---|
| 151 | | try: |
|---|
| 152 | | resolver.add_object(Object(type, name, value, description)) |
|---|
| 153 | | except Resolver.Error, e: |
|---|
| 154 | | error(e) |
|---|
| 155 | | |
|---|
| 156 | | |
|---|
| 157 | | def list_objects(context, type = 'all', filter = '*'): |
|---|
| 158 | | if type == 'all': |
|---|
| 159 | | type = Resolver.get_object_types() |
|---|
| 160 | | filter = filter.lower() |
|---|
| 161 | | if '*' not in filter and '?' not in filter: |
|---|
| 162 | | filter = '*%s*' % filter |
|---|
| 163 | | for t in tolist(type): |
|---|
| 164 | | objects = [x for x in resolver.get_objects(t) if fnmatch((x.name + (x.description and x.description or '')).lower(), filter) or x.description and fnmatch(x.description, filter)] |
|---|
| 165 | | for o in sorted(objects, lambda a, b: cmp(a.name, b.name)): |
|---|
| 166 | | text = "%s ^B%s^B ^B^6%s^N" % (o.type, o.name, ' '.join(map(str, tolist(o.value)))) |
|---|
| 167 | | if o.description: text += " description ^2'%s'^N" % o.description |
|---|
| 168 | | cprint(text) |
|---|
| 169 | | |
|---|
| 170 | | def quit(context = None): |
|---|
| 171 | | info("Firewall console exit.") |
|---|
| 172 | | sys.exit(0) |
|---|
| 173 | | |
|---|
| 174 | | # Grammar content |
|---|
| 175 | | |
|---|
| 176 | | placement_rules = { |
|---|
| 177 | | 'replace' : { |
|---|
| 178 | | GROUP : 10, |
|---|
| 179 | | UNLESS_VAR : [ 'old', 'where' ], |
|---|
| 180 | | VAR : 'where', |
|---|
| 181 | | HELP : 'Replace an existing rule.', |
|---|
| 182 | | RULE : { |
|---|
| 183 | | HELP : ('<rule>', 'Rule to replace.'), |
|---|
| 184 | | VAR : 'index', |
|---|
| 185 | | JUMP : RETURN, |
|---|
| 186 | | }, |
|---|
| 187 | | }, |
|---|
| 188 | | 'top|bottom' : { |
|---|
| 189 | | GROUP : 10, |
|---|
| 190 | | UNLESS_VAR : 'where', |
|---|
| 191 | | VAR : 'where', |
|---|
| 192 | | HELP : { |
|---|
| 193 | | 'top' : 'Insert rule at top of ruleset.', |
|---|
| 194 | | 'bottom' : 'Insert rule at bottom of ruleset (default).', |
|---|
| 195 | | }, |
|---|
| 196 | | JUMP : RETURN, |
|---|
| 197 | | }, |
|---|
| 198 | | 'before|after' : { |
|---|
| 199 | | GROUP : 10, |
|---|
| 200 | | UNLESS_VAR : 'where', |
|---|
| 201 | | VAR : 'where', |
|---|
| 202 | | HELP : { |
|---|
| 203 | | 'before' : 'Insert rule before another.', |
|---|
| 204 | | 'after' : 'Insert rule after another.', |
|---|
| 205 | | }, |
|---|
| 206 | | RULE : { |
|---|
| 207 | | HELP : ('<rule>', 'Rule to insert relative to.'), |
|---|
| 208 | | VAR : 'index', |
|---|
| 209 | | JUMP : RETURN, |
|---|
| 210 | | }, |
|---|
| 211 | | }, |
|---|
| 212 | | } |
|---|
| 213 | | |
|---|
| 214 | | protocol_rules = { |
|---|
| 215 | | lambda ctx, token: resolver.have_object(Object.PROTOCOL, token) : { |
|---|
| 216 | | GROUP : 30, |
|---|
| 217 | | UNLESS_VAR : 'protocol', |
|---|
| 218 | | VAR : 'protocol', |
|---|
| 219 | | HELP : lambda x: [(x.name, x.description or 'Protocol %s' % x.value) for x in resolver.get_objects('protocol')], |
|---|
| 220 | | JUMP : RETURN, |
|---|
| 221 | | }, |
|---|
| 222 | | 'protocol' : { |
|---|
| 223 | | GROUP : 30, |
|---|
| 224 | | HELP : 'Arbitrary IP protocol number.', |
|---|
| 225 | | UNLESS_VAR : 'protocol', |
|---|
| 226 | | '\d+' : { |
|---|
| 227 | | HELP : ('<protocol>', 'IP protocol number.'), |
|---|
| 228 | | JUMP : RETURN, |
|---|
| 229 | | VAR : 'protocol', |
|---|
| 230 | | }, |
|---|
| 231 | | }, |
|---|
| 232 | | } |
|---|
| 233 | | |
|---|
| 234 | | # Grammar |
|---|
| 235 | | cli = CLI({ |
|---|
| 236 | | 'nat' : { |
|---|
| 237 | | MERGE : [ placement_rules, protocol_rules ], |
|---|
| 238 | | GROUP : 10, |
|---|
| 239 | | HELP : 'NAT matching packets.', |
|---|
| 240 | | VAR : 'action', |
|---|
| 241 | | 'to' : { |
|---|
| 242 | | HELP : 'Specify destination to NAT to.', |
|---|
| 243 | | Object.NETWORK_PATTERN : { |
|---|
| 244 | | VAR : 'nat_ip', |
|---|
| 245 | | HELP : ('<ip>', 'NAT to IP address.'), |
|---|
| 246 | | 'if' : { |
|---|
| 247 | | HELP : 'NAT on the following conditions.', |
|---|
| 248 | | JUMP : 'commands', |
|---|
| 249 | | }, |
|---|
| 250 | | }, |
|---|
| 251 | | }, |
|---|
| 252 | | }, |
|---|
| 253 | | 'accept|drop|reject' : { |
|---|
| 254 | | HOOK : 'match', |
|---|
| 255 | | GROUP : 10, |
|---|
| 256 | | IF : have_modifiable_firewall, |
|---|
| 257 | | GLOBAL_LABEL : 'commands', |
|---|
| 258 | | MERGE : [ placement_rules, protocol_rules ], |
|---|
| 259 | | VAR : 'action', |
|---|
| 260 | | ACTION : { |
|---|
| 261 | | HELP : 'Add rule to firewall.', |
|---|
| 262 | | # Only if a source or destination have actually been specified |
|---|
| 263 | | #IF : lambda ctx: 'source' in ctx or 'destination' in ctx, |
|---|
| 264 | | ACTION : insert_rule, |
|---|
| 265 | | }, |
|---|
| 266 | | HELP : { |
|---|
| 267 | | 'accept' : 'Add rule accepting matching packets.', |
|---|
| 268 | | 'drop' : 'Add rule dropping matching packets.', |
|---|
| 269 | | 'reject' : 'Add rule rejecting matching packets.', |
|---|
| 270 | | }, |
|---|
| 271 | | 'with' : { |
|---|
| 272 | | IF : lambda ctx: 'reject_type' not in ctx and ctx['action'] == 'reject', |
|---|
| 273 | | HELP : 'Reject with the specified packet type.', |
|---|
| 274 | | 'tcp' : { |
|---|
| 275 | | VAR : 'reject_type', |
|---|
| 276 | | HELP : 'Reject with tcp... packet.', |
|---|
| 277 | | 'reset' : { |
|---|
| 278 | | VAR : 'reject_subtype', |
|---|
| 279 | | IF : lambda ctx: 'protocol' in ctx and ctx['protocol'] == 'tcp', |
|---|
| 280 | | HELP : 'Reject with TCP reset.', |
|---|
| 281 | | JUMP : 'commands', |
|---|
| 282 | | }, |
|---|
| 283 | | }, |
|---|
| 284 | | 'network|host|protocol|admin' : { |
|---|
| 285 | | VAR : 'reject_type', |
|---|
| 286 | | HELP : { |
|---|
| 287 | | 'network' : 'Reject with icmp-network... packet.', |
|---|
| 288 | | 'host' : 'Reject with icmp-host... packet.', |
|---|
| 289 | | 'protocol' : 'Reject with icmp-protocol... packet.', |
|---|
| 290 | | 'admin' : 'Reject with icmp-admin... packet.', |
|---|
| 291 | | }, |
|---|
| 292 | | 'unreachable' : { |
|---|
| 293 | | HELP : lambda ctx: { 'unreachable' : 'Reject with icmp-%s-unreachable.' % ctx['reject_type'] }, |
|---|
| 294 | | IF : lambda ctx: ctx['reject_type'] != 'admin', |
|---|
| 295 | | VAR : 'reject_subtype', |
|---|
| 296 | | JUMP : 'commands', |
|---|
| 297 | | }, |
|---|
| 298 | | 'prohibited' : { |
|---|
| 299 | | HELP : lambda ctx: { 'prohibited' : 'Reject with icmp-%s-prohibited.' % ctx['reject_type'] }, |
|---|
| 300 | | IF : lambda ctx: ctx['reject_type'] in ('network', 'host', 'admin'), |
|---|
| 301 | | VAR : 'reject_subtype', |
|---|
| 302 | | JUMP : 'commands', |
|---|
| 303 | | }, |
|---|
| 304 | | }, |
|---|
| 305 | | |
|---|
| 306 | | }, |
|---|
| 307 | | 'from' : { |
|---|
| 308 | | GROUP : 20, |
|---|
| 309 | | HELP : 'Match source network or port.', |
|---|
| 310 | | IF : lambda ctx: ('protocol' in ctx and ctx['protocol'] in ['tcp', 'udp'] and not 'sport' in ctx) or not 'source' in ctx, |
|---|
| 311 | | NETWORK : { |
|---|
| 312 | | VAR : 'source', |
|---|
| 313 | | LABEL : 'source', |
|---|
| 314 | | JUMP : 'commands', |
|---|
| 315 | | JUMP_TO : 'source', |
|---|
| 316 | | GROUP : 20, |
|---|
| 317 | | 'port' : { |
|---|
| 318 | | GROUP : 20, |
|---|
| 319 | | RANGE : 1, |
|---|
| 320 | | HELP : 'Match source port.', |
|---|
| 321 | | PORT : { |
|---|
| 322 | | LABEL : 'sport', |
|---|
| 323 | | VAR : 'sport', |
|---|
| 324 | | JUMP : 'commands', |
|---|
| 325 | | JUMP_TO : 'sport', |
|---|
| 326 | | HELP : lambda ctx: help_object(ctx, 'port'), |
|---|
| 327 | | }, |
|---|
| 328 | | IF : check_port_protocol, |
|---|
| 329 | | }, |
|---|
| 330 | | HELP : help_network, |
|---|
| 331 | | }, |
|---|
| 332 | | 'port' : { |
|---|
| 333 | | HELP : 'Match source port.', |
|---|
| 334 | | GROUP : 20, |
|---|
| 335 | | PORT : { |
|---|
| 336 | | HELP : help_port, |
|---|
| 337 | | LABEL : 'sports', |
|---|
| 338 | | VAR : 'sport', |
|---|
| 339 | | JUMP : 'commands', |
|---|
| 340 | | # This allows syntax like: accept from port 22 23 25 to anywhere |
|---|
| 341 | | JUMP_TO : 'sports', |
|---|
| 342 | | }, |
|---|
| 343 | | IF : check_port_protocol, |
|---|
| 344 | | }, |
|---|
| 345 | | }, |
|---|
| 346 | | 'description' : { |
|---|
| 347 | | '.+' : { |
|---|
| 348 | | VAR : 'description', |
|---|
| 349 | | JUMP : 'commands', |
|---|
| 350 | | HELP : ('<description>', 'Rule description.'), |
|---|
| 351 | | }, |
|---|
| 352 | | RANGE : 1, |
|---|
| 353 | | HELP : 'Set rule description.' |
|---|
| 354 | | }, |
|---|
| 355 | | 'to' : { |
|---|
| 356 | | GROUP : 20, |
|---|
| 357 | | IF : lambda ctx: ('protocol' in ctx and ctx['protocol'] in ['tcp', 'udp'] and not 'dport' in ctx) or not 'destination' in ctx, |
|---|
| 358 | | UNLESS_VAR : [ 'dport', 'destination' ], |
|---|
| 359 | | NETWORK : { |
|---|
| 360 | | GROUP : 20, |
|---|
| 361 | | LABEL : 'destination', |
|---|
| 362 | | VAR : 'destination', |
|---|
| 363 | | JUMP : 'commands', |
|---|
| 364 | | JUMP_TO : 'destination', |
|---|
| 365 | | 'port' : { |
|---|
| 366 | | GROUP : 20, |
|---|
| 367 | | PORT : { |
|---|
| 368 | | LABEL : 'dport', |
|---|
| 369 | | VAR : 'dport', |
|---|
| 370 | | JUMP : 'commands', |
|---|
| 371 | | JUMP_TO : 'dport', |
|---|
| 372 | | HELP : help_port, |
|---|
| 373 | | }, |
|---|
| 374 | | IF : check_port_protocol, |
|---|
| 375 | | HELP : 'Match destination port.', |
|---|
| 376 | | }, |
|---|
| 377 | | HELP : help_network, |
|---|
| 378 | | }, |
|---|
| 379 | | 'port' : { |
|---|
| 380 | | GROUP : 20, |
|---|
| 381 | | PORT : { |
|---|
| 382 | | LABEL : 'dports', |
|---|
| 383 | | VAR : 'dport', |
|---|
| 384 | | JUMP : 'commands', |
|---|
| 385 | | JUMP_TO : 'dports', |
|---|
| 386 | | HELP : help_port, |
|---|
| 387 | | }, |
|---|
| 388 | | IF : check_port_protocol, |
|---|
| 389 | | HELP : 'Match destination port.', |
|---|
| 390 | | }, |
|---|
| 391 | | HELP : 'Match destination network or port.', |
|---|
| 392 | | }, |
|---|
| 393 | | 'log' : { |
|---|
| 394 | | VAR : 'log', |
|---|
| 395 | | UNLESS_VAR : 'log', |
|---|
| 396 | | HELP : "Log a message when this rule matches.", |
|---|
| 397 | | 'message' : { |
|---|
| 398 | | HELP : 'Log with custom message.', |
|---|
| 399 | | '.+' : { |
|---|
| 400 | | HELP : ('<message>', 'Custom log message.'), |
|---|
| 401 | | JUMP : 'commands', |
|---|
| 402 | | VAR : 'log', |
|---|
| 403 | | }, |
|---|
| 404 | | }, |
|---|
| 405 | | JUMP : 'commands', |
|---|
| 406 | | }, |
|---|
| 407 | | }, |
|---|
| 408 | | 'quit|exit' : { |
|---|
| 409 | | HELP : [ 'quit', 'Exit.' ], |
|---|
| 410 | | ACTION : { |
|---|
| 411 | | HELP : 'Exit.', |
|---|
| 412 | | ACTION : quit, |
|---|
| 413 | | }, |
|---|
| 414 | | }, |
|---|
| 415 | | 'object' : { |
|---|
| 416 | | HELP : 'Ruleset object manipulation.', |
|---|
| 417 | | 'import' : { |
|---|
| 418 | | LABEL : 'import', |
|---|
| 419 | | 'all' : { |
|---|
| 420 | | RANGE : 1, |
|---|
| 421 | | GROUP : 10, |
|---|
| 422 | | HELP : 'Also import aliases.', |
|---|
| 423 | | VAR : 'aliases', |
|---|
| 424 | | JUMP : 'import', |
|---|
| 425 | | }, |
|---|
| 426 | | 's|'.join(Resolver.get_object_types()) + 's' : { |
|---|
| 427 | | GROUP : 20, |
|---|
| 428 | | VAR : 'import_types', |
|---|
| 429 | | HELP : lambda ctx: [(x + 's', 'Import system %s objects' % x) for x in Resolver.get_object_types()], |
|---|
| 430 | | ACTION : lambda ctx, import_types, **args: resolver.populate_defaults(tolist(import_types), with_aliases = 'aliases' in ctx), |
|---|
| 431 | | }, |
|---|
| 432 | | HELP : 'Import system objects.', |
|---|
| 433 | | ACTION : lambda ctx, **args: resolver.populate_defaults(with_aliases = 'aliases' in ctx), |
|---|
| 434 | | }, |
|---|
| 435 | | 'create' : { |
|---|
| 436 | | HELP : 'Create a ruleset object.', |
|---|
| 437 | | '|'.join(Resolver.get_object_types()) : { |
|---|
| 438 | | VAR : 'type', |
|---|
| 439 | | HELP : lambda ctx: [(x, 'Create %s object' % x) for x in Resolver.get_object_types()], |
|---|
| 440 | | Object.NAME_PATTERN : { |
|---|
| 441 | | VAR : 'name', |
|---|
| 442 | | HELP : { '<name>' : 'Name of object to create.' }, |
|---|
| 443 | | LABEL : 'create', |
|---|
| 444 | | 'description' : { |
|---|
| 445 | | UNLESS_VAR : 'description', |
|---|
| 446 | | ORDER : 10, |
|---|
| 447 | | GROUP : 20, |
|---|
| 448 | | '.+' : { |
|---|
| 449 | | VAR : 'description', |
|---|
| 450 | | HELP : { '<description>' : 'Description of object.' }, |
|---|
| 451 | | JUMP : 'create', |
|---|
| 452 | | ACTION : { |
|---|
| 453 | | IF_VAR : 'value', |
|---|
| 454 | | ACTION : create_object, |
|---|
| 455 | | }, |
|---|
| 456 | | }, |
|---|
| 457 | | HELP : 'Optional description of object.' |
|---|
| 458 | | }, |
|---|
| 459 | | lambda ctx, token: re.match(Object.TYPE_PATTERNS[ctx['type']], token) or token in help_object(ctx, ctx['type']) : { |
|---|
| 460 | | ORDER : 20, |
|---|
| 461 | | GROUP : (10, 'Add ...'), |
|---|
| 462 | | VAR : 'value', |
|---|
| 463 | | HELP : lambda ctx: [(k, v) for k, v in help_network(ctx).iteritems() if 'value' in ctx and k not in tolist(ctx['value']) or 'value' not in ctx], |
|---|
| 464 | | JUMP : 'create', |
|---|
| 465 | | ACTION : create_object, |
|---|
| 466 | | }, |
|---|
| 467 | | }, |
|---|
| 468 | | }, |
|---|
| 469 | | }, |
|---|
| 470 | | 'delete' : { |
|---|
| 471 | | HELP : 'Delete a ruleset object.', |
|---|
| 472 | | '|'.join(Resolver.get_object_types()) : { |
|---|
| 473 | | VAR : 'type', |
|---|
| 474 | | HELP : lambda ctx: [(x + 's', 'Remove %s objects' % x) for x in Resolver.get_object_types()], |
|---|
| 475 | | }, |
|---|
| 476 | | }, |
|---|
| 477 | | 'list' : { |
|---|
| 478 | | HELP : 'List objects.', |
|---|
| 479 | | ACTION : { |
|---|
| 480 | | HELP : 'List objects.', |
|---|
| 481 | | ACTION : list_objects, |
|---|
| 482 | | }, |
|---|
| 483 | | 's|'.join(Resolver.get_object_types()) + 's|all' : { |
|---|
| 484 | | ORDER : 10, |
|---|
| 485 | | VAR : 'type', |
|---|
| 486 | | HELP : lambda ctx: [(x + 's', 'List %s objects' % x) for x in Resolver.get_object_types()] + [ ('all', 'All object types.') ], |
|---|
| 487 | | ACTION : list_objects, |
|---|
| 488 | | '.+' : { |
|---|
| 489 | | VAR : 'filter', |
|---|
| 490 | | HELP : ('<glob>', 'Filter results using the given glob.'), |
|---|
| 491 | | ACTION : list_objects, |
|---|
| 492 | | }, |
|---|
| 493 | | }, |
|---|
| 494 | | '.+' : { |
|---|
| 495 | | ORDER : 20, |
|---|
| 496 | | VAR : 'filter', |
|---|
| 497 | | HELP : ('<glob>', 'Filter results using the given glob.'), |
|---|
| 498 | | ACTION : list_objects, |
|---|
| 499 | | }, |
|---|
| 500 | | } |
|---|
| 501 | | }, |
|---|
| 502 | | 'delete' : { |
|---|
| 503 | | GROUP : 20, |
|---|
| 504 | | IF : have_modifiable_firewall, |
|---|
| 505 | | HELP : 'Remove rule(s) from the ruleset.', |
|---|
| 506 | | LABEL : 'rule', |
|---|
| 507 | | RULE : { |
|---|
| 508 | | VAR : 'rules', |
|---|
| 509 | | HELP : ('<rule>', 'Rule to remove.'), |
|---|
| 510 | | JUMP : 'rule', |
|---|
| 511 | | ACTION : remove_rule, |
|---|
| 512 | | }, |
|---|
| 513 | | }, |
|---|
| 514 | | 'list' : { |
|---|
| 515 | | GROUP : 20, |
|---|
| 516 | | IF : have_firewall, |
|---|
| 517 | | HELP : 'List ruleset.', |
|---|
| 518 | | ACTION : list_ruleset, |
|---|
| 519 | | }, |
|---|
| 520 | | 'move' : { |
|---|
| 521 | | GROUP : 20, |
|---|
| 522 | | IF : have_modifiable_firewall, |
|---|
| 523 | | HELP : 'Move rule.', |
|---|
| 524 | | RULE : { |
|---|
| 525 | | LABEL : 'move', |
|---|
| 526 | | VAR : 'old', |
|---|
| 527 | | HELP : ('<rule>', 'Rule to move.'), |
|---|
| 528 | | MERGE : placement_rules, |
|---|
| 529 | | ACTION : { |
|---|
| 530 | | HELP : 'Move rule.', |
|---|
| 531 | | ACTION : move_rule |
|---|
| 532 | | }, |
|---|
| 533 | | }, |
|---|
| 534 | | }, |
|---|
| 535 | | 'firewall' : { |
|---|
| 536 | | HELP : "Commands for firewall management.", |
|---|
| 537 | | 'acquire' : { |
|---|
| 538 | | HELP : 'Acquire a firewall for management.', |
|---|
| 539 | | '\w+' : { |
|---|
| 540 | | }, |
|---|
| 541 | | }, |
|---|
| 542 | | 'list' : { |
|---|
| 543 | | HELP : "List available firewalls.", |
|---|
| 544 | | }, |
|---|
| 545 | | FIREWALL : { |
|---|
| 546 | | HELP : help_firewall, |
|---|
| 547 | | }, |
|---|
| 548 | | }, |
|---|
| 549 | | } |
|---|
| 550 | | ) |
|---|
| 551 | | |
|---|
| 552 | | #from IPTables import IPTables |
|---|
| 553 | | #engine = IPTables(cli) |
|---|
| 554 | | |
|---|
| 555 | | # Command line interaction |
|---|
| 556 | | |
|---|
| 557 | | |
|---|
| 558 | | cli_inject_text = '' |
|---|
| 559 | | bind_key = hasattr(readline, 'bind_key') and readline.bind_key or None |
|---|
| 560 | | force_redisplay = hasattr(readline, 'force_redisplay') and readline.force_redisplay or None |
|---|
| 561 | | set_input_hook = hasattr(readline, 'set_input_hook') and readline.set_input_hook or None |
|---|
| 562 | | cursor = hasattr(readline, 'cursor') and readline.cursor or None |
|---|
| 563 | | |
|---|
| 564 | | def cli_completion(text, state): |
|---|
| 565 | | line = readline.get_line_buffer()[0:readline.get_begidx()] |
|---|
| 566 | | ctx = None |
|---|
| 567 | | try: |
|---|
| 568 | | result = cli.parse(line) |
|---|
| 569 | | except CLI.Error: |
|---|
| 570 | | pass |
|---|
| 571 | | try: |
|---|
| 572 | | help = result.candidates() |
|---|
| 573 | | out = [] |
|---|
| 574 | | for h in help: |
|---|
| 575 | | if h[0] != '<' and h.startswith(text): |
|---|
| 576 | | out.append(h + ' ') |
|---|
| 577 | | except e: |
|---|
| 578 | | print e |
|---|
| 579 | | return out[state] |
|---|
| 580 | | |
|---|
| 581 | | def cli_injector(): |
|---|
| 582 | | try: |
|---|
| 583 | | global cli_inject_text |
|---|
| 584 | | readline.insert_text(cli_inject_text) |
|---|
| 585 | | cli_inject_text = '' |
|---|
| 586 | | except Exception, e: |
|---|
| 587 | | pass |
|---|
| 588 | | |
|---|
| 589 | | def generate_help(line, need_linefeed = False): |
|---|
| 590 | | result = cli.parse(line) |
|---|
| 591 | | # We are not at the end of the line |
|---|
| 592 | | if result.token: |
|---|
| 593 | | readline.insert_text("?") |
|---|
| 594 | | return |
|---|
| 595 | | if cursor: |
|---|
| 596 | | if result.context.parsed_tokens and result.context.parsed_tokens[-1].start() >= cursor(): |
|---|
| 597 | | return |
|---|
| 598 | | if need_linefeed: |
|---|
| 599 | | print |
|---|
| 600 | | format_help(result.help()) |
|---|
| 601 | | if force_redisplay: |
|---|
| 602 | | force_redisplay() |
|---|
| 603 | | |
|---|
| 604 | | def cli_help(key, count): |
|---|
| 605 | | generate_help(readline.get_line_buffer(), True) |
|---|
| 606 | | |
|---|
| 607 | | def format_help(help): |
|---|
| 608 | | length = 0 |
|---|
| 609 | | for group in help: |
|---|
| 610 | | for h in help[group]: |
|---|
| 611 | | if len(h[0]) > length: |
|---|
| 612 | | length = len(h[0]) |
|---|
| 613 | | header, footer = None, None |
|---|
| 614 | | if HELP_HEADER in help: |
|---|
| 615 | | header = help[HELP_HEADER] |
|---|
| 616 | | del(help[HELP_HEADER]) |
|---|
| 617 | | if HELP_FOOTER in help: |
|---|
| 618 | | footer = help[HELP_FOOTER] |
|---|
| 619 | | del(help[HELP_FOOTER]) |
|---|
| 620 | | if header: |
|---|
| 621 | | cprint(header) |
|---|
| 622 | | print |
|---|
| 623 | | count = 0 |
|---|
| 624 | | last_order = None |
|---|
| 625 | | for order, group in sorted(help, lambda a, b: cmp(a[0], b[0])): |
|---|
| 626 | | if last_order != None and order != last_order: |
|---|
| 627 | | print |
|---|
| 628 | | last_order = order |
|---|
| 629 | | if group: |
|---|
| 630 | | cprint(" ^6%s^N" % group) |
|---|
| 631 | | elif count: |
|---|
| 632 | | print_next = 1 |
|---|
| 633 | | for h in help[(order, group)]: |
|---|
| 634 | | cprint(" ^B%s^B %s" % (h[0] + (length - len(h[0])) * ' ', h[1])) |
|---|
| 635 | | count += 1 |
|---|
| 636 | | if footer: |
|---|
| 637 | | print |
|---|
| 638 | | cprint(footer) |
|---|
| 639 | | |
|---|
| 640 | | readline.set_completer(cli_completion) |
|---|
| 641 | | readline.set_startup_hook(cli_injector) |
|---|
| 642 | | # Use custom readline extensions not in 2.4 |
|---|
| 643 | | if bind_key and set_input_hook: |
|---|
| 644 | | bind_key(ord('?'), cli_help) |
|---|
| 645 | | #set_input_hook(timeout) |
|---|
| 646 | | |
|---|
| | 18 | engine = Engine() |
|---|