1""" 2Management of iptables 3====================== 4 5This is an iptables-specific module designed to manage Linux firewalls. It is 6expected that this state module, and other system-specific firewall states, may 7at some point be deprecated in favor of a more generic ``firewall`` state. 8 9.. code-block:: yaml 10 11 httpd: 12 iptables.append: 13 - table: filter 14 - chain: INPUT 15 - jump: ACCEPT 16 - match: state 17 - connstate: NEW 18 - dport: 80 19 - protocol: tcp 20 - sport: 1025:65535 21 - save: True 22 23 httpd: 24 iptables.append: 25 - table: filter 26 - chain: INPUT 27 - jump: ACCEPT 28 - match: 29 - state 30 - comment 31 - comment: "Allow HTTP" 32 - connstate: NEW 33 - dport: 80 34 - protocol: tcp 35 - sport: 1025:65535 36 - save: True 37 38 httpd: 39 iptables.append: 40 - table: filter 41 - chain: INPUT 42 - jump: ACCEPT 43 - match: 44 - state 45 - comment 46 - comment: "Allow HTTP" 47 - connstate: NEW 48 - source: '127.0.0.1' 49 - dport: 80 50 - protocol: tcp 51 - sport: 1025:65535 52 - save: True 53 54 .. Invert Rule 55 httpd: 56 iptables.append: 57 - table: filter 58 - chain: INPUT 59 - jump: ACCEPT 60 - match: 61 - state 62 - comment 63 - comment: "Allow HTTP" 64 - connstate: NEW 65 - source: '! 127.0.0.1' 66 - dport: 80 67 - protocol: tcp 68 - sport: 1025:65535 69 - save: True 70 71 httpd: 72 iptables.append: 73 - table: filter 74 - chain: INPUT 75 - jump: ACCEPT 76 - match: 77 - state 78 - comment 79 - comment: "Allow HTTP" 80 - connstate: NEW 81 - source: 'not 127.0.0.1' 82 - dport: 80 83 - protocol: tcp 84 - sport: 1025:65535 85 - save: True 86 87 httpd: 88 iptables.append: 89 - table: filter 90 - family: ipv6 91 - chain: INPUT 92 - jump: ACCEPT 93 - match: state 94 - connstate: NEW 95 - dport: 80 96 - protocol: tcp 97 - sport: 1025:65535 98 - save: True 99 100 httpd: 101 iptables.append: 102 - table: filter 103 - family: ipv4 104 - chain: INPUT 105 - jump: ACCEPT 106 - match: state 107 - connstate: NEW 108 - dports: 109 - 80 110 - 443 111 - protocol: tcp 112 - sport: 1025:65535 113 - save: True 114 115 httpd: 116 iptables.insert: 117 - position: 1 118 - table: filter 119 - chain: INPUT 120 - jump: ACCEPT 121 - match: state 122 - connstate: NEW 123 - dport: 80 124 - protocol: tcp 125 - sport: 1025:65535 126 - save: True 127 128 httpd: 129 iptables.insert: 130 - position: 1 131 - table: filter 132 - family: ipv6 133 - chain: INPUT 134 - jump: ACCEPT 135 - match: state 136 - connstate: NEW 137 - dport: 80 138 - protocol: tcp 139 - sport: 1025:65535 140 - save: True 141 142 httpd: 143 iptables.delete: 144 - table: filter 145 - chain: INPUT 146 - jump: ACCEPT 147 - match: state 148 - connstate: NEW 149 - dport: 80 150 - protocol: tcp 151 - sport: 1025:65535 152 - save: True 153 154 httpd: 155 iptables.delete: 156 - position: 1 157 - table: filter 158 - chain: INPUT 159 - jump: ACCEPT 160 - match: state 161 - connstate: NEW 162 - dport: 80 163 - protocol: tcp 164 - sport: 1025:65535 165 - save: True 166 167 httpd: 168 iptables.delete: 169 - table: filter 170 - family: ipv6 171 - chain: INPUT 172 - jump: ACCEPT 173 - match: state 174 - connstate: NEW 175 - dport: 80 176 - protocol: tcp 177 - sport: 1025:65535 178 - save: True 179 180 default to accept: 181 iptables.set_policy: 182 - chain: INPUT 183 - policy: ACCEPT 184 185.. note:: 186 187 Whereas iptables will accept ``-p``, ``--proto[c[o[l]]]`` as synonyms of 188 ``--protocol``, if ``--proto`` appears in an iptables command after the 189 appearance of ``-m policy``, it is interpreted as the ``--proto`` option of 190 the policy extension (see the iptables-extensions(8) man page). 191 192Example rules for IPSec policy: 193 194.. code-block:: yaml 195 196 accept_esp_in: 197 iptables.append: 198 - table: filter 199 - chain: INPUT 200 - jump: ACCEPT 201 - source: 10.20.0.0/24 202 - destination: 10.10.0.0/24 203 - in-interface: eth0 204 - match: policy 205 - dir: in 206 - pol: ipsec 207 - reqid: 1 208 - proto: esp 209 accept_esp_forward_in: 210 iptables.append: 211 - use: 212 - iptables: accept_esp_in 213 - chain: FORWARD 214 215 accept_esp_out: 216 iptables.append: 217 - table: filter 218 - chain: OUTPUT 219 - jump: ACCEPT 220 - source: 10.10.0.0/24 221 - destination: 10.20.0.0/24 222 - out-interface: eth0 223 - match: policy 224 - dir: out 225 - pol: ipsec 226 - reqid: 1 227 - proto: esp 228 accept_esp_forward_out: 229 iptables.append: 230 - use: 231 - iptables: accept_esp_out 232 - chain: FORWARD 233 234.. note:: 235 236 Various functions of the ``iptables`` module use the ``--check`` option. If 237 the version of ``iptables`` on the target system does not include this 238 option, an alternate version of this check will be performed using the 239 output of iptables-save. This may have unintended consequences on legacy 240 releases of ``iptables``. 241""" 242from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS 243 244 245def __virtual__(): 246 """ 247 Only load if the locale module is available in __salt__ 248 """ 249 if "iptables.version" in __salt__: 250 return True 251 return (False, "iptables module could not be loaded") 252 253 254def chain_present(name, table="filter", family="ipv4"): 255 """ 256 .. versionadded:: 2014.1.0 257 258 Verify the chain is exist. 259 260 name 261 A user-defined chain name. 262 263 table 264 The table to own the chain. 265 266 family 267 Networking family, either ipv4 or ipv6 268 """ 269 270 ret = {"name": name, "changes": {}, "result": None, "comment": ""} 271 272 chain_check = __salt__["iptables.check_chain"](table, name, family) 273 if chain_check is True: 274 ret["result"] = True 275 ret["comment"] = "iptables {} chain is already exist in {} table for {}".format( 276 name, table, family 277 ) 278 return ret 279 280 if __opts__["test"]: 281 ret["comment"] = "iptables {} chain in {} table needs to be set for {}".format( 282 name, table, family 283 ) 284 return ret 285 command = __salt__["iptables.new_chain"](table, name, family) 286 if command is True: 287 ret["changes"] = {"locale": name} 288 ret["result"] = True 289 ret["comment"] = "iptables {} chain in {} table create success for {}".format( 290 name, table, family 291 ) 292 return ret 293 else: 294 ret["result"] = False 295 ret["comment"] = "Failed to create {} chain in {} table: {} for {}".format( 296 name, table, command.strip(), family 297 ) 298 return ret 299 300 301def chain_absent(name, table="filter", family="ipv4"): 302 """ 303 .. versionadded:: 2014.1.0 304 305 Verify the chain is absent. 306 307 table 308 The table to remove the chain from 309 310 family 311 Networking family, either ipv4 or ipv6 312 """ 313 314 ret = {"name": name, "changes": {}, "result": None, "comment": ""} 315 316 chain_check = __salt__["iptables.check_chain"](table, name, family) 317 if not chain_check: 318 ret["result"] = True 319 ret[ 320 "comment" 321 ] = "iptables {} chain is already absent in {} table for {}".format( 322 name, table, family 323 ) 324 return ret 325 if __opts__["test"]: 326 ret["comment"] = "iptables {} chain in {} table needs to be removed {}".format( 327 name, table, family 328 ) 329 return ret 330 flush_chain = __salt__["iptables.flush"](table, name, family) 331 if not flush_chain: 332 command = __salt__["iptables.delete_chain"](table, name, family) 333 if command is True: 334 ret["changes"] = {"locale": name} 335 ret["result"] = True 336 ret[ 337 "comment" 338 ] = "iptables {} chain in {} table delete success for {}".format( 339 name, table, family 340 ) 341 else: 342 ret["result"] = False 343 ret["comment"] = "Failed to delete {} chain in {} table: {} for {}".format( 344 name, table, command.strip(), family 345 ) 346 else: 347 ret["result"] = False 348 ret["comment"] = "Failed to flush {} chain in {} table: {} for {}".format( 349 name, table, flush_chain.strip(), family 350 ) 351 return ret 352 353 354def append(name, table="filter", family="ipv4", **kwargs): 355 """ 356 .. versionadded:: 0.17.0 357 358 Add a rule to the end of the specified chain. 359 360 name 361 A user-defined name to call this rule by in another part of a state or 362 formula. This should not be an actual rule. 363 364 table 365 The table that owns the chain which should be modified 366 367 family 368 Network family, ipv4 or ipv6. 369 370 All other arguments are passed in with the same name as the long option 371 that would normally be used for iptables, with one exception: ``--state`` is 372 specified as `connstate` instead of `state` (not to be confused with 373 `ctstate`). 374 375 Jump options that doesn't take arguments should be passed in with an empty 376 string. 377 """ 378 ret = {"name": name, "changes": {}, "result": None, "comment": ""} 379 380 if "rules" in kwargs: 381 ret["changes"]["locale"] = [] 382 comments = [] 383 save = False 384 for rule in kwargs["rules"]: 385 if "rules" in rule: 386 del rule["rules"] 387 if "__agg__" in rule: 388 del rule["__agg__"] 389 if "save" in rule and rule["save"]: 390 save = True 391 if rule["save"] is not True: 392 save_file = rule["save"] 393 else: 394 save_file = True 395 rule["save"] = False 396 _ret = append(**rule) 397 if "locale" in _ret["changes"]: 398 ret["changes"]["locale"].append(_ret["changes"]["locale"]) 399 comments.append(_ret["comment"]) 400 ret["result"] = _ret["result"] 401 if save: 402 if save_file is True: 403 save_file = None 404 __salt__["iptables.save"](filename=save_file, family=family) 405 if not ret["changes"]["locale"]: 406 del ret["changes"]["locale"] 407 ret["comment"] = "\n".join(comments) 408 return ret 409 410 for ignore in _STATE_INTERNAL_KEYWORDS: 411 if ignore in kwargs: 412 del kwargs[ignore] 413 kwargs["name"] = name 414 kwargs["table"] = table 415 rule = __salt__["iptables.build_rule"](family=family, **kwargs) 416 command = __salt__["iptables.build_rule"]( 417 full="True", family=family, command="A", **kwargs 418 ) 419 if __salt__["iptables.check"](table, kwargs["chain"], rule, family) is True: 420 ret["result"] = True 421 ret["comment"] = "iptables rule for {} already set ({}) for {}".format( 422 name, command.strip(), family 423 ) 424 if "save" in kwargs and kwargs["save"]: 425 if kwargs["save"] is not True: 426 filename = kwargs["save"] 427 else: 428 filename = None 429 saved_rules = __salt__["iptables.get_saved_rules"]( 430 conf_file=filename, family=family 431 ) 432 _rules = __salt__["iptables.get_rules"](family=family) 433 __rules = [] 434 for table in _rules: 435 for chain in _rules[table]: 436 __rules.append(_rules[table][chain].get("rules")) 437 __saved_rules = [] 438 for table in saved_rules: 439 for chain in saved_rules[table]: 440 __saved_rules.append(saved_rules[table][chain].get("rules")) 441 # Only save if rules in memory are different than saved rules 442 if __rules != __saved_rules: 443 out = __salt__["iptables.save"](filename=filename, family=family) 444 ret["comment"] += "\nSaved iptables rule {} for {}\n{}\n{}".format( 445 name, family, command.strip(), out 446 ) 447 return ret 448 if __opts__["test"]: 449 ret["comment"] = "iptables rule for {} needs to be set ({}) for {}".format( 450 name, command.strip(), family 451 ) 452 return ret 453 if __salt__["iptables.append"](table, kwargs["chain"], rule, family): 454 ret["changes"] = {"locale": name} 455 ret["result"] = True 456 ret["comment"] = "Set iptables rule for {} to: {} for {}".format( 457 name, command.strip(), family 458 ) 459 if "save" in kwargs and kwargs["save"]: 460 if kwargs["save"] is not True: 461 filename = kwargs["save"] 462 else: 463 filename = None 464 out = __salt__["iptables.save"](filename=filename, family=family) 465 ret["comment"] = "Set and saved iptables rule {} for {}\n{}\n{}".format( 466 name, family, command.strip(), out 467 ) 468 return ret 469 else: 470 ret["result"] = False 471 ret[ 472 "comment" 473 ] = "Failed to set iptables rule for {}.\nAttempted rule was {} for {}".format( 474 name, command.strip(), family 475 ) 476 return ret 477 478 479def insert(name, table="filter", family="ipv4", **kwargs): 480 """ 481 .. versionadded:: 2014.1.0 482 483 Insert a rule into a chain 484 485 name 486 A user-defined name to call this rule by in another part of a state or 487 formula. This should not be an actual rule. 488 489 table 490 The table that owns the chain that should be modified 491 492 family 493 Networking family, either ipv4 or ipv6 494 495 position 496 The numerical representation of where the rule should be inserted into 497 the chain. Note that ``-1`` is not a supported position value. 498 499 All other arguments are passed in with the same name as the long option 500 that would normally be used for iptables, with one exception: ``--state`` is 501 specified as `connstate` instead of `state` (not to be confused with 502 `ctstate`). 503 504 Jump options that doesn't take arguments should be passed in with an empty 505 string. 506 """ 507 ret = {"name": name, "changes": {}, "result": None, "comment": ""} 508 509 if "rules" in kwargs: 510 ret["changes"]["locale"] = [] 511 comments = [] 512 save = False 513 for rule in kwargs["rules"]: 514 if "rules" in rule: 515 del rule["rules"] 516 if "__agg__" in rule: 517 del rule["__agg__"] 518 if "save" in rule and rule["save"]: 519 save = True 520 if rule["save"] is not True: 521 save_file = rule["save"] 522 else: 523 save_file = True 524 rule["save"] = False 525 _ret = insert(**rule) 526 if "locale" in _ret["changes"]: 527 ret["changes"]["locale"].append(_ret["changes"]["locale"]) 528 comments.append(_ret["comment"]) 529 ret["result"] = _ret["result"] 530 if save: 531 if save_file is True: 532 save_file = None 533 __salt__["iptables.save"](filename=save_file, family=family) 534 if not ret["changes"]["locale"]: 535 del ret["changes"]["locale"] 536 ret["comment"] = "\n".join(comments) 537 return ret 538 539 for ignore in _STATE_INTERNAL_KEYWORDS: 540 if ignore in kwargs: 541 del kwargs[ignore] 542 kwargs["name"] = name 543 kwargs["table"] = table 544 rule = __salt__["iptables.build_rule"](family=family, **kwargs) 545 command = __salt__["iptables.build_rule"]( 546 full=True, family=family, command="I", **kwargs 547 ) 548 if __salt__["iptables.check"](table, kwargs["chain"], rule, family) is True: 549 ret["result"] = True 550 ret["comment"] = "iptables rule for {} already set for {} ({})".format( 551 name, family, command.strip() 552 ) 553 if "save" in kwargs and kwargs["save"]: 554 if kwargs["save"] is not True: 555 filename = kwargs["save"] 556 else: 557 filename = None 558 saved_rules = __salt__["iptables.get_saved_rules"]( 559 conf_file=filename, family=family 560 ) 561 _rules = __salt__["iptables.get_rules"](family=family) 562 __rules = [] 563 for table in _rules: 564 for chain in _rules[table]: 565 __rules.append(_rules[table][chain].get("rules")) 566 __saved_rules = [] 567 for table in saved_rules: 568 for chain in saved_rules[table]: 569 __saved_rules.append(saved_rules[table][chain].get("rules")) 570 # Only save if rules in memory are different than saved rules 571 if __rules != __saved_rules: 572 out = __salt__["iptables.save"](filename=filename, family=family) 573 ret["comment"] += "\nSaved iptables rule {} for {}\n{}\n{}".format( 574 name, family, command.strip(), out 575 ) 576 return ret 577 if __opts__["test"]: 578 ret["comment"] = "iptables rule for {} needs to be set for {} ({})".format( 579 name, family, command.strip() 580 ) 581 return ret 582 if not __salt__["iptables.insert"]( 583 table, kwargs["chain"], kwargs["position"], rule, family 584 ): 585 ret["changes"] = {"locale": name} 586 ret["result"] = True 587 ret["comment"] = "Set iptables rule for {} to: {} for {}".format( 588 name, command.strip(), family 589 ) 590 if "save" in kwargs and kwargs["save"]: 591 if kwargs["save"] is not True: 592 filename = kwargs["save"] 593 else: 594 filename = None 595 out = __salt__["iptables.save"](filename=filename, family=family) 596 ret["comment"] = "Set and saved iptables rule {} for {}\n{}\n{}".format( 597 name, family, command.strip(), out 598 ) 599 return ret 600 else: 601 ret["result"] = False 602 ret[ 603 "comment" 604 ] = "Failed to set iptables rule for {}.\nAttempted rule was {}".format( 605 name, command.strip() 606 ) 607 return ret 608 609 610def delete(name, table="filter", family="ipv4", **kwargs): 611 """ 612 .. versionadded:: 2014.1.0 613 614 Delete a rule to a chain 615 616 name 617 A user-defined name to call this rule by in another part of a state or 618 formula. This should not be an actual rule. 619 620 table 621 The table that owns the chain that should be modified 622 623 family 624 Networking family, either ipv4 or ipv6 625 626 All other arguments are passed in with the same name as the long option 627 that would normally be used for iptables, with one exception: ``--state`` is 628 specified as `connstate` instead of `state` (not to be confused with 629 `ctstate`). 630 631 Jump options that doesn't take arguments should be passed in with an empty 632 string. 633 """ 634 ret = {"name": name, "changes": {}, "result": None, "comment": ""} 635 636 if "rules" in kwargs: 637 ret["changes"]["locale"] = [] 638 comments = [] 639 save = False 640 for rule in kwargs["rules"]: 641 if "rules" in rule: 642 del rule["rules"] 643 if "__agg__" in rule: 644 del rule["__agg__"] 645 if "save" in rule and rule["save"]: 646 if rule["save"] is not True: 647 save_file = rule["save"] 648 else: 649 save_file = True 650 rule["save"] = False 651 _ret = delete(**rule) 652 if "locale" in _ret["changes"]: 653 ret["changes"]["locale"].append(_ret["changes"]["locale"]) 654 comments.append(_ret["comment"]) 655 ret["result"] = _ret["result"] 656 if save: 657 if save_file is True: 658 save_file = None 659 __salt__["iptables.save"](filename=save_file, family=family) 660 if not ret["changes"]["locale"]: 661 del ret["changes"]["locale"] 662 ret["comment"] = "\n".join(comments) 663 return ret 664 665 for ignore in _STATE_INTERNAL_KEYWORDS: 666 if ignore in kwargs: 667 del kwargs[ignore] 668 kwargs["name"] = name 669 kwargs["table"] = table 670 rule = __salt__["iptables.build_rule"](family=family, **kwargs) 671 command = __salt__["iptables.build_rule"]( 672 full=True, family=family, command="D", **kwargs 673 ) 674 675 if not __salt__["iptables.check"](table, kwargs["chain"], rule, family) is True: 676 if "position" not in kwargs: 677 ret["result"] = True 678 ret["comment"] = "iptables rule for {} already absent for {} ({})".format( 679 name, family, command.strip() 680 ) 681 return ret 682 if __opts__["test"]: 683 ret["comment"] = "iptables rule for {} needs to be deleted for {} ({})".format( 684 name, family, command.strip() 685 ) 686 return ret 687 688 if "position" in kwargs: 689 result = __salt__["iptables.delete"]( 690 table, kwargs["chain"], family=family, position=kwargs["position"] 691 ) 692 else: 693 result = __salt__["iptables.delete"]( 694 table, kwargs["chain"], family=family, rule=rule 695 ) 696 697 if not result: 698 ret["changes"] = {"locale": name} 699 ret["result"] = True 700 ret["comment"] = "Delete iptables rule for {} {}".format(name, command.strip()) 701 if "save" in kwargs and kwargs["save"]: 702 if kwargs["save"] is not True: 703 filename = kwargs["save"] 704 else: 705 filename = None 706 out = __salt__["iptables.save"](filename=filename, family=family) 707 ret["comment"] = "Deleted and saved iptables rule {} for {}\n{}\n{}".format( 708 name, family, command.strip(), out 709 ) 710 return ret 711 else: 712 ret["result"] = False 713 ret[ 714 "comment" 715 ] = "Failed to delete iptables rule for {}.\nAttempted rule was {}".format( 716 name, command.strip() 717 ) 718 return ret 719 720 721def set_policy(name, table="filter", family="ipv4", **kwargs): 722 """ 723 .. versionadded:: 2014.1.0 724 725 Sets the default policy for iptables firewall tables 726 727 table 728 The table that owns the chain that should be modified 729 730 family 731 Networking family, either ipv4 or ipv6 732 733 policy 734 The requested table policy 735 736 """ 737 ret = {"name": name, "changes": {}, "result": None, "comment": ""} 738 739 for ignore in _STATE_INTERNAL_KEYWORDS: 740 if ignore in kwargs: 741 del kwargs[ignore] 742 743 if ( 744 __salt__["iptables.get_policy"](table, kwargs["chain"], family) 745 == kwargs["policy"] 746 ): 747 ret["result"] = True 748 ret[ 749 "comment" 750 ] = "iptables default policy for chain {} on table {} for {} already set to {}".format( 751 kwargs["chain"], table, family, kwargs["policy"] 752 ) 753 return ret 754 if __opts__["test"]: 755 ret["comment"] = ( 756 "iptables default policy for chain {} on table {} for {} needs to be set" 757 " to {}".format(kwargs["chain"], table, family, kwargs["policy"]) 758 ) 759 return ret 760 if not __salt__["iptables.set_policy"]( 761 table, kwargs["chain"], kwargs["policy"], family 762 ): 763 ret["changes"] = {"locale": name} 764 ret["result"] = True 765 ret["comment"] = "Set default policy for {} to {} family {}".format( 766 kwargs["chain"], kwargs["policy"], family 767 ) 768 if "save" in kwargs and kwargs["save"]: 769 if kwargs["save"] is not True: 770 filename = kwargs["save"] 771 else: 772 filename = None 773 __salt__["iptables.save"](filename=filename, family=family) 774 ret[ 775 "comment" 776 ] = "Set and saved default policy for {} to {} family {}".format( 777 kwargs["chain"], kwargs["policy"], family 778 ) 779 return ret 780 else: 781 ret["result"] = False 782 ret["comment"] = "Failed to set iptables default policy" 783 return ret 784 785 786def flush(name, table="filter", family="ipv4", **kwargs): 787 """ 788 .. versionadded:: 2014.1.0 789 790 Flush current iptables state 791 792 table 793 The table that owns the chain that should be modified 794 795 family 796 Networking family, either ipv4 or ipv6 797 798 chain 799 The chain to be flushed. All the chains in the table if none is given. 800 801 802 """ 803 ret = {"name": name, "changes": {}, "result": None, "comment": ""} 804 805 for ignore in _STATE_INTERNAL_KEYWORDS: 806 if ignore in kwargs: 807 del kwargs[ignore] 808 809 if "chain" not in kwargs: 810 kwargs["chain"] = "" 811 if __opts__["test"]: 812 ret[ 813 "comment" 814 ] = "iptables rules in {} table {} chain {} family needs to be flushed".format( 815 name, table, family 816 ) 817 return ret 818 if not __salt__["iptables.flush"](table, kwargs["chain"], family): 819 ret["changes"] = {"locale": name} 820 ret["result"] = True 821 ret["comment"] = "Flush iptables rules in {} table {} chain {} family".format( 822 table, kwargs["chain"], family 823 ) 824 return ret 825 else: 826 ret["result"] = False 827 ret["comment"] = "Failed to flush iptables rules" 828 return ret 829 830 831def mod_aggregate(low, chunks, running): 832 """ 833 The mod_aggregate function which looks up all rules in the available 834 low chunks and merges them into a single rules ref in the present low data 835 """ 836 rules = [] 837 agg_enabled = [ 838 "append", 839 "insert", 840 ] 841 if low.get("fun") not in agg_enabled: 842 return low 843 for chunk in chunks: 844 tag = __utils__["state.gen_tag"](chunk) 845 if tag in running: 846 # Already ran the iptables state, skip aggregation 847 continue 848 if chunk.get("state") == "iptables": 849 if "__agg__" in chunk: 850 continue 851 # Check for the same function 852 if chunk.get("fun") != low.get("fun"): 853 continue 854 855 if chunk not in rules: 856 rules.append(chunk) 857 chunk["__agg__"] = True 858 859 if rules: 860 if "rules" in low: 861 low["rules"].extend(rules) 862 else: 863 low["rules"] = rules 864 return low 865