From 04f25060b50d5468b976a7a10c3eac429981d535 Mon Sep 17 00:00:00 2001 From: pke11y Date: Wed, 1 Dec 2021 00:00:45 +0000 Subject: [PATCH 1/3] Normalise banner demiliter for IOS to ^C & support parsing delimiter ^ (#69) * Normailise banner demiliter for IOS to ^C & support parsing delimiter ^ --- docs/source/netutils/banner/index.rst | 6 ++ docs/source/netutils/index.rst | 1 + netutils/banner.py | 50 +++++++++++ netutils/config/parser.py | 13 +-- netutils/constants.py | 5 ++ netutils/utils.py | 2 + .../compliance/cisco_ios/ios_basic_backup.txt | 8 +- .../compliance/cisco_ios/ios_basic_feature.py | 1 + .../cisco_ios/ios_basic_intended.txt | 8 +- .../cisco_ios/ios_basic_received.json | 12 ++- tests/unit/test_banner.py | 87 +++++++++++++++++++ tests/unit/test_parser.py | 2 +- 12 files changed, 185 insertions(+), 10 deletions(-) create mode 100644 docs/source/netutils/banner/index.rst create mode 100644 netutils/banner.py create mode 100644 tests/unit/test_banner.py diff --git a/docs/source/netutils/banner/index.rst b/docs/source/netutils/banner/index.rst new file mode 100644 index 00000000..06a746b5 --- /dev/null +++ b/docs/source/netutils/banner/index.rst @@ -0,0 +1,6 @@ +******* +Banner +******* + +.. automodule:: netutils.banner + :members: \ No newline at end of file diff --git a/docs/source/netutils/index.rst b/docs/source/netutils/index.rst index 9aa93f09..3993f783 100644 --- a/docs/source/netutils/index.rst +++ b/docs/source/netutils/index.rst @@ -7,6 +7,7 @@ Netutils Functions asn/index bandwidth/index + banner/index configs/index dns/index interface/index diff --git a/netutils/banner.py b/netutils/banner.py new file mode 100644 index 00000000..3e2ee213 --- /dev/null +++ b/netutils/banner.py @@ -0,0 +1,50 @@ +"""Functions for working with the banner configuration.""" +import re +from netutils.constants import CARET_C + + +def delimiter_change(config, from_delimiter, to_delimiter): + r"""Change the banner delimiter. + + Args: + config (str): Configuration line containing banner delimiter. + from_delimiter (str): Delimiter to replace in the banner. + to_delimiter (str): Delimiter to include in the config. + + Returns: + str: Configuration with delimiter replaced. + + Example: + >>> from netutils.banner import delimiter_change + >>> delimiter_change("banner login ^\n******************\n TEST BANNER\n******************^", "^", "^C") + 'banner login ^C\n******************\n TEST BANNER\n******************^C' + >>> delimiter_change("banner login #\n******************\n TEST BANNER\n******************#", "#", "^C") + 'banner login ^C\n******************\n TEST BANNER\n******************^C' + >>> delimiter_change("banner login ^CCCCC\n******************\n TEST BANNER\n******************^C", "^C", "^C") + 'banner login ^C\n******************\n TEST BANNER\n******************^C' + """ + config_line = config.replace(from_delimiter, to_delimiter) + if to_delimiter == CARET_C: + config_line = re.sub(r"\^C+", CARET_C, config_line) + return config_line + + +def normalise_delimiter_caret_c(delimiter, config): + r"""Normalise delimiter to ^C. + + Args: + delimiter (str): Banner delimiter. + config (str): Configuration line containing banner delimiter. + + Returns: + str: Configuration with delimiter normalised to ^C. + + Example: + >>> from netutils.banner import normalise_delimiter_caret_c + >>> normalise_delimiter_caret_c("^", "banner login ^\n******************\n TEST BANNER\n******************^") + 'banner login ^C\n******************\n TEST BANNER\n******************^C' + >>> normalise_delimiter_caret_c("^C", "banner login ^CCCCC\n******************\n TEST BANNER\n******************^C") + 'banner login ^C\n******************\n TEST BANNER\n******************^C' + """ + config_line = delimiter_change(config, delimiter, CARET_C) + return config_line diff --git a/netutils/config/parser.py b/netutils/config/parser.py index cfa7990f..931079e1 100644 --- a/netutils/config/parser.py +++ b/netutils/config/parser.py @@ -3,6 +3,7 @@ import re from collections import namedtuple +from netutils.banner import normalise_delimiter_caret_c ConfigLine = namedtuple("ConfigLine", "config_line,parents") @@ -196,7 +197,7 @@ def _build_banner(self, config_line): if not self.is_banner_end(line): banner_config.append(line) else: - line = line.replace("\x03", "^C") + line = normalise_delimiter_caret_c(self.banner_end, line) banner_config.append(line) line = "\n".join(banner_config) if line.endswith("^C"): @@ -417,7 +418,7 @@ def _build_multiline_config(self, delimiter): class CiscoConfigParser(BaseSpaceConfigParser): """Cisco Implementation of ConfigParser Class.""" - regex_banner = re.compile(r"^(banner\s+\S+|\s*vacant-message)\s+(?P\^C|\x03)") + regex_banner = re.compile(r"^(banner\s+\S+|\s*vacant-message)\s+(?P\^C|.)") def __init__(self, config): """Create ConfigParser Object. @@ -449,9 +450,10 @@ def _build_banner(self, config_line): return None return super(CiscoConfigParser, self)._build_banner(config_line) - def is_banner_one_line(self, config_line): + @staticmethod + def is_banner_one_line(config_line): """Determine if all banner config is on one line.""" - _, delimeter, banner = config_line.partition(self.banner_end) + _, delimeter, banner = config_line.partition("^C") # Based on NXOS configs, the banner delimeter is ignored until another char is used banner_config_start = banner.lstrip(delimeter) if delimeter not in banner_config_start: @@ -504,8 +506,7 @@ def _build_banner(self, config_line): Raises: ValueError: When the parser is unable to identify the End of the Banner. """ - config_line = config_line.replace("\x03", "^C") - config_line = re.sub(r"\^C+", "^C", config_line) + config_line = normalise_delimiter_caret_c(self.banner_end, config_line) return super(IOSConfigParser, self)._build_banner(config_line) def _update_same_line_children_configs(self): diff --git a/netutils/constants.py b/netutils/constants.py index 3089369f..8081aed1 100644 --- a/netutils/constants.py +++ b/netutils/constants.py @@ -380,3 +380,8 @@ 340282366920938312347647155603121373184, 340282366920919120650260773364972912640, } + +# End of Text characters for banner +ETX_HEX = "\x03" +CARET_C = "^C" +CARET = "^" diff --git a/netutils/utils.py b/netutils/utils.py index 1570d107..7e4eeb45 100644 --- a/netutils/utils.py +++ b/netutils/utils.py @@ -59,6 +59,8 @@ "longest_prefix_match": "route.longest_prefix_match", "vlanlist_to_config": "vlan.vlanlist_to_config", "vlanconfig_to_list": "vlan.vlanconfig_to_list", + "normalise_delimiter_caret_c": "banner.normalise_delimiter_caret_c", + "delimiter_change": "banner.delimiter_change", } diff --git a/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_backup.txt b/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_backup.txt index 7c11962c..2d9161e5 100644 --- a/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_backup.txt +++ b/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_backup.txt @@ -8,4 +8,10 @@ access-list 1 permit 10.10.15.15 access-list 1 permit 10.10.20.20 ! ntp server 192.168.0.100 -ntp server 192.168.0.101 \ No newline at end of file +ntp server 192.168.0.101 +! +banner login ^C +****************** + TEST BANNER +****************** +^C \ No newline at end of file diff --git a/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_feature.py b/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_feature.py index 89d0635f..ef1ecb88 100644 --- a/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_feature.py +++ b/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_feature.py @@ -2,4 +2,5 @@ {"name": "bgp", "ordered": True, "section": ["router bgp "]}, {"name": "snmp", "ordered": True, "section": ["snmp-server "]}, {"name": "ntp", "ordered": False, "section": ["ntp server "]}, + {"name": "banner", "ordered": True, "section": ["banner "]}, ] diff --git a/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_intended.txt b/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_intended.txt index c694f63c..0b76de5b 100644 --- a/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_intended.txt +++ b/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_intended.txt @@ -8,4 +8,10 @@ access-list 1 permit 10.10.15.15 access-list 1 permit 10.10.20.20 ! ntp server 192.168.0.101 -ntp server 192.168.0.100 \ No newline at end of file +ntp server 192.168.0.100 +! +banner login ^ +****************** + TEST BANNER +****************** +^ \ No newline at end of file diff --git a/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_received.json b/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_received.json index d89ad26c..03543450 100644 --- a/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_received.json +++ b/tests/unit/mock/config/compliance/compliance/cisco_ios/ios_basic_received.json @@ -28,5 +28,15 @@ "missing": "", "ordered_compliant": false, "unordered_compliant": true - } + }, + "banner": { + "actual": "banner login ^C\n******************\n TEST BANNER\n******************^C", + "cannot_parse": true, + "compliant": true, + "extra": "", + "intended": "banner login ^C\n******************\n TEST BANNER\n******************^C", + "missing": "", + "ordered_compliant": true, + "unordered_compliant": true + } } \ No newline at end of file diff --git a/tests/unit/test_banner.py b/tests/unit/test_banner.py new file mode 100644 index 00000000..273f0aa1 --- /dev/null +++ b/tests/unit/test_banner.py @@ -0,0 +1,87 @@ +"""Test for the banner functions.""" + +import pytest +from netutils import banner + + +BANNER_CARET_C = "banner login ^C\n******************\n TEST BANNER\n******************^C" + +banner_config_and_delimiter = [ + { + "sent": ["banner login ^\n******************\n TEST BANNER\n******************^", "^", "^C"], + "received": BANNER_CARET_C, + }, + { + "sent": ["banner login ^\n******************\n TEST BANNER\n******************\n^", "^", "^C"], + "received": "banner login ^C\n******************\n TEST BANNER\n******************\n^C", + }, + { + "sent": ["banner login ^C\n******************\n TEST BANNER\n******************^C", "^C", "^C"], + "received": BANNER_CARET_C, + }, + { + "sent": ["banner login ^C\n******************\n TEST BANNER\n******************\n^C", "^C", "^C"], + "received": "banner login ^C\n******************\n TEST BANNER\n******************\n^C", + }, + { + "sent": ["banner login \x03\n******************\n TEST BANNER\n******************\x03", "\x03", "^C"], + "received": BANNER_CARET_C, + }, + { + "sent": ["banner login ^CCCCC\n******************\n TEST BANNER\n******************^C", "^C", "^C"], + "received": BANNER_CARET_C, + }, + { + "sent": ["banner login ^C\n******************\n TEST BANNER\n******************^C", "^C", "^"], + "received": "banner login ^\n******************\n TEST BANNER\n******************^", + }, + { + "sent": ["banner login ^C\n******************\n TEST BANNER\n******************^C", "^C", "\x03"], + "received": "banner login \x03\n******************\n TEST BANNER\n******************\x03", + }, + { + "sent": ["banner login #\n******************\n TEST BANNER\n******************#", "#", "^C"], + "received": BANNER_CARET_C, + }, + { + "sent": ["banner login $\n******************\n TEST BANNER\n******************\n$", "$", "^C"], + "received": "banner login ^C\n******************\n TEST BANNER\n******************\n^C", + }, +] + +banner_config = [ + { + "sent": ["^", "banner login ^\n******************\n TEST BANNER\n******************^"], + "received": BANNER_CARET_C, + }, + { + "sent": ["^C", "banner login ^C\n******************\n TEST BANNER\n******************^C"], + "received": BANNER_CARET_C, + }, + { + "sent": ["\x03", "banner login \x03\n******************\n TEST BANNER\n******************\x03"], + "received": BANNER_CARET_C, + }, + { + "sent": ["^C", "banner login ^CCCCC\n******************\n TEST BANNER\n******************^C"], + "received": BANNER_CARET_C, + }, + { + "sent": ["#", "banner login #\n******************\n TEST BANNER\n******************#"], + "received": BANNER_CARET_C, + }, + { + "sent": ["Z", "banner login Z\n******************\n TEST BANNER\n******************Z"], + "received": BANNER_CARET_C, + }, +] + + +@pytest.mark.parametrize("data", banner_config_and_delimiter) +def test_banner_delimitier_change(data): + assert banner.delimiter_change(*data["sent"]) == data["received"] + + +@pytest.mark.parametrize("data", banner_config) +def test_banner_normalise_delimiter_caret_c(data): + assert banner.normalise_delimiter_caret_c(*data["sent"]) == data["received"] diff --git a/tests/unit/test_parser.py b/tests/unit/test_parser.py index 862f643b..68ccd3fb 100644 --- a/tests/unit/test_parser.py +++ b/tests/unit/test_parser.py @@ -28,7 +28,7 @@ def test_incorrect_banner_ios(): banner_cfg = ( "aaa new-model\n" "!\n" - "banner exec c\n" + "banner exec $\n" "**************************************************************************\n" "* IOSv is strictly limited to use for evaluation, demonstration and IOS. *\n" "**************************************************************************c\n" From 4b359dfb1c235e933a4f4588a6d3f67128fb2d49 Mon Sep 17 00:00:00 2001 From: Jeff Kala <48843785+jeffkala@users.noreply.github.com> Date: Thu, 2 Dec 2021 10:51:10 -0600 Subject: [PATCH 2/3] F5 parser fix for irules with multiline single command lines. (#79) * add prepocessing to remove irules --- docs/source/netutils/configs/index.rst | 4 + netutils/config/parser.py | 124 ++++++++++++++++++ .../parser/bigip_f5/bigip_full_received.py | 24 ++++ .../parser/bigip_f5/bigip_full_sent.txt | 49 +++++++ 4 files changed, 201 insertions(+) diff --git a/docs/source/netutils/configs/index.rst b/docs/source/netutils/configs/index.rst index 1ae43f59..ffbc954c 100644 --- a/docs/source/netutils/configs/index.rst +++ b/docs/source/netutils/configs/index.rst @@ -17,3 +17,7 @@ Edge Cases Fortinet Fortios Parser ----------------------- - In order to support html blocks that exist in Fortios configurations, some preprocessing is executed, this is a regex that specifically grabs everything between quotes after the 'set buffer' sub-command. It's explicitly looking for double quote followed by a newline ("\n) to end the captured data. This support for html data will not support any other html that doesn't follow this convention. + +F5 Parser +----------------------- +- The "ltm rule" configuration sections are not uniform nor standardized; therefor, these sections are completely removed from the configuration in a preprocessing event. diff --git a/netutils/config/parser.py b/netutils/config/parser.py index 931079e1..58b14611 100644 --- a/netutils/config/parser.py +++ b/netutils/config/parser.py @@ -598,6 +598,130 @@ class F5ConfigParser(BaseBraceConfigParser): multiline_delimiters = ['"'] + def __init__(self, config): + """Create ConfigParser Object. + + Args: + config (str): The config text to parse. + """ + super().__init__(self._clean_config_f5(config)) + + def _clean_config_f5(self, config_text): # pylint: disable=no-self-use + """Removes all configuration items with 'ltm rule'. + + iRules are essentially impossible to parse with the lack of uniformity, + therefore, this method ensures they are not included in ``self.config``. + + Args: + config_text (str): The entire config as a string. + + Returns: + str: The sanitized config with all iRules (ltm rule) stanzas removed. + """ + config_split = config_text.split("ltm rule") + if len(config_split) > 1: + start_config = config_split[0] + end_config = config_split[-1] + _, ltm, clean_config = end_config.partition("ltm") + final_config = start_config + ltm + clean_config + else: + final_config = config_text + return final_config + + def build_config_relationship(self): + r"""Parse text tree of config lines and their parents. + + Example: + >>> config = '''apm resource webtop-link aShare { + ... application-uri http://funshare.example.com + ... customization-group a_customization_group + ... } + ... apm sso form-based portal_ext_sso_form_based { + ... form-action /Citrix/Example/ExplicitAuth/LoginAttempt + ... form-field "LoginBtn Log+On + ... StateContext " + ... form-password password + ... form-username username + ... passthru true + ... start-uri /Citrix/Example/ExplicitAuth/Login* + ... success-match-type cookie + ... success-match-value CtxsAuthId + ... } + ... ''' + >>> + >>> config_tree = F5ConfigParser(config) + >>> print(config_tree.build_config_relationship()) + [ConfigLine(config_line='apm resource webtop-link aShare {', parents=()), ConfigLine(config_line=' application-uri http://funshare.example.com', parents=('apm resource webtop-link aShare {',)), ConfigLine(config_line=' customization-group a_customization_group', parents=('apm resource webtop-link aShare {',)), ConfigLine(config_line='}', parents=('apm resource webtop-link aShare {',)), ConfigLine(config_line='apm sso form-based portal_ext_sso_form_based {', parents=()), ConfigLine(config_line=' form-action /Citrix/Example/ExplicitAuth/LoginAttempt', parents=('apm sso form-based portal_ext_sso_form_based {',)), ConfigLine(config_line=' form-field "LoginBtn Log+On\nStateContext "', parents=('apm sso form-based portal_ext_sso_form_based {',)), ConfigLine(config_line=' form-password password', parents=()), ConfigLine(config_line=' form-username username', parents=()), ConfigLine(config_line=' passthru true', parents=()), ConfigLine(config_line=' start-uri /Citrix/Example/ExplicitAuth/Login*', parents=()), ConfigLine(config_line=' success-match-type cookie', parents=()), ConfigLine(config_line=' success-match-value CtxsAuthId', parents=()), ConfigLine(config_line='}', parents=())] + """ + for line in self.generator_config: + self.config_lines.append(ConfigLine(line, self._current_parents)) + line_end = line[-1] + if line.endswith("{"): + self._current_parents += (line,) + elif line.lstrip() == "}": + self._current_parents = self._current_parents[:-1] + elif any( + delimiters in self.multiline_delimiters and line.count(delimiters) == 1 + for delimiters in self.multiline_delimiters + ): + for delimiter in self.multiline_delimiters: + if line.count(delimiter) == 1: + self._build_multiline_single_configuration_line(delimiter, line) + elif line_end in self.multiline_delimiters and line.count(line_end) == 1: + self._current_parents += (line,) + self._build_multiline_config(line_end) + + return self.config_lines + + def _build_multiline_single_configuration_line(self, delimiter, prev_line): + r"""Concatenate Multiline strings between delimiter when newlines causes string to traverse multiple lines. + + Args: + delimiter (str): The text to look for to end multiline config. + prev_line (str): The text from the previously analyzed line. + + Returns: + ConfigLine: The multiline string text that was added to ``self.config_lines``. + + Example: + config = '''apm resource webtop-link aShare { + application-uri http://funshare.example.com + customization-group a_customization_group + } + apm sso form-based portal_ext_sso_form_based { + form-action /Citrix/Example/ExplicitAuth/LoginAttempt + >>> + >>> config = '''apm resource webtop-link aShare { + ... application-uri http://funshare.example.com + ... customization-group a_customization_group + ... } + ... apm sso form-based portal_ext_sso_form_based { + ... form-action /Citrix/Example/ExplicitAuth/LoginAttempt + ... form-field "LoginBtn Log+On + ... StateContext " + ... form-password password + ... form-username username + ... passthru true + ... start-uri /Citrix/Example/ExplicitAuth/Login* + ... success-match-type cookie + ... success-match-value CtxsAuthId + ... } + ... ''' + >>> + >>> + >>> config_tree = F5ConfigParser(str(config)) + >>> print(config_tree.build_config_relationship()) + [ConfigLine(config_line='apm resource webtop-link aShare {', parents=()), ConfigLine(config_line=' application-uri http://funshare.example.com', parents=('apm resource webtop-link aShare {',)), ConfigLine(config_line=' customization-group a_customization_group', parents=('apm resource webtop-link aShare {',)), ConfigLine(config_line='}', parents=('apm resource webtop-link aShare {',)), ConfigLine(config_line='apm sso form-based portal_ext_sso_form_based {', parents=()), ConfigLine(config_line=' form-action /Citrix/Example/ExplicitAuth/LoginAttempt', parents=('apm sso form-based portal_ext_sso_form_based {',)), ConfigLine(config_line=' form-field "LoginBtn Log+On\nStateContext "', parents=('apm sso form-based portal_ext_sso_form_based {',)), ConfigLine(config_line=' form-password password', parents=()), ConfigLine(config_line=' form-username username', parents=()), ConfigLine(config_line=' passthru true', parents=()), ConfigLine(config_line=' start-uri /Citrix/Example/ExplicitAuth/Login*', parents=()), ConfigLine(config_line=' success-match-type cookie', parents=()), ConfigLine(config_line=' success-match-value CtxsAuthId', parents=()), ConfigLine(config_line='}', parents=())] + """ + multiline_config = [prev_line] + for line in self.generator_config: + multiline_config.append(line) + if line.endswith(delimiter): + multiline_entry = ConfigLine("\n".join(multiline_config), self._current_parents) + self.config_lines[-1] = multiline_entry + self._current_parents = self._current_parents[:-1] + return multiline_entry + class JunosConfigParser(BaseSpaceConfigParser): """Junos config parser.""" diff --git a/tests/unit/mock/config/parser/bigip_f5/bigip_full_received.py b/tests/unit/mock/config/parser/bigip_f5/bigip_full_received.py index 1a006288..2342c906 100644 --- a/tests/unit/mock/config/parser/bigip_f5/bigip_full_received.py +++ b/tests/unit/mock/config/parser/bigip_f5/bigip_full_received.py @@ -24,6 +24,30 @@ ConfigLine(config_line=" }", parents=("auth user admin {", " partition-access {")), ConfigLine(config_line=" shell bash", parents=("auth user admin {",)), ConfigLine(config_line="}", parents=("auth user admin {",)), + ConfigLine(config_line="apm resource webtop-link aShare {", parents=()), + ConfigLine( + config_line=" application-uri http://funshare.example.com", parents=("apm resource webtop-link aShare {",) + ), + ConfigLine( + config_line=" customization-group a_customization_group", parents=("apm resource webtop-link aShare {",) + ), + ConfigLine(config_line="}", parents=("apm resource webtop-link aShare {",)), + ConfigLine(config_line="apm sso form-based portal_ext_sso_form_based {", parents=()), + ConfigLine( + config_line=" form-action /Citrix/Example/ExplicitAuth/LoginAttempt", + parents=("apm sso form-based portal_ext_sso_form_based {",), + ), + ConfigLine( + config_line=' form-field "LoginBtn Log+On\nStateContext "', + parents=("apm sso form-based portal_ext_sso_form_based {",), + ), + ConfigLine(config_line=" form-password password", parents=()), + ConfigLine(config_line=" form-username username", parents=()), + ConfigLine(config_line=" passthru true", parents=()), + ConfigLine(config_line=" start-uri /Citrix/Example/ExplicitAuth/Login*", parents=()), + ConfigLine(config_line=" success-match-type cookie", parents=()), + ConfigLine(config_line=" success-match-value CtxsAuthId", parents=()), + ConfigLine(config_line="}", parents=()), ConfigLine(config_line="cli global-settings { }", parents=()), ConfigLine(config_line="cli preference {", parents=()), ConfigLine(config_line=" alias-path { /Common }", parents=("cli preference {",)), diff --git a/tests/unit/mock/config/parser/bigip_f5/bigip_full_sent.txt b/tests/unit/mock/config/parser/bigip_f5/bigip_full_sent.txt index fc9a3cd9..24899857 100644 --- a/tests/unit/mock/config/parser/bigip_f5/bigip_full_sent.txt +++ b/tests/unit/mock/config/parser/bigip_f5/bigip_full_sent.txt @@ -13,6 +13,21 @@ auth user admin { } shell bash } +apm resource webtop-link aShare { + application-uri http://funshare.example.com + customization-group a_customization_group +} +apm sso form-based portal_ext_sso_form_based { + form-action /Citrix/Example/ExplicitAuth/LoginAttempt + form-field "LoginBtn Log+On +StateContext " + form-password password + form-username username + passthru true + start-uri /Citrix/Example/ExplicitAuth/Login* + success-match-type cookie + success-match-value CtxsAuthId +} cli global-settings { } cli preference { alias-path { /Common } @@ -154,6 +169,40 @@ ltm global-settings general { share-single-mac vmw-compat } ltm global-settings rule { } +ltm rule imap_tls { +when CLIENT_ACCEPTED { + SSL::disable + } + when SERVER_CONNECTED { + TCP::collect + } + when CLIENT_DATA { + set lcpayload [string tolower [TCP::payload]] + if { $lcpayload contains "starttls" } { + set tag [getfield [TCP::payload] " " 1] + TCP::respond "$tag OK \"Begin TLS negotiation now\"\r\n" + TCP::payload replace 0 [TCP::payload length] "" + TCP::release + SSL::enable + } else { + set id [getfield [TCP::payload] " " 1] + TCP::respond "$id BAD \"Must issue a STARTTLS command first\"\r\n" + TCP::payload replace 0 [TCP::payload length] "" + TCP::release + TCP::collect + } + } + when SERVER_DATA { + if { [TCP::payload] contains "* CAPABILITY" } { + TCP::payload replace 12 0 " STARTTLS" + TCP::release + clientside { TCP::collect } + } else { + TCP::release + TCP::collect + } +} +} ltm global-settings traffic-control { } ltm persistence global-settings { } ltm tacdb licenseddb licensed-tacdb { From c2d75178d2613a44f070f55ef94e11866eef8f36 Mon Sep 17 00:00:00 2001 From: Jeff Kala <48843785+jeffkala@users.noreply.github.com> Date: Thu, 2 Dec 2021 12:13:25 -0600 Subject: [PATCH 3/3] remove support for old function 'is_fqdn_valid' as prep for 1.0.0 (#83) * remove support for old function as prep for 1.0.0 * remove is_fqdn_valid jinja map * prep for 1.0.0 release --- CHANGELOG.md | 14 ++++++++++++++ netutils/__init__.py | 2 +- netutils/dns.py | 4 ---- netutils/utils.py | 1 - pyproject.toml | 2 +- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 001962fc..3e18dbd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## v1.0.0 - 2021-11 + +### Added + +- #69 Normalise banner demiliter for IOS to ^C & support parsing delimiter ^ + +### Fixed + +- #79 F5 parser fix for irules with multiline single command lines. + +### Removed + +- #83 remove support for old function 'is_fqdn_valid' as prep for 1.0.0 + ## v0.2.5 - 2021-11 ### Added diff --git a/netutils/__init__.py b/netutils/__init__.py index 5092a193..31ad53a3 100644 --- a/netutils/__init__.py +++ b/netutils/__init__.py @@ -1,3 +1,3 @@ """Initialization file for library.""" -__version__ = "0.2.5" +__version__ = "1.0.0" diff --git a/netutils/dns.py b/netutils/dns.py index bf5399a4..a57ac16a 100644 --- a/netutils/dns.py +++ b/netutils/dns.py @@ -53,7 +53,3 @@ def is_fqdn_resolvable(hostname): return True except socket.error: return False - - -# Provide until transition to 1.0 -is_fqdn_valid = is_fqdn_resolvable diff --git a/netutils/utils.py b/netutils/utils.py index 7e4eeb45..28b1a807 100644 --- a/netutils/utils.py +++ b/netutils/utils.py @@ -17,7 +17,6 @@ "find_unordered_cfg_lines": "config.compliance.find_unordered_cfg_lines", "section_config": "config.compliance.section_config", "fqdn_to_ip": "dns.fqdn_to_ip", - "is_fqdn_valid": "dns.is_fqdn_valid", "is_fqdn_resolvable": "dns.is_fqdn_resolvable", "interface_range_expansion": "interface.interface_range_expansion", "interface_range_compress": "interface.interface_range_compress", diff --git a/pyproject.toml b/pyproject.toml index 75b0568d..32259061 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netutils" -version = "0.2.5" +version = "1.0.0" description = "Common helper functions useful in network automation." authors = ["Network to Code, LLC "] license = "Apache-2.0"