diff --git a/docs/sources/Cisco/index.md b/docs/sources/Cisco/index.md index 9928aec..ae72697 100644 --- a/docs/sources/Cisco/index.md +++ b/docs/sources/Cisco/index.md @@ -48,6 +48,55 @@ index= sourcetype=cisco:acs Verify timestamp, and host values match as expected +## Product - APIC (ACI) + +| Ref | Link | +|----------------|---------------------------------------------------------------------------------------------------------| +| Splunk Add-on | No current add-on for syslog events | +| Product Manual | https://community.cisco.com/t5/security-documents/acs-5-x-configuring-the-external-syslog-server/ta-p/3143143 | + + +### Sourcetypes + +| sourcetype | notes | +|----------------|---------------------------------------------------------------------------------------------------------| +| cisco:apic:acl: | APIC events from leaf switches | +| cisco:apic:events | APIC events from any component used | + +### Sourcetype and Index Configuration + +| key | sourcetype | index | notes | +|----------------|----------------|----------------|----------------| +| cisco_apic_acl | cisco:apic:acl | netfw | None | +| cisco_apic_events | cisco:apic:events | netops | None | + +### Filter type + +PATTERN MATCH + +### Setup and Configuration + +* No special steps required + +### Options + +| Variable | default | description | +|----------------|----------------|----------------| +| SC4S_LISTEN_CISCO_APIC_TCP_PORT | empty string | Enable a TCP port for this specific vendor product using the number defined | +| SC4S_LISTEN_CISCO_APIC_UDP_PORT | empty string | Enable a UDP port for this specific vendor product using the number defined | +| SC4S_ARCHIVE_CISCO_APIC | no | Enable archive to disk for this specific source | +| SC4S_DEST_CISCO_APIC_HEC | no | When Splunk HEC is disabled globally set to yes to enable this specific source | + +### Verification + +Use the following search to validate events are present + +``` +index= sourcetype=cisco:apic:* +``` + +Verify timestamp, and host values match as expected + ## Product - ASA (Pre Firepower) | Ref | Link | diff --git a/package/etc/conf.d/filters/cisco/apic.conf b/package/etc/conf.d/filters/cisco/apic.conf new file mode 100644 index 0000000..ea6660d --- /dev/null +++ b/package/etc/conf.d/filters/cisco/apic.conf @@ -0,0 +1,6 @@ + +filter f_cisco_apic { + program('^%LOG_LOCAL\d-\d-'); + or + program('^%ACLLOG-\d-ACLLOG_PKTLOG'); +}; \ No newline at end of file diff --git a/package/etc/conf.d/log_paths/lp-cisco_apic.conf.tmpl b/package/etc/conf.d/log_paths/lp-cisco_apic.conf.tmpl new file mode 100644 index 0000000..cd3d9f9 --- /dev/null +++ b/package/etc/conf.d/log_paths/lp-cisco_apic.conf.tmpl @@ -0,0 +1,56 @@ +# Cisco APIC +{{- /* The following provides a unique port source configuration if env var(s) are set */}} +{{- $context := dict "port_id" "CISCO_APIC" "parser" "rfc3164" }} +{{- tmpl.Exec "t/source_network.t" $context }} + +log { + junction { +{{- if or (or (getenv (print "SC4S_LISTEN_CISCO_APIC_TCP_PORT")) (getenv (print "SC4S_LISTEN_CISCO_APIC_UDP_PORT"))) (getenv (print "SC4S_LISTEN_CISCO_APIC_TLS_PORT")) }} + channel { + # Listen on the specified dedicated port(s) for CISCO_APIC traffic + source (s_CISCO_APIC); + flags (final); + }; +{{- end}} + channel { + # Listen on the default port (typically 514) for CISCO_APIC traffic + source (s_DEFAULT); + filter(f_cisco_apic); + flags(final); + }; + }; + + rewrite { + guess-time-zone(); + }; + if { + filter { + program('^%ACLLOG-\d-ACLLOG_PKTLOG') + }; + rewrite { + set("cisco_APIC_acl", value("fields.sc4s_vendor_product")); + r_set_splunk_dest_default(sourcetype("cisco:apic:acl"), index("netfw"), template("t_hdr_msg")) + }; + parser { p_add_context_splunk(key("cisco_apic_acl")); }; + + } elif { + rewrite { + set("cisco_APIC_events", value("fields.sc4s_vendor_product")); + r_set_splunk_dest_default(sourcetype("cisco:apic:events"), index("netops"), template("t_hdr_msg")) + }; + parser { p_add_context_splunk(key("cisco_apic_events")); }; + }; + + parser (compliance_meta_by_source); + rewrite { set("$(template ${.splunk.sc4s_template} $(template t_hdr_msg))" value("MSG")); }; + +{{- if or (conv.ToBool (getenv "SC4S_DEST_SPLUNK_HEC_GLOBAL" "yes")) (conv.ToBool (getenv "SC4S_DEST_CISCO_APIC_HEC" "no")) }} + destination(d_hec); +{{- end}} + +{{- if or (conv.ToBool (getenv "SC4S_ARCHIVE_GLOBAL" "no")) (conv.ToBool (getenv "SC4S_ARCHIVE_CISCO_APIC" "no")) }} + destination(d_archive); +{{- end}} + + flags(flow-control,final); +}; diff --git a/package/etc/context_templates/splunk_index.csv.example b/package/etc/context_templates/splunk_index.csv.example index f3975d7..e996eee 100644 --- a/package/etc/context_templates/splunk_index.csv.example +++ b/package/etc/context_templates/splunk_index.csv.example @@ -13,6 +13,8 @@ #checkpoint_splunk_web,index,netproxy #checkpoint_splunk,index,netops #checkpoint_splunk,index,netops +#cisco_apic_acl,index,netfw +#cisco_apic_events,index,netops #cisco_acs,index,netauth #cisco_asa,index,netfw #cisco_ios,index,netops diff --git a/tests/conftest.py b/tests/conftest.py index e5429ee..271f3a5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,6 +14,7 @@ import splunklib.client as client + @pytest.fixture(scope="module") def setup_wordlist(): path_to_current_file = os.path.realpath(__file__) diff --git a/tests/splunkutils.py b/tests/splunkutils.py index bb77a58..205fcfa 100644 --- a/tests/splunkutils.py +++ b/tests/splunkutils.py @@ -24,12 +24,13 @@ def splunk_single(service, search): if stats["isDone"] == "1": break - sleep(2) + else: + sleep(2) # Get the results and display them resultCount = stats["resultCount"] eventCount = stats["eventCount"] - if resultCount > 0 or tried > 15: + if resultCount > 0 or tried > 5: break else: tried += 1 diff --git a/tests/test_cisco_apic.py b/tests/test_cisco_apic.py new file mode 100644 index 0000000..b7509fb --- /dev/null +++ b/tests/test_cisco_apic.py @@ -0,0 +1,53 @@ +# Copyright 2019 Splunk, Inc. +# +# Use of this source code is governed by a BSD-2-clause-style +# license that can be found in the LICENSE-BSD2 file or at +# https://opensource.org/licenses/BSD-2-Clause +import random + +from jinja2 import Environment + +from .sendmessage import * +from .splunkutils import * + +env = Environment(extensions=['jinja2_time.TimeExtension']) + +#<11>July 22 22:45:28 apic1 %LOG_LOCAL0-2-SYSTEM_MSG [F0110][soaking][node-failed][critical][topology/pod-1/node-102/fault-F0110] Node 102 not reachable. unknown +def test_cisco_aci(record_property, setup_wordlist, setup_splunk, setup_sc4s): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + + mt = env.from_string( + "{{ mark }}{% now 'utc', '%b %d %H:%M:%S' %} {{ host }} %LOG_LOCAL0-2-SYSTEM_MSG [F0110][soaking][node-failed][critical][topology/pod-1/node-102/fault-F0110]\n") + message = mt.render(mark="<165>", host=host) + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string("search index=netops host=\"{{ host }}\" sourcetype=\"cisco:apic:events\" | head 2") + search = st.render(host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1 + +#%ACLLOG-5-ACLLOG_PKTLOG +def test_cisco_aci_acl(record_property, setup_wordlist, setup_splunk, setup_sc4s): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + + mt = env.from_string( + "{{ mark }}{% now 'utc', '%b %d %H:%M:%S' %} {{ host }} %ACLLOG-5-ACLLOG_PKTLOG unable to locate real message\n") + message = mt.render(mark="<165>", host=host) + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string("search index=netfw host=\"{{ host }}\" sourcetype=\"cisco:apic:acl\" | head 2") + search = st.render(host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1 diff --git a/tests/test_cisco_asa.py b/tests/test_cisco_asa.py index edc2389..db8d3ac 100644 --- a/tests/test_cisco_asa.py +++ b/tests/test_cisco_asa.py @@ -46,7 +46,7 @@ def test_cisco_asa_traditional_nohost(record_property, setup_wordlist, setup_spl sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) - st = env.from_string("search index=netfw sourcetype=\"cisco:asa\" \"%ASA-4-402119\" \"{host}\" | head 2") + st = env.from_string("search index=netfw sourcetype=\"cisco:asa\" \"%ASA-4-402119\" \"{{ host }}\" | head 2") search = st.render(host=host) resultCount, eventCount = splunk_single(setup_splunk, search)