From 45b15652a4b48e7d2f01b790cf657831664c057a Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 14 Mar 2023 21:55:17 -0600 Subject: [PATCH 001/239] added rsync utility to docker images --- Dockerfiles/api.Dockerfile | 2 +- Dockerfiles/arkime.Dockerfile | 1 + Dockerfiles/dashboards-helper.Dockerfile | 2 +- Dockerfiles/dashboards.Dockerfile | 2 +- Dockerfiles/file-monitor.Dockerfile | 3 ++- Dockerfiles/file-upload.Dockerfile | 1 + Dockerfiles/filebeat.Dockerfile | 1 + Dockerfiles/freq.Dockerfile | 1 + Dockerfiles/htadmin.Dockerfile | 1 + Dockerfiles/logstash.Dockerfile | 1 + Dockerfiles/name-map-ui.Dockerfile | 2 +- Dockerfiles/netbox.Dockerfile | 1 + Dockerfiles/nginx.Dockerfile | 2 +- Dockerfiles/opensearch.Dockerfile | 2 +- Dockerfiles/pcap-capture.Dockerfile | 1 + Dockerfiles/pcap-monitor.Dockerfile | 1 + Dockerfiles/postgresql.Dockerfile | 2 +- Dockerfiles/redis.Dockerfile | 2 +- Dockerfiles/suricata.Dockerfile | 1 + Dockerfiles/zeek.Dockerfile | 1 + 20 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Dockerfiles/api.Dockerfile b/Dockerfiles/api.Dockerfile index 2859869fd..7275bc3f3 100644 --- a/Dockerfiles/api.Dockerfile +++ b/Dockerfiles/api.Dockerfile @@ -76,7 +76,7 @@ COPY shared/bin/opensearch_status.sh "${APP_HOME}"/ ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ RUN apt-get -q update \ && apt-get -y -q --no-install-recommends upgrade \ - && apt-get -y -q --no-install-recommends install curl netcat tini \ + && apt-get -y -q --no-install-recommends install curl netcat rsync tini \ && python3 -m pip install --upgrade pip \ && python3 -m pip install --no-cache /wheels/* \ && chmod 755 /usr/local/bin/docker-uid-gid-setup.sh \ diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index 889b703cb..1366de05d 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -45,6 +45,7 @@ RUN apt-get -q update && \ python3-pip \ python3-setuptools \ python3-wheel \ + rsync \ sudo \ swig \ wget \ diff --git a/Dockerfiles/dashboards-helper.Dockerfile b/Dockerfiles/dashboards-helper.Dockerfile index 3d56ce366..e586d12a2 100644 --- a/Dockerfiles/dashboards-helper.Dockerfile +++ b/Dockerfiles/dashboards-helper.Dockerfile @@ -71,7 +71,7 @@ ADD scripts/malcolm_common.py /data/ RUN apk update --no-cache && \ apk upgrade --no-cache && \ - apk --no-cache add bash python3 py3-pip curl openssl procps psmisc npm shadow jq tini && \ + apk --no-cache add bash python3 py3-pip curl openssl procps psmisc npm rsync shadow jq tini && \ npm install -g http-server && \ pip3 install supervisor humanfriendly requests && \ curl -fsSLO "$SUPERCRONIC_URL" && \ diff --git a/Dockerfiles/dashboards.Dockerfile b/Dockerfiles/dashboards.Dockerfile index 0b70fcb96..eb8579a17 100644 --- a/Dockerfiles/dashboards.Dockerfile +++ b/Dockerfiles/dashboards.Dockerfile @@ -116,7 +116,7 @@ COPY --from=build /usr/share/opensearch-dashboards/plugins/sankey_vis/build/kbnS ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini RUN yum upgrade -y && \ - yum install -y curl psmisc util-linux openssl python3 zip unzip && \ + yum install -y curl psmisc util-linux openssl rsync python3 zip unzip && \ usermod -a -G tty ${PUSER} && \ # Malcolm manages authentication and encryption via NGINX reverse proxy /usr/share/opensearch-dashboards/bin/opensearch-dashboards-plugin remove securityDashboards --allow-root && \ diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index ec3486357..b2d506dab 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -132,7 +132,8 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l python3-pip \ python3-pyinotify \ python3-requests \ - python3-zmq && \ + python3-zmq \ + rsync && \ pip3 install clamd supervisor yara-python python-magic psutil pycryptodome && \ curl -fsSLO "$SUPERCRONIC_URL" && \ echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - && \ diff --git a/Dockerfiles/file-upload.Dockerfile b/Dockerfiles/file-upload.Dockerfile index 287efd1c2..c522b9d5a 100644 --- a/Dockerfiles/file-upload.Dockerfile +++ b/Dockerfiles/file-upload.Dockerfile @@ -67,6 +67,7 @@ RUN apt-get -q update && \ php$PHP_VERSION-fpm \ php$PHP_VERSION-apcu \ nginx-light \ + rsync \ tini && \ apt-get clean -y -q && \ rm -rf /var/lib/apt/lists/* diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index ea6a808de..b87576415 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -86,6 +86,7 @@ RUN apt-get -q update && \ psmisc \ python3-pip \ python3-setuptools \ + rsync \ tar \ unar \ unzip \ diff --git a/Dockerfiles/freq.Dockerfile b/Dockerfiles/freq.Dockerfile index 914ec2cee..8d9d4d7d8 100644 --- a/Dockerfiles/freq.Dockerfile +++ b/Dockerfiles/freq.Dockerfile @@ -38,6 +38,7 @@ RUN apt-get -q update && \ python3 \ python3-dev \ python3-pip \ + rsync \ tini && \ pip3 install supervisor six && \ cd /opt && \ diff --git a/Dockerfiles/htadmin.Dockerfile b/Dockerfiles/htadmin.Dockerfile index 54dcdc17e..b47212fcd 100644 --- a/Dockerfiles/htadmin.Dockerfile +++ b/Dockerfiles/htadmin.Dockerfile @@ -51,6 +51,7 @@ RUN apt-get -q update && \ php$PHP_VERSION-fpm \ php$PHP_VERSION-gd \ procps \ + rsync \ supervisor \ tini && \ ( yes '' | pecl channel-update pecl.php.net ) && \ diff --git a/Dockerfiles/logstash.Dockerfile b/Dockerfiles/logstash.Dockerfile index ed4e4cbc5..4a17924a4 100644 --- a/Dockerfiles/logstash.Dockerfile +++ b/Dockerfiles/logstash.Dockerfile @@ -55,6 +55,7 @@ RUN set -x && \ python3-setuptools \ python3-pip \ python3-requests \ + rsync \ tini && \ chmod +x /usr/bin/tini && \ pip3 install ipaddress supervisor manuf pyyaml && \ diff --git a/Dockerfiles/name-map-ui.Dockerfile b/Dockerfiles/name-map-ui.Dockerfile index a98eb3fbd..4425dc385 100644 --- a/Dockerfiles/name-map-ui.Dockerfile +++ b/Dockerfiles/name-map-ui.Dockerfile @@ -28,7 +28,7 @@ RUN apk update --no-cache && \ apk upgrade --no-cache && \ apk --no-cache add bash php81 php81-fpm php81-mysqli php81-json php81-openssl php81-curl php81-fileinfo \ php81-zlib php81-xml php81-phar php81-intl php81-dom php81-xmlreader php81-ctype php81-session \ - php81-mbstring php81-gd nginx supervisor curl inotify-tools file psmisc shadow openssl tini + php81-mbstring php81-gd nginx supervisor curl inotify-tools file psmisc rsync shadow openssl tini COPY name-map-ui/config/nginx.conf /etc/nginx/nginx.conf COPY name-map-ui/config/fpm-pool.conf /etc/php81/php-fpm.d/www.conf diff --git a/Dockerfiles/netbox.Dockerfile b/Dockerfiles/netbox.Dockerfile index 3503b04c7..0eb09fd6a 100644 --- a/Dockerfiles/netbox.Dockerfile +++ b/Dockerfiles/netbox.Dockerfile @@ -45,6 +45,7 @@ RUN apt-get -q update && \ jq \ procps \ psmisc \ + rsync \ supervisor \ tini && \ /opt/netbox/venv/bin/python -m pip install psycopg2 pynetbox python-slugify randomcolor && \ diff --git a/Dockerfiles/nginx.Dockerfile b/Dockerfiles/nginx.Dockerfile index 2c2e0a197..cbd56e5b1 100644 --- a/Dockerfiles/nginx.Dockerfile +++ b/Dockerfiles/nginx.Dockerfile @@ -143,7 +143,7 @@ RUN set -x ; \ " ; \ apk update --no-cache; \ apk upgrade --no-cache; \ - apk add --no-cache curl shadow libressl; \ + apk add --no-cache curl rsync shadow libressl; \ addgroup -g ${DEFAULT_GID} -S ${PGROUP} ; \ adduser -S -D -H -u ${DEFAULT_UID} -h /var/cache/nginx -s /sbin/nologin -G ${PGROUP} -g ${PUSER} ${PUSER} ; \ addgroup ${PUSER} shadow ; \ diff --git a/Dockerfiles/opensearch.Dockerfile b/Dockerfiles/opensearch.Dockerfile index d745a1705..ed8663d34 100644 --- a/Dockerfiles/opensearch.Dockerfile +++ b/Dockerfiles/opensearch.Dockerfile @@ -41,7 +41,7 @@ ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/ # Remove the opensearch-security plugin - Malcolm manages authentication and encryption via NGINX reverse proxy # Remove the performance-analyzer plugin - Reduce resources in docker image -RUN yum install -y openssl util-linux procps && \ +RUN yum install -y openssl util-linux procps rsync && \ yum upgrade -y && \ /usr/share/opensearch/bin/opensearch-plugin remove opensearch-security --purge && \ /usr/share/opensearch/bin/opensearch-plugin remove opensearch-performance-analyzer --purge && \ diff --git a/Dockerfiles/pcap-capture.Dockerfile b/Dockerfiles/pcap-capture.Dockerfile index 7cfc61744..fd98b1d3d 100644 --- a/Dockerfiles/pcap-capture.Dockerfile +++ b/Dockerfiles/pcap-capture.Dockerfile @@ -68,6 +68,7 @@ RUN apt-get -q update && \ openssl \ procps \ psmisc \ + rsync \ supervisor \ tcpdump \ tini && \ diff --git a/Dockerfiles/pcap-monitor.Dockerfile b/Dockerfiles/pcap-monitor.Dockerfile index 7abeb18df..79e5ae7c7 100644 --- a/Dockerfiles/pcap-monitor.Dockerfile +++ b/Dockerfiles/pcap-monitor.Dockerfile @@ -54,6 +54,7 @@ RUN apt-get -q update && \ python3-pip \ python3-setuptools \ python3-wheel \ + rsync \ supervisor \ tini \ vim-tiny && \ diff --git a/Dockerfiles/postgresql.Dockerfile b/Dockerfiles/postgresql.Dockerfile index 7653ab105..4c0bd8fc8 100644 --- a/Dockerfiles/postgresql.Dockerfile +++ b/Dockerfiles/postgresql.Dockerfile @@ -27,7 +27,7 @@ COPY --from=pierrezemb/gostatic --chmod=755 /goStatic /usr/bin/goStatic RUN apk update --no-cache && \ apk upgrade --no-cache && \ - apk add --no-cache bash procps psmisc shadow tini && \ + apk add --no-cache bash procps psmisc rsync shadow tini && \ apk add --no-cache --virtual .build-deps rsync && \ rsync -a /usr/local/bin/ /usr/bin/ && \ rsync -a /usr/local/share/ /usr/share/ && \ diff --git a/Dockerfiles/redis.Dockerfile b/Dockerfiles/redis.Dockerfile index d79f3df05..f3e158a7d 100644 --- a/Dockerfiles/redis.Dockerfile +++ b/Dockerfiles/redis.Dockerfile @@ -26,7 +26,7 @@ COPY --from=pierrezemb/gostatic --chmod=755 /goStatic /usr/bin/goStatic RUN apk update --no-cache && \ apk upgrade --no-cache && \ - apk --no-cache add bash psmisc shadow tini && \ + apk --no-cache add bash psmisc rsync shadow tini && \ addgroup ${PUSER} tty WORKDIR /home/${PUSER} diff --git a/Dockerfiles/suricata.Dockerfile b/Dockerfiles/suricata.Dockerfile index 4dd34aead..5cdcaffa3 100644 --- a/Dockerfiles/suricata.Dockerfile +++ b/Dockerfiles/suricata.Dockerfile @@ -92,6 +92,7 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l psmisc \ python3-ruamel.yaml \ python3-zmq \ + rsync \ supervisor \ vim-tiny \ tini \ diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index 86ec72736..602547de9 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -102,6 +102,7 @@ RUN export DEBARCH=$(dpkg --print-architecture) && \ python3-tz \ python3-wheel \ python3-zmq \ + rsync \ supervisor \ swig \ tini \ From d872bf9d58d7abd87830edaba0f1deeba2fcf927 Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 16 Mar 2023 13:59:55 -0600 Subject: [PATCH 002/239] Initial minor tweaks to things to allow the new configmap docker entrypoint to work its magical magic --- docker-compose-standalone.yml | 2 +- docker-compose.yml | 2 +- docs/contributing-local-modifications.md | 2 +- nginx/nginx_auth_basic.conf | 2 +- nginx/scripts/docker_entrypoint.sh | 2 +- shared/bin/docker-uid-gid-setup.sh | 63 +++++++++++++++++++++--- 6 files changed, 60 insertions(+), 13 deletions(-) diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index ccdc723e3..9daed7ec3 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -1037,7 +1037,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - nginx-log-path:/var/log/nginx:rw - ./nginx/nginx_ldap.conf:/etc/nginx/nginx_ldap.conf:ro - - ./nginx/htpasswd:/etc/nginx/.htpasswd:ro + - ./nginx/htpasswd:/etc/nginx/htpasswd:ro - ./nginx/certs:/etc/nginx/certs:ro - ./nginx/certs/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro healthcheck: diff --git a/docker-compose.yml b/docker-compose.yml index 25cb415e6..1e5e01b3b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1116,7 +1116,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - nginx-log-path:/var/log/nginx:rw - ./nginx/nginx_ldap.conf:/etc/nginx/nginx_ldap.conf:ro - - ./nginx/htpasswd:/etc/nginx/.htpasswd:ro + - ./nginx/htpasswd:/etc/nginx/htpasswd:ro - ./nginx/certs:/etc/nginx/certs:ro - ./nginx/certs/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro healthcheck: diff --git a/docs/contributing-local-modifications.md b/docs/contributing-local-modifications.md index cd1444b08..f8f5d8705 100644 --- a/docs/contributing-local-modifications.md +++ b/docs/contributing-local-modifications.md @@ -98,7 +98,7 @@ $ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml nginx-proxy: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./nginx/nginx_ldap.conf:/etc/nginx/nginx_ldap.conf:ro - - ./nginx/htpasswd:/etc/nginx/.htpasswd:ro + - ./nginx/htpasswd:/etc/nginx/htpasswd:ro - ./nginx/certs:/etc/nginx/certs:ro - ./nginx/certs/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro ``` diff --git a/nginx/nginx_auth_basic.conf b/nginx/nginx_auth_basic.conf index 42cc9d1f6..9b42438fd 100644 --- a/nginx/nginx_auth_basic.conf +++ b/nginx/nginx_auth_basic.conf @@ -1,3 +1,3 @@ auth_basic "Authentication Required"; -auth_basic_user_file /etc/nginx/.htpasswd; +auth_basic_user_file /etc/nginx/htpasswd; set $authenticated_user $remote_user; diff --git a/nginx/scripts/docker_entrypoint.sh b/nginx/scripts/docker_entrypoint.sh index 8215af859..a8bf3a3ea 100755 --- a/nginx/scripts/docker_entrypoint.sh +++ b/nginx/scripts/docker_entrypoint.sh @@ -38,7 +38,7 @@ NGINX_SSL_CONF=/etc/nginx/nginx_ssl_config.conf # a blank file just to use as an "include" placeholder for the nginx's LDAP config when LDAP is not used NGINX_BLANK_CONF=/etc/nginx/nginx_blank.conf -# "include" file for auth_basic, prompt, and .htpasswd location +# "include" file for auth_basic, prompt, and htpasswd location NGINX_BASIC_AUTH_CONF=/etc/nginx/nginx_auth_basic.conf # "include" file for auth_ldap, prompt, and "auth_ldap_servers" name diff --git a/shared/bin/docker-uid-gid-setup.sh b/shared/bin/docker-uid-gid-setup.sh index d111ebfd0..d6375569f 100755 --- a/shared/bin/docker-uid-gid-setup.sh +++ b/shared/bin/docker-uid-gid-setup.sh @@ -10,19 +10,65 @@ unset ENTRYPOINT_ARGS usermod --non-unique --uid ${PUID:-${DEFAULT_UID}} ${PUSER} groupmod --non-unique --gid ${PGID:-${DEFAULT_GID}} ${PGROUP} +# Any directory named with the value of CONFIG_MAP_DIR will have its contents rsync'ed into +# the parent directory as the container starts up. This is mostly for convenience for +# Kubernetes configmap objects, which, because the directory into which they are +# copied is made read-only, doesn't play nicely if you're using it for configuration +# files which exist in a directory which may need to do read-write operations on other files. +# This works for nested subdirectories, but don't nest CONFIG_MAP_DIR directories +# inside of other CONFIG_MAP_DIR directories. +# +# TODO: else with cpio, tar, cp? + +if [[ -n ${CONFIG_MAP_DIR} ]] && command -v rsync >/dev/null 2>&1; then + find / -type d -name "${CONFIG_MAP_DIR}" -print -o -path /sys -prune -o -path /proc -prune 2>/dev/null | \ + awk '{print gsub("/","/"), $0}' | sort -n | cut -d' ' -f2- | \ + while read CMDIR; do + + rsync --recursive --mkpath \ + "--usermap=*:${PUID:-${DEFAULT_UID}}" \ + "--groupmap=*:${PGID:-${DEFAULT_GID}}" \ + --exclude="${CONFIG_MAP_DIR}"/ --exclude=.dockerignore --exclude=.gitignore \ + "${CMDIR}"/ "${CMDIR}"/../ + + # TODO - regarding ownership and permissions: + # + # I *think* what we want to do here is change the ownership of + # these configmap-copied files to be owned by the user specified by PUID + # (falling back to DEFAULT_UID) and PGID (falling back to DEFAULT_GID). + # The other option would be to preserve the ownership of the source + # fine with --owner --group, but I don't think that's what we want, as + # if we were doing this with a docker bind mount they'd likely have the + # permissions of the original user on the host, anyway, which is + # supposed to match up to PUID/PGID. + # + # For permissions, rsync says that "existing files retain their existing permissions" + # and "new files get their normal permission bits set to the source file's + # permissions masked with the receiving directory's default permissions" + # (either via umask or ACL) which I think is what we want. The other alternative + # would be to do something like --chmod=D2755,F644 + + done # loop over found CONFIG_MAP_DIR directories + CONFIG_MAP_FIND_PRUNE_ARGS=(-o -name "${CONFIG_MAP_DIR}" -prune) + +else + CONFIG_MAP_FIND_PRUNE_ARGS=() +fi # check for CONFIG_MAP_DIR and rsync + # change user/group ownership of any files/directories belonging to the original IDs +set +e if [[ -n ${PUID} ]] && [[ "${PUID}" != "${DEFAULT_UID}" ]]; then - find / -path /sys -prune -o -path /proc -prune -o -user ${DEFAULT_UID} -exec chown -f ${PUID} "{}" \; || true + find / -path /sys -prune -o -path /proc -prune -o -user ${DEFAULT_UID} -exec chown -f ${PUID} "{}" \; 2>/dev/null fi if [[ -n ${PGID} ]] && [[ "${PGID}" != "${DEFAULT_GID}" ]]; then - find / -path /sys -prune -o -path /proc -prune -o -group ${DEFAULT_GID} -exec chown -f :${PGID} "{}" \; || true + find / -path /sys -prune -o -path /proc -prune -o -group ${DEFAULT_GID} -exec chown -f :${PGID} "{}" \; 2>/dev/null fi # if there are semicolon-separated PUSER_CHOWN entries explicitly specified, chown them too if [[ -n ${PUSER_CHOWN} ]]; then IFS=';' read -ra ENTITIES <<< "${PUSER_CHOWN}" for ENTITY in "${ENTITIES[@]}"; do - chown -R ${PUSER}:${PGROUP} "${ENTITY}" || true + chown -R ${PUSER}:${PGROUP} "${ENTITY}" 2>/dev/null done fi @@ -32,7 +78,7 @@ if [[ -n ${PUSER_CA_TRUST} ]] && command -v openssl >/dev/null 2>&1; then if [[ -d "${PUSER_CA_TRUST}" ]]; then while read -r -d ''; do CA_FILES+=("$REPLY") - done < <(find "${PUSER_CA_TRUST}" -type f -size +31c -print0 2>/dev/null) + done < <(find "${PUSER_CA_TRUST}" -type f -size +31c -print0 "${CONFIG_MAP_FIND_PRUNE_ARGS[@]}" 2>/dev/null) elif [[ -f "${PUSER_CA_TRUST}" ]]; then CA_FILES+=("${PUSER_CA_TRUST}") fi @@ -71,15 +117,16 @@ if [[ -n ${PUSER_CA_TRUST} ]] && command -v openssl >/dev/null 2>&1; then CONCAT_FILE=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem fi fi - [[ -n "$DEST_FILE" ]] && ( cp "$CA_FILE" "$DEST_FILE" && chmod 644 "$DEST_FILE" ) || true + [[ -n "$DEST_FILE" ]] && ( cp "$CA_FILE" "$DEST_FILE" && chmod 644 "$DEST_FILE" ) [[ -n "$CONCAT_FILE" ]] && \ ( echo "" >> "$CONCAT_FILE" && \ echo "# $CA_NAME_ORIG" >> "$CONCAT_FILE" \ - && cat "$CA_FILE" >> "$CONCAT_FILE" ) || true + && cat "$CA_FILE" >> "$CONCAT_FILE" ) done - command -v update-ca-certificates >/dev/null 2>&1 && update-ca-certificates >/dev/null 2>&1 || true - command -v update-ca-trust >/dev/null 2>&1 && update-ca-trust extract >/dev/null 2>&1 || true + command -v update-ca-certificates >/dev/null 2>&1 && update-ca-certificates >/dev/null 2>&1 + command -v update-ca-trust >/dev/null 2>&1 && update-ca-trust extract >/dev/null 2>&1 fi +set -e # determine if we are now dropping privileges to exec ENTRYPOINT_CMD if [[ "$PUSER_PRIV_DROP" == "true" ]]; then From f7b7c018ff7ead23c1a1739f91e928bb5e216477 Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 16 Mar 2023 14:29:54 -0600 Subject: [PATCH 003/239] stubbed-out kubernetes file, commented-out nginx.conf file --- kubernetes/01-malcolm.yml | 115 +++++++++ nginx/nginx.conf | 524 +++++++++++++++++++------------------- 2 files changed, 378 insertions(+), 261 deletions(-) create mode 100644 kubernetes/01-malcolm.yml diff --git a/kubernetes/01-malcolm.yml b/kubernetes/01-malcolm.yml new file mode 100644 index 000000000..3aa7e11d7 --- /dev/null +++ b/kubernetes/01-malcolm.yml @@ -0,0 +1,115 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: nginx-ingress + namespace: malcolm + annotations: + kubernetes.io/ingress.class: "nginx" + nginx.ingress.kubernetes.io/backend-protocol: "https" + nginx.ingress.kubernetes.io/preserve-trailing-slash: "true" + nginx.ingress.kubernetes.io/ssl-passthrough: "false" +spec: + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: nginx-proxy + port: + number: 443 + +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx-proxy + namespace: malcolm +spec: + ports: + - port: 443 + protocol: TCP + selector: + app: malcolm-app + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: malcolm-app + namespace: malcolm +spec: + selector: + matchLabels: + app: malcolm-app + replicas: 1 + template: + metadata: + labels: + app: malcolm-app + spec: + containers: + - name: nginx-proxy-container + image: malcolmnetsec/nginx-proxy:23.05.0 + imagePullPolicy: Always + ports: + - containerPort: 443 + env: + - name: PUID + value: '1000' + - name: PGID + value: '1000' + - name: PUSER_CA_TRUST + value: '/var/local/ca-trust' + - name: NGINX_BASIC_AUTH + value: 'true' + - name: NGINX_LDAP_TLS_STUNNEL + value: 'false' + - name: NGINX_LDAP_TLS_STUNNEL_CHECK_HOST + value: '' + - name: NGINX_LDAP_TLS_STUNNEL_CHECK_IP + value: '' + - name: NGINX_LDAP_TLS_STUNNEL_VERIFY_LEVEL + value: '2' + - name: NGINX_SSL + value: 'true' + - name: NGINX_LOG_ACCESS_AND_ERRORS + value: 'false' + - name: CONFIG_MAP_DIR + value: 'configmap' + livenessProbe: + exec: + command: + - curl + - --insecure + - --silent + - https://localhost:443 + initialDelaySeconds: 120 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 + volumeMounts: + - name: etc-nginx-volume + mountPath: /etc/nginx/configmap + - name: var-local-catrust-volume + mountPath: /var/local/ca-trust/configmap + - name: etc-nginx-certs-volume + mountPath: /etc/nginx/certs/configmap + - name: etc-nginx-certs-pem-volume + mountPath: /etc/nginx/dhparam/configmap + volumes: + - name: etc-nginx-volume + configMap: + name: etc-nginx + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: etc-nginx-certs-volume + configMap: + name: etc-nginx-certs + - name: etc-nginx-certs-pem-volume + configMap: + name: etc-nginx-certs-pem \ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf index d73d9c868..e1c65df2e 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -29,61 +29,61 @@ http { # if LDAP authentication is enabled, this will configure the ldap_server section include /etc/nginx/nginx_ldap_rt.conf; - upstream arkime { - server arkime:8005; - } - - upstream api { - server api:5000; - } - - upstream upload { - server upload:80; - } - - upstream htadmin { - server htadmin:80; - } - - upstream dashboards { - server dashboards:5601; - } - - upstream dashboards-maps { - server dashboards-helper:28991; - } - - upstream opensearch { - server opensearch:9200; - } - - upstream logstash-stats { - server logstash:9600; - } - - upstream name-map-ui { - server name-map-ui:8080; - } - - upstream netbox { - server netbox:8080; - } - - upstream extracted-file-http-server { - server file-monitor:8440; - } +## upstream arkime { +## server arkime:8005; +## } +## +## upstream api { +## server api:5000; +## } +## +## upstream upload { +## server upload:80; +## } +## +## upstream htadmin { +## server htadmin:80; +## } +## +## upstream dashboards { +## server dashboards:5601; +## } +## +## upstream dashboards-maps { +## server dashboards-helper:28991; +## } +## +## upstream opensearch { +## server opensearch:9200; +## } +## +## upstream logstash-stats { +## server logstash:9600; +## } +## +## upstream name-map-ui { +## server name-map-ui:8080; +## } +## +## upstream netbox { +## server netbox:8080; +## } +## +## upstream extracted-file-http-server { +## server file-monitor:8440; +## } # htadmin (htpasswd/user management) - server { - listen 488; - include /etc/nginx/nginx_ssl_config.conf; - - location / { - proxy_pass http://htadmin; - proxy_redirect off; - proxy_set_header Host htadmin.malcolm.local; - } - } +## server { +## listen 488; +## include /etc/nginx/nginx_ssl_config.conf; +## +## location / { +## proxy_pass http://htadmin; +## proxy_redirect off; +## proxy_set_header Host htadmin.malcolm.local; +## } +## } # Main web interface server { @@ -99,222 +99,224 @@ http { try_files $uri $uri/index.html; } - # Malcolm file upload - location /upload { - proxy_http_version 1.1; - proxy_set_header Connection ""; - proxy_pass http://upload/; - proxy_redirect off; - proxy_set_header Host upload.malcolm.local; - proxy_request_buffering off; - proxy_buffering off; - client_max_body_size 50G; - } - location /server/php { - proxy_http_version 1.1; - proxy_set_header Connection ""; - proxy_pass http://upload/server/php/; - proxy_redirect off; - proxy_set_header Host upload.malcolm.local; - proxy_request_buffering off; - proxy_buffering off; - client_max_body_size 50G; - } - - # Logstash statistics - location ~* ^/logstash\b(.*) { - proxy_pass http://logstash-stats/_node/stats$1; - proxy_redirect off; - proxy_set_header Host arkime.malcolm.local; - } - - # Arkime -> Dashboards shortcut - location ~* ^/idark2dash(.*) { - - set $filter_start_time now-1d; - if ($arg_start != '') { - set $filter_start_time \'$arg_start\'; - } - - set $filter_stop_time now; - if ($arg_stop != '') { - set $filter_stop_time \'$arg_stop\'; - } - - set $filter_field undefined; - if ($arg_field != '') { - set $filter_field $arg_field; - } - - set $filter_value undefined; - if ($arg_value != '') { - set $filter_value $arg_value; - } - - rewrite ^/idark2dash/(.*) /dashboards/app/discover#/?_g=(refreshInterval:(pause:!t,value:0),time:(from:$filter_start_time,mode:absolute,to:$filter_stop_time))&_a=(columns:!(_source),filters:!((meta:(alias:!n,disabled:!f,index:'sessions2-*',key:$filter_field,negate:!f,params:(query:'$filter_value',type:phrase),type:phrase,value:'$filter_value'),query:(match:($filter_field:(query:'$filter_value',type:phrase))))),index:'sessions2-*',interval:auto,query:(language:lucene,query:''),sort:!(firstPacket,desc)) redirect; - proxy_pass http://dashboards; - proxy_redirect off; - proxy_set_header Host dashboards.malcolm.local; - } - - # Dashboards -> Arkime shortcut - location ~* /iddash2ark/(.*) { - rewrite ^.*/iddash2ark/(.*) /sessions?expression=($1) redirect; - proxy_pass https://arkime; - proxy_ssl_verify off; - proxy_redirect off; - proxy_set_header Host arkime.malcolm.local; - proxy_set_header http_auth_http_user $authenticated_user; - proxy_set_header Authorization ""; - } - - # Dashboards/Arkime -> extracted file download - location ~* /dl-extracted-files/(.*) { - rewrite ^.*/dl-extracted-files/(.*) /extracted-files/$1 redirect; - proxy_pass http://extracted-file-http-server; - proxy_redirect off; - proxy_set_header Host file-monitor.malcolm.local; - } - - # already prepended /dashboards to match the server.basePath in OpenSearch Dashboards's YML config file - location /dashboards { - proxy_pass http://dashboards; - proxy_redirect off; - proxy_set_header Host dashboards.malcolm.local; - } - - # offline region maps for dashboards - location /world.geojson { - proxy_pass http://dashboards-maps; - proxy_redirect off; - proxy_set_header Host dashboards-helper.malcolm.local; - } - - # name-map-ui (UI for mapping names to network hosts and subnets) - location /name-map-ui { - proxy_pass http://name-map-ui/; - proxy_redirect off; - proxy_set_header Host name-map-ui.malcolm.local; - proxy_cache off; - } - - location ~* ^/extracted-files\b(.*) { - proxy_pass http://extracted-file-http-server$1; - proxy_redirect off; - proxy_set_header Host file-monitor.malcolm.local; - } - - # netbox - location /netbox { - proxy_pass http://netbox; - proxy_redirect off; - proxy_set_header Host netbox.malcolm.local; - proxy_set_header X-Forwarded-Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Remote-Auth $authenticated_user; - } - - # favicon, logos, banners, etc. - include /etc/nginx/nginx_image_aliases.conf; - - # Fix cyberchef JS module(s) - # https://localhost/arkime/session/190924-KgO9H30qhdREw7ltsDXn1Rgp/modules/Regex.js - location ~* ^/arkime/session/.*/(modules/.*\.js) { - proxy_hide_header Content-Type; - proxy_set_header Content-Type "application/javascript"; - add_header Content-Type "application/javascript"; - default_type application/javascript; - add_header X-Content-Type-Options 'nosniff'; - proxy_pass https://arkime/cyberchef/$1; - proxy_ssl_verify off; - proxy_redirect off; - proxy_set_header Host arkime.malcolm.local; - proxy_set_header http_auth_http_user $authenticated_user; - proxy_set_header Authorization ""; - } - - # Malcolm API - location /mapi { - proxy_pass http://api/; - proxy_redirect off; - proxy_set_header Host api.malcolm.local; - } +## # Malcolm file upload +## location /upload { +## proxy_http_version 1.1; +## proxy_set_header Connection ""; +## proxy_pass http://upload/; +## proxy_redirect off; +## proxy_set_header Host upload.malcolm.local; +## proxy_request_buffering off; +## proxy_buffering off; +## client_max_body_size 50G; +## } +## location /server/php { +## proxy_http_version 1.1; +## proxy_set_header Connection ""; +## proxy_pass http://upload/server/php/; +## proxy_redirect off; +## proxy_set_header Host upload.malcolm.local; +## proxy_request_buffering off; +## proxy_buffering off; +## client_max_body_size 50G; +## } +## +## # Logstash statistics +## location ~* ^/logstash\b(.*) { +## proxy_pass http://logstash-stats/_node/stats$1; +## proxy_redirect off; +## proxy_set_header Host arkime.malcolm.local; +## } +## +## # Arkime -> Dashboards shortcut +## location ~* ^/idark2dash(.*) { +## +## set $filter_start_time now-1d; +## if ($arg_start != '') { +## set $filter_start_time \'$arg_start\'; +## } +## +## set $filter_stop_time now; +## if ($arg_stop != '') { +## set $filter_stop_time \'$arg_stop\'; +## } +## +## set $filter_field undefined; +## if ($arg_field != '') { +## set $filter_field $arg_field; +## } +## +## set $filter_value undefined; +## if ($arg_value != '') { +## set $filter_value $arg_value; +## } +## +## rewrite ^/idark2dash/(.*) /dashboards/app/discover#/?_g=(refreshInterval:(pause:!t,value:0),time:(from:$filter_start_time,mode:absolute,to:$filter_stop_time))&_a=(columns:!(_source),filters:!((meta:(alias:!n,disabled:!f,index:'sessions2-*',key:$filter_field,negate:!f,params:(query:'$filter_value',type:phrase),type:phrase,value:'$filter_value'),query:(match:($filter_field:(query:'$filter_value',type:phrase))))),index:'sessions2-*',interval:auto,query:(language:lucene,query:''),sort:!(firstPacket,desc)) redirect; +## proxy_pass http://dashboards; +## proxy_redirect off; +## proxy_set_header Host dashboards.malcolm.local; +## } +## +## # Dashboards -> Arkime shortcut +## location ~* /iddash2ark/(.*) { +## rewrite ^.*/iddash2ark/(.*) /sessions?expression=($1) redirect; +## proxy_pass https://arkime; +## proxy_ssl_verify off; +## proxy_redirect off; +## proxy_set_header Host arkime.malcolm.local; +## proxy_set_header http_auth_http_user $authenticated_user; +## proxy_set_header Authorization ""; +## } +## +## # Dashboards/Arkime -> extracted file download +## location ~* /dl-extracted-files/(.*) { +## rewrite ^.*/dl-extracted-files/(.*) /extracted-files/$1 redirect; +## proxy_pass http://extracted-file-http-server; +## proxy_redirect off; +## proxy_set_header Host file-monitor.malcolm.local; +## } +## +## # already prepended /dashboards to match the server.basePath in OpenSearch Dashboards's YML config file +## location /dashboards { +## proxy_pass http://dashboards; +## proxy_redirect off; +## proxy_set_header Host dashboards.malcolm.local; +## } +## +## # offline region maps for dashboards +## location /world.geojson { +## proxy_pass http://dashboards-maps; +## proxy_redirect off; +## proxy_set_header Host dashboards-helper.malcolm.local; +## } +## +## # name-map-ui (UI for mapping names to network hosts and subnets) +## location /name-map-ui { +## proxy_pass http://name-map-ui/; +## proxy_redirect off; +## proxy_set_header Host name-map-ui.malcolm.local; +## proxy_cache off; +## } +## +## location ~* ^/extracted-files\b(.*) { +## proxy_pass http://extracted-file-http-server$1; +## proxy_redirect off; +## proxy_set_header Host file-monitor.malcolm.local; +## } +## +## # netbox +## location /netbox { +## proxy_pass http://netbox; +## proxy_redirect off; +## proxy_set_header Host netbox.malcolm.local; +## proxy_set_header X-Forwarded-Host $http_host; +## proxy_set_header X-Real-IP $remote_addr; +## proxy_set_header X-Forwarded-Proto $scheme; +## proxy_set_header X-Remote-Auth $authenticated_user; +## } +## +## # favicon, logos, banners, etc. +## include /etc/nginx/nginx_image_aliases.conf; +## +## # Fix cyberchef JS module(s) +## # https://localhost/arkime/session/190924-KgO9H30qhdREw7ltsDXn1Rgp/modules/Regex.js +## location ~* ^/arkime/session/.*/(modules/.*\.js) { +## proxy_hide_header Content-Type; +## proxy_set_header Content-Type "application/javascript"; +## add_header Content-Type "application/javascript"; +## default_type application/javascript; +## add_header X-Content-Type-Options 'nosniff'; +## proxy_pass https://arkime/cyberchef/$1; +## proxy_ssl_verify off; +## proxy_redirect off; +## proxy_set_header Host arkime.malcolm.local; +## proxy_set_header http_auth_http_user $authenticated_user; +## proxy_set_header Authorization ""; +## } +## +## # Malcolm API +## location /mapi { +## proxy_pass http://api/; +## proxy_redirect off; +## proxy_set_header Host api.malcolm.local; +## } # Arkime location / { - proxy_pass https://arkime; - proxy_ssl_verify off; - proxy_redirect off; - proxy_set_header Host arkime.malcolm.local; - proxy_set_header http_auth_http_user $authenticated_user; - proxy_set_header Authorization ""; + default_type text/plain; + return 418 "418 I'm a teapot\n"; +## proxy_pass https://arkime; +## proxy_ssl_verify off; +## proxy_redirect off; +## proxy_set_header Host arkime.malcolm.local; +## proxy_set_header http_auth_http_user $authenticated_user; +## proxy_set_header Authorization ""; } } # OpenSearch dashboards interface - server { - listen 5601; - include /etc/nginx/nginx_ssl_config.conf; - - # use either auth_basic or auth_ldap - include /etc/nginx/nginx_auth_rt.conf; - - # favicon, logos, banners, etc. - include /etc/nginx/nginx_image_aliases.conf; - - # Dashboards -> Arkime shortcut - location ~* /iddash2ark/(.*) { - rewrite ^.*/iddash2ark/(.*) /sessions?expression=($1) redirect; - proxy_pass https://arkime; - proxy_ssl_verify off; - proxy_redirect off; - proxy_set_header Host arkime.malcolm.local; - proxy_set_header http_auth_http_user $authenticated_user; - proxy_set_header Authorization ""; - } - - # Dashboards -> extracted file download - location ~* /dl-extracted-files/(.*) { - rewrite ^.*/dl-extracted-files/(.*) /extracted-files/$1 redirect; - proxy_pass http://extracted-file-http-server; - proxy_redirect off; - proxy_set_header Host file-monitor.malcolm.local; - } - - # already prepended /dashboards to match the server.basePath in OpenSearch Dashboards' YML config file - location /dashboards { - proxy_pass http://dashboards; - proxy_redirect off; - proxy_set_header Host dashboards.malcolm.local; - } - - # otherwise prepend /dashboards to match the server.basePath in OpenSearch Dashboards' YML config file - location / { - rewrite ^/(.*) /dashboards/$1; - proxy_pass http://dashboards; - proxy_redirect off; - proxy_set_header Host dashboards.malcolm.local; - } - } +## server { +## listen 5601; +## include /etc/nginx/nginx_ssl_config.conf; +## +## # use either auth_basic or auth_ldap +## include /etc/nginx/nginx_auth_rt.conf; +## +## # favicon, logos, banners, etc. +## include /etc/nginx/nginx_image_aliases.conf; +## +## # Dashboards -> Arkime shortcut +## location ~* /iddash2ark/(.*) { +## rewrite ^.*/iddash2ark/(.*) /sessions?expression=($1) redirect; +## proxy_pass https://arkime; +## proxy_ssl_verify off; +## proxy_redirect off; +## proxy_set_header Host arkime.malcolm.local; +## proxy_set_header http_auth_http_user $authenticated_user; +## proxy_set_header Authorization ""; +## } +## +## # Dashboards -> extracted file download +## location ~* /dl-extracted-files/(.*) { +## rewrite ^.*/dl-extracted-files/(.*) /extracted-files/$1 redirect; +## proxy_pass http://extracted-file-http-server; +## proxy_redirect off; +## proxy_set_header Host file-monitor.malcolm.local; +## } +## +## # already prepended /dashboards to match the server.basePath in OpenSearch Dashboards' YML config file +## location /dashboards { +## proxy_pass http://dashboards; +## proxy_redirect off; +## proxy_set_header Host dashboards.malcolm.local; +## } +## +## # otherwise prepend /dashboards to match the server.basePath in OpenSearch Dashboards' YML config file +## location / { +## rewrite ^/(.*) /dashboards/$1; +## proxy_pass http://dashboards; +## proxy_redirect off; +## proxy_set_header Host dashboards.malcolm.local; +## } +## } # OpenSearch API - server { - listen 9200; - include /etc/nginx/nginx_ssl_config.conf; - - # use either auth_basic or auth_ldap - include /etc/nginx/nginx_auth_rt.conf; - - # favicon, logos, banners, etc. - include /etc/nginx/nginx_image_aliases.conf; - - location / { - proxy_pass http://opensearch; - proxy_redirect off; - proxy_set_header Host os.malcolm.local; - client_max_body_size 50m; - } - } +## server { +## listen 9200; +## include /etc/nginx/nginx_ssl_config.conf; +## +## # use either auth_basic or auth_ldap +## include /etc/nginx/nginx_auth_rt.conf; +## +## # favicon, logos, banners, etc. +## include /etc/nginx/nginx_image_aliases.conf; +## +## location / { +## proxy_pass http://opensearch; +## proxy_redirect off; +## proxy_set_header Host os.malcolm.local; +## client_max_body_size 50m; +## } +## } } From a54bfe818554981e44b01e97484c4aad59a0ee24 Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 16 Mar 2023 15:01:15 -0600 Subject: [PATCH 004/239] Roughing out stuff for k8s development --- .github/workflows/api-build-and-push-ghcr.yml | 1 + .../workflows/arkime-build-and-push-ghcr.yml | 1 + .../dashboards-build-and-push-ghcr.yml | 1 + .../dashboards-helper-build-and-push-ghcr.yml | 1 + .../file-monitor-build-and-push-ghcr.yml | 1 + .../file-upload-build-and-push-ghcr.yml | 1 + .../filebeat-build-and-push-ghcr.yml | 1 + .../workflows/freq-build-and-push-ghcr.yml | 1 + .../workflows/htadmin-build-and-push-ghcr.yml | 1 + .../logstash-build-and-push-ghcr.yml | 1 + ...alcolm-iso-build-docker-wrap-push-ghcr.yml | 1 + .../name-map-ui-build-and-push-ghcr.yml | 1 + .../workflows/netbox-build-and-push-ghcr.yml | 1 + .../workflows/nginx-build-and-push-ghcr.yml | 1 + .../opensearch-build-and-push-ghcr.yml | 1 + .../pcap-capture-build-and-push-ghcr.yml | 1 + .../pcap-monitor-build-and-push-ghcr.yml | 1 + .../postgresql-build-and-push-ghcr.yml | 1 + .../workflows/redis-build-and-push-ghcr.yml | 1 + ...sensor-iso-build-docker-wrap-push-ghcr.yml | 1 + .../suricata-build-and-push-ghcr.yml | 1 + .../workflows/zeek-build-and-push-ghcr.yml | 1 + docker-compose-standalone.yml | 46 +++++++++---------- docker-compose.yml | 46 +++++++++---------- docs/download.md | 4 +- docs/hedgehog-iso-build.md | 2 +- docs/malcolm-iso.md | 2 +- docs/quickstart.md | 40 ++++++++-------- docs/ubuntu-install-example.md | 40 ++++++++-------- kubernetes/01-malcolm.yml | 2 +- 30 files changed, 113 insertions(+), 91 deletions(-) diff --git a/.github/workflows/api-build-and-push-ghcr.yml b/.github/workflows/api-build-and-push-ghcr.yml index e786a1a81..bdf5c4f5d 100644 --- a/.github/workflows/api-build-and-push-ghcr.yml +++ b/.github/workflows/api-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'api/**' - 'Dockerfiles/api.Dockerfile' diff --git a/.github/workflows/arkime-build-and-push-ghcr.yml b/.github/workflows/arkime-build-and-push-ghcr.yml index 1c73661f4..242f0ed18 100644 --- a/.github/workflows/arkime-build-and-push-ghcr.yml +++ b/.github/workflows/arkime-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'arkime/**' - 'Dockerfiles/arkime.Dockerfile' diff --git a/.github/workflows/dashboards-build-and-push-ghcr.yml b/.github/workflows/dashboards-build-and-push-ghcr.yml index dacd0e6e2..686ca67a0 100644 --- a/.github/workflows/dashboards-build-and-push-ghcr.yml +++ b/.github/workflows/dashboards-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'dashboards/**' - 'Dockerfiles/dashboards.Dockerfile' diff --git a/.github/workflows/dashboards-helper-build-and-push-ghcr.yml b/.github/workflows/dashboards-helper-build-and-push-ghcr.yml index 96c3a1912..b89107083 100644 --- a/.github/workflows/dashboards-helper-build-and-push-ghcr.yml +++ b/.github/workflows/dashboards-helper-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'dashboards/**' - 'Dockerfiles/dashboards-helper.Dockerfile' diff --git a/.github/workflows/file-monitor-build-and-push-ghcr.yml b/.github/workflows/file-monitor-build-and-push-ghcr.yml index ae6b8b932..523e33909 100644 --- a/.github/workflows/file-monitor-build-and-push-ghcr.yml +++ b/.github/workflows/file-monitor-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'file-monitor/**' - 'Dockerfiles/file-monitor.Dockerfile' diff --git a/.github/workflows/file-upload-build-and-push-ghcr.yml b/.github/workflows/file-upload-build-and-push-ghcr.yml index 86d2a128b..bd5ceb9c5 100644 --- a/.github/workflows/file-upload-build-and-push-ghcr.yml +++ b/.github/workflows/file-upload-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'file-upload/**' - 'Dockerfiles/file-upload.Dockerfile' diff --git a/.github/workflows/filebeat-build-and-push-ghcr.yml b/.github/workflows/filebeat-build-and-push-ghcr.yml index 4784f7727..dd89bb33a 100644 --- a/.github/workflows/filebeat-build-and-push-ghcr.yml +++ b/.github/workflows/filebeat-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'filebeat/**' - 'Dockerfiles/filebeat.Dockerfile' diff --git a/.github/workflows/freq-build-and-push-ghcr.yml b/.github/workflows/freq-build-and-push-ghcr.yml index f498b34a0..fc1d0235d 100644 --- a/.github/workflows/freq-build-and-push-ghcr.yml +++ b/.github/workflows/freq-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'freq-server/**' - 'Dockerfiles/freq.Dockerfile' diff --git a/.github/workflows/htadmin-build-and-push-ghcr.yml b/.github/workflows/htadmin-build-and-push-ghcr.yml index 812740c8f..650001766 100644 --- a/.github/workflows/htadmin-build-and-push-ghcr.yml +++ b/.github/workflows/htadmin-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'htadmin/**' - 'Dockerfiles/htadmin.Dockerfile' diff --git a/.github/workflows/logstash-build-and-push-ghcr.yml b/.github/workflows/logstash-build-and-push-ghcr.yml index fe6a6c4f9..367ec5006 100644 --- a/.github/workflows/logstash-build-and-push-ghcr.yml +++ b/.github/workflows/logstash-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'logstash/**' - 'Dockerfiles/logstash.Dockerfile' diff --git a/.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml b/.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml index 599ea01f8..bd5b9a6d3 100644 --- a/.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml +++ b/.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'malcolm-iso/**' - 'shared/bin/*' diff --git a/.github/workflows/name-map-ui-build-and-push-ghcr.yml b/.github/workflows/name-map-ui-build-and-push-ghcr.yml index f433f7f35..e01ec9e59 100644 --- a/.github/workflows/name-map-ui-build-and-push-ghcr.yml +++ b/.github/workflows/name-map-ui-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'name-map-ui/**' - 'Dockerfiles/name-map-ui.Dockerfile' diff --git a/.github/workflows/netbox-build-and-push-ghcr.yml b/.github/workflows/netbox-build-and-push-ghcr.yml index a7fee896c..17c778e9b 100644 --- a/.github/workflows/netbox-build-and-push-ghcr.yml +++ b/.github/workflows/netbox-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'netbox/**' - 'Dockerfiles/netbox.Dockerfile' diff --git a/.github/workflows/nginx-build-and-push-ghcr.yml b/.github/workflows/nginx-build-and-push-ghcr.yml index 7301baccd..83dae26cf 100644 --- a/.github/workflows/nginx-build-and-push-ghcr.yml +++ b/.github/workflows/nginx-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'nginx/**' - 'Dockerfiles/nginx.Dockerfile' diff --git a/.github/workflows/opensearch-build-and-push-ghcr.yml b/.github/workflows/opensearch-build-and-push-ghcr.yml index b9de9d74f..4190e06b6 100644 --- a/.github/workflows/opensearch-build-and-push-ghcr.yml +++ b/.github/workflows/opensearch-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'Dockerfiles/opensearch.Dockerfile' - 'shared/bin/*' diff --git a/.github/workflows/pcap-capture-build-and-push-ghcr.yml b/.github/workflows/pcap-capture-build-and-push-ghcr.yml index 07bd3944b..e1b095f96 100644 --- a/.github/workflows/pcap-capture-build-and-push-ghcr.yml +++ b/.github/workflows/pcap-capture-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'pcap-capture/**' - 'Dockerfiles/pcap-capture.Dockerfile' diff --git a/.github/workflows/pcap-monitor-build-and-push-ghcr.yml b/.github/workflows/pcap-monitor-build-and-push-ghcr.yml index f4c52be24..dd9350a21 100644 --- a/.github/workflows/pcap-monitor-build-and-push-ghcr.yml +++ b/.github/workflows/pcap-monitor-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'pcap-monitor/**' - 'Dockerfiles/pcap-monitor.Dockerfile' diff --git a/.github/workflows/postgresql-build-and-push-ghcr.yml b/.github/workflows/postgresql-build-and-push-ghcr.yml index 3f78d13b5..3b497c74c 100644 --- a/.github/workflows/postgresql-build-and-push-ghcr.yml +++ b/.github/workflows/postgresql-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'Dockerfiles/postgresql.Dockerfile' - 'shared/bin/*' diff --git a/.github/workflows/redis-build-and-push-ghcr.yml b/.github/workflows/redis-build-and-push-ghcr.yml index af0b312cf..6bcaab345 100644 --- a/.github/workflows/redis-build-and-push-ghcr.yml +++ b/.github/workflows/redis-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'Dockerfiles/redis.Dockerfile' - 'shared/bin/*' diff --git a/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml b/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml index 78cbe5b2c..f08e61988 100644 --- a/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml +++ b/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'sensor-iso/**' - 'shared/bin/*' diff --git a/.github/workflows/suricata-build-and-push-ghcr.yml b/.github/workflows/suricata-build-and-push-ghcr.yml index 9fb1f70af..15b450eb4 100644 --- a/.github/workflows/suricata-build-and-push-ghcr.yml +++ b/.github/workflows/suricata-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'suricata/**' - 'Dockerfiles/suricata.Dockerfile' diff --git a/.github/workflows/zeek-build-and-push-ghcr.yml b/.github/workflows/zeek-build-and-push-ghcr.yml index 4447f7171..4ed3706ec 100644 --- a/.github/workflows/zeek-build-and-push-ghcr.yml +++ b/.github/workflows/zeek-build-and-push-ghcr.yml @@ -5,6 +5,7 @@ on: branches: - main - development + - kubernetes paths: - 'zeek/**' - 'Dockerfiles/zeek.Dockerfile' diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 9daed7ec3..c751a5c44 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -359,7 +359,7 @@ x-pcap-capture-variables: &pcap-capture-variables services: opensearch: - image: malcolmnetsec/opensearch:23.03.0 + image: malcolmnetsec/opensearch:kubernetes restart: "no" stdin_open: false tty: true @@ -400,7 +400,7 @@ services: retries: 3 start_period: 180s dashboards-helper: - image: malcolmnetsec/dashboards-helper:23.03.0 + image: malcolmnetsec/dashboards-helper:kubernetes restart: "no" stdin_open: false tty: true @@ -431,7 +431,7 @@ services: retries: 3 start_period: 30s dashboards: - image: malcolmnetsec/dashboards:23.03.0 + image: malcolmnetsec/dashboards:kubernetes restart: "no" stdin_open: false tty: true @@ -456,7 +456,7 @@ services: retries: 3 start_period: 210s logstash: - image: malcolmnetsec/logstash-oss:23.03.0 + image: malcolmnetsec/logstash-oss:kubernetes restart: "no" stdin_open: false tty: true @@ -499,7 +499,7 @@ services: retries: 3 start_period: 600s filebeat: - image: malcolmnetsec/filebeat-oss:23.03.0 + image: malcolmnetsec/filebeat-oss:kubernetes restart: "no" stdin_open: false tty: true @@ -538,7 +538,7 @@ services: retries: 3 start_period: 60s arkime: - image: malcolmnetsec/arkime:23.03.0 + image: malcolmnetsec/arkime:kubernetes restart: "no" stdin_open: false tty: true @@ -576,7 +576,7 @@ services: retries: 3 start_period: 210s zeek: - image: malcolmnetsec/zeek:23.03.0 + image: malcolmnetsec/zeek:kubernetes restart: "no" stdin_open: false tty: true @@ -615,7 +615,7 @@ services: retries: 3 start_period: 60s zeek-live: - image: malcolmnetsec/zeek:23.03.0 + image: malcolmnetsec/zeek:kubernetes restart: "no" stdin_open: false tty: true @@ -647,7 +647,7 @@ services: - ./zeek-logs/extract_files:/zeek/extract_files - ./zeek/intel:/opt/zeek/share/zeek/site/intel suricata: - image: malcolmnetsec/suricata:23.03.0 + image: malcolmnetsec/suricata:kubernetes restart: "no" stdin_open: false tty: true @@ -684,7 +684,7 @@ services: retries: 3 start_period: 120s suricata-live: - image: malcolmnetsec/suricata:23.03.0 + image: malcolmnetsec/suricata:kubernetes restart: "no" stdin_open: false tty: true @@ -711,7 +711,7 @@ services: - ./suricata-logs:/var/log/suricata - ./suricata/rules:/opt/suricata/rules:ro file-monitor: - image: malcolmnetsec/file-monitor:23.03.0 + image: malcolmnetsec/file-monitor:kubernetes restart: "no" stdin_open: false tty: true @@ -735,7 +735,7 @@ services: retries: 3 start_period: 60s pcap-capture: - image: malcolmnetsec/pcap-capture:23.03.0 + image: malcolmnetsec/pcap-capture:kubernetes restart: "no" stdin_open: false tty: true @@ -757,7 +757,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/pcap pcap-monitor: - image: malcolmnetsec/pcap-monitor:23.03.0 + image: malcolmnetsec/pcap-monitor:kubernetes restart: "no" stdin_open: false tty: true @@ -783,7 +783,7 @@ services: retries: 3 start_period: 90s upload: - image: malcolmnetsec/file-upload:23.03.0 + image: malcolmnetsec/file-upload:kubernetes restart: "no" stdin_open: false tty: true @@ -811,7 +811,7 @@ services: retries: 3 start_period: 60s htadmin: - image: malcolmnetsec/htadmin:23.03.0 + image: malcolmnetsec/htadmin:kubernetes restart: "no" stdin_open: false tty: true @@ -835,7 +835,7 @@ services: retries: 3 start_period: 60s freq: - image: malcolmnetsec/freq:23.03.0 + image: malcolmnetsec/freq:kubernetes restart: "no" stdin_open: false tty: true @@ -856,7 +856,7 @@ services: retries: 3 start_period: 60s name-map-ui: - image: malcolmnetsec/name-map-ui:23.03.0 + image: malcolmnetsec/name-map-ui:kubernetes restart: "no" stdin_open: false tty: true @@ -877,7 +877,7 @@ services: retries: 3 start_period: 60s netbox: - image: malcolmnetsec/netbox:23.03.0 + image: malcolmnetsec/netbox:kubernetes restart: "no" stdin_open: false tty: true @@ -908,7 +908,7 @@ services: retries: 3 start_period: 120s netbox-postgres: - image: malcolmnetsec/postgresql:23.03.0 + image: malcolmnetsec/postgresql:kubernetes restart: "no" stdin_open: false tty: true @@ -931,7 +931,7 @@ services: retries: 3 start_period: 45s netbox-redis: - image: malcolmnetsec/redis:23.03.0 + image: malcolmnetsec/redis:kubernetes restart: "no" stdin_open: false tty: true @@ -958,7 +958,7 @@ services: retries: 3 start_period: 45s netbox-redis-cache: - image: malcolmnetsec/redis:23.03.0 + image: malcolmnetsec/redis:kubernetes restart: "no" stdin_open: false tty: true @@ -984,7 +984,7 @@ services: retries: 3 start_period: 45s api: - image: malcolmnetsec/api:23.03.0 + image: malcolmnetsec/api:kubernetes command: gunicorn --bind 0:5000 manage:app restart: "no" stdin_open: false @@ -1007,7 +1007,7 @@ services: retries: 3 start_period: 60s nginx-proxy: - image: malcolmnetsec/nginx-proxy:23.03.0 + image: malcolmnetsec/nginx-proxy:kubernetes restart: "no" stdin_open: false tty: true diff --git a/docker-compose.yml b/docker-compose.yml index 1e5e01b3b..6e9ef58bd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -362,7 +362,7 @@ services: build: context: . dockerfile: Dockerfiles/opensearch.Dockerfile - image: malcolmnetsec/opensearch:23.03.0 + image: malcolmnetsec/opensearch:kubernetes restart: "no" stdin_open: false tty: true @@ -406,7 +406,7 @@ services: build: context: . dockerfile: Dockerfiles/dashboards-helper.Dockerfile - image: malcolmnetsec/dashboards-helper:23.03.0 + image: malcolmnetsec/dashboards-helper:kubernetes restart: "no" stdin_open: false tty: true @@ -440,7 +440,7 @@ services: build: context: . dockerfile: Dockerfiles/dashboards.Dockerfile - image: malcolmnetsec/dashboards:23.03.0 + image: malcolmnetsec/dashboards:kubernetes restart: "no" stdin_open: false tty: true @@ -468,7 +468,7 @@ services: build: context: . dockerfile: Dockerfiles/logstash.Dockerfile - image: malcolmnetsec/logstash-oss:23.03.0 + image: malcolmnetsec/logstash-oss:kubernetes restart: "no" stdin_open: false tty: true @@ -518,7 +518,7 @@ services: build: context: . dockerfile: Dockerfiles/filebeat.Dockerfile - image: malcolmnetsec/filebeat-oss:23.03.0 + image: malcolmnetsec/filebeat-oss:kubernetes restart: "no" stdin_open: false tty: true @@ -560,7 +560,7 @@ services: build: context: . dockerfile: Dockerfiles/arkime.Dockerfile - image: malcolmnetsec/arkime:23.03.0 + image: malcolmnetsec/arkime:kubernetes restart: "no" stdin_open: false tty: true @@ -604,7 +604,7 @@ services: build: context: . dockerfile: Dockerfiles/zeek.Dockerfile - image: malcolmnetsec/zeek:23.03.0 + image: malcolmnetsec/zeek:kubernetes restart: "no" stdin_open: false tty: true @@ -647,7 +647,7 @@ services: build: context: . dockerfile: Dockerfiles/zeek.Dockerfile - image: malcolmnetsec/zeek:23.03.0 + image: malcolmnetsec/zeek:kubernetes restart: "no" stdin_open: false tty: true @@ -683,7 +683,7 @@ services: build: context: . dockerfile: Dockerfiles/suricata.Dockerfile - image: malcolmnetsec/suricata:23.03.0 + image: malcolmnetsec/suricata:kubernetes restart: "no" stdin_open: false tty: true @@ -723,7 +723,7 @@ services: build: context: . dockerfile: Dockerfiles/suricata.Dockerfile - image: malcolmnetsec/suricata:23.03.0 + image: malcolmnetsec/suricata:kubernetes restart: "no" stdin_open: false tty: true @@ -753,7 +753,7 @@ services: build: context: . dockerfile: Dockerfiles/file-monitor.Dockerfile - image: malcolmnetsec/file-monitor:23.03.0 + image: malcolmnetsec/file-monitor:kubernetes restart: "no" stdin_open: false tty: true @@ -780,7 +780,7 @@ services: build: context: . dockerfile: Dockerfiles/pcap-capture.Dockerfile - image: malcolmnetsec/pcap-capture:23.03.0 + image: malcolmnetsec/pcap-capture:kubernetes restart: "no" stdin_open: false tty: true @@ -805,7 +805,7 @@ services: build: context: . dockerfile: Dockerfiles/pcap-monitor.Dockerfile - image: malcolmnetsec/pcap-monitor:23.03.0 + image: malcolmnetsec/pcap-monitor:kubernetes restart: "no" stdin_open: false tty: true @@ -834,7 +834,7 @@ services: build: context: . dockerfile: Dockerfiles/file-upload.Dockerfile - image: malcolmnetsec/file-upload:23.03.0 + image: malcolmnetsec/file-upload:kubernetes restart: "no" stdin_open: false tty: true @@ -862,7 +862,7 @@ services: retries: 3 start_period: 60s htadmin: - image: malcolmnetsec/htadmin:23.03.0 + image: malcolmnetsec/htadmin:kubernetes build: context: . dockerfile: Dockerfiles/htadmin.Dockerfile @@ -889,7 +889,7 @@ services: retries: 3 start_period: 60s freq: - image: malcolmnetsec/freq:23.03.0 + image: malcolmnetsec/freq:kubernetes build: context: . dockerfile: Dockerfiles/freq.Dockerfile @@ -913,7 +913,7 @@ services: retries: 3 start_period: 60s name-map-ui: - image: malcolmnetsec/name-map-ui:23.03.0 + image: malcolmnetsec/name-map-ui:kubernetes build: context: . dockerfile: Dockerfiles/name-map-ui.Dockerfile @@ -937,7 +937,7 @@ services: retries: 3 start_period: 60s netbox: - image: malcolmnetsec/netbox:23.03.0 + image: malcolmnetsec/netbox:kubernetes build: context: . dockerfile: Dockerfiles/netbox.Dockerfile @@ -972,7 +972,7 @@ services: retries: 3 start_period: 120s netbox-postgres: - image: malcolmnetsec/postgresql:23.03.0 + image: malcolmnetsec/postgresql:kubernetes build: context: . dockerfile: Dockerfiles/postgresql.Dockerfile @@ -998,7 +998,7 @@ services: retries: 3 start_period: 45s netbox-redis: - image: malcolmnetsec/redis:23.03.0 + image: malcolmnetsec/redis:kubernetes build: context: . dockerfile: Dockerfiles/redis.Dockerfile @@ -1028,7 +1028,7 @@ services: retries: 3 start_period: 45s netbox-redis-cache: - image: malcolmnetsec/redis:23.03.0 + image: malcolmnetsec/redis:kubernetes build: context: . dockerfile: Dockerfiles/redis.Dockerfile @@ -1057,7 +1057,7 @@ services: retries: 3 start_period: 45s api: - image: malcolmnetsec/api:23.03.0 + image: malcolmnetsec/api:kubernetes build: context: . dockerfile: Dockerfiles/api.Dockerfile @@ -1086,7 +1086,7 @@ services: build: context: . dockerfile: Dockerfiles/nginx.Dockerfile - image: malcolmnetsec/nginx-proxy:23.03.0 + image: malcolmnetsec/nginx-proxy:kubernetes restart: "no" stdin_open: false tty: true diff --git a/docs/download.md b/docs/download.md index f0ae7097e..b0f4ddb0a 100644 --- a/docs/download.md +++ b/docs/download.md @@ -16,7 +16,7 @@ While official downloads of the Malcolm installer ISO are not provided, an **uno | ISO | SHA256 | |---|---| -| [malcolm-23.03.0.iso](/iso/malcolm-23.03.0.iso) (5.3GiB) | [`9459fb0ce61fba8c7a9a9457b24d42182519dbb62247111471e38f8c190113eb`](/iso/malcolm-23.03.0.iso.sha256.txt) | +| [malcolm-kubernetes.iso](/iso/malcolm-kubernetes.iso) (5.3GiB) | [`9459fb0ce61fba8c7a9a9457b24d42182519dbb62247111471e38f8c190113eb`](/iso/malcolm-kubernetes.iso.sha256.txt) | ## Hedgehog Linux @@ -26,7 +26,7 @@ While official downloads of the Malcolm installer ISO are not provided, an **uno | ISO | SHA256 | |---|---| -| [hedgehog-23.03.0.iso](/iso/hedgehog-23.03.0.iso) (2.3GiB) | [`3cdba91e417f6ada83130aabc3be38dd0a8b12b6bda227859a546ace198680bc`](/iso/hedgehog-23.03.0.iso.sha256.txt) | +| [hedgehog-kubernetes.iso](/iso/hedgehog-kubernetes.iso) (2.3GiB) | [`3cdba91e417f6ada83130aabc3be38dd0a8b12b6bda227859a546ace198680bc`](/iso/hedgehog-kubernetes.iso.sha256.txt) | ## Warning diff --git a/docs/hedgehog-iso-build.md b/docs/hedgehog-iso-build.md index c37ed0b23..bd3c790d6 100644 --- a/docs/hedgehog-iso-build.md +++ b/docs/hedgehog-iso-build.md @@ -29,7 +29,7 @@ Building the ISO may take 90 minutes or more depending on your system. As the bu ``` … -Finished, created "/sensor-build/hedgehog-23.03.0.iso" +Finished, created "/sensor-build/hedgehog-kubernetes.iso" … ``` diff --git a/docs/malcolm-iso.md b/docs/malcolm-iso.md index edeb32810..d45f91a0b 100644 --- a/docs/malcolm-iso.md +++ b/docs/malcolm-iso.md @@ -41,7 +41,7 @@ Building the ISO may take 30 minutes or more depending on your system. As the bu ``` … -Finished, created "/malcolm-build/malcolm-iso/malcolm-23.03.0.iso" +Finished, created "/malcolm-build/malcolm-iso/malcolm-kubernetes.iso" … ``` diff --git a/docs/quickstart.md b/docs/quickstart.md index 01dbc0a35..609da2ac3 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -53,26 +53,26 @@ You can then observe that the images have been retrieved by running `docker imag ``` $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE -malcolmnetsec/api 23.03.0 xxxxxxxxxxxx 3 days ago 158MB -malcolmnetsec/arkime 23.03.0 xxxxxxxxxxxx 3 days ago 816MB -malcolmnetsec/dashboards 23.03.0 xxxxxxxxxxxx 3 days ago 1.02GB -malcolmnetsec/dashboards-helper 23.03.0 xxxxxxxxxxxx 3 days ago 184MB -malcolmnetsec/file-monitor 23.03.0 xxxxxxxxxxxx 3 days ago 588MB -malcolmnetsec/file-upload 23.03.0 xxxxxxxxxxxx 3 days ago 259MB -malcolmnetsec/filebeat-oss 23.03.0 xxxxxxxxxxxx 3 days ago 624MB -malcolmnetsec/freq 23.03.0 xxxxxxxxxxxx 3 days ago 132MB -malcolmnetsec/htadmin 23.03.0 xxxxxxxxxxxx 3 days ago 242MB -malcolmnetsec/logstash-oss 23.03.0 xxxxxxxxxxxx 3 days ago 1.35GB -malcolmnetsec/name-map-ui 23.03.0 xxxxxxxxxxxx 3 days ago 143MB -malcolmnetsec/netbox 23.03.0 xxxxxxxxxxxx 3 days ago 1.01GB -malcolmnetsec/nginx-proxy 23.03.0 xxxxxxxxxxxx 3 days ago 121MB -malcolmnetsec/opensearch 23.03.0 xxxxxxxxxxxx 3 days ago 1.17GB -malcolmnetsec/pcap-capture 23.03.0 xxxxxxxxxxxx 3 days ago 121MB -malcolmnetsec/pcap-monitor 23.03.0 xxxxxxxxxxxx 3 days ago 213MB -malcolmnetsec/postgresql 23.03.0 xxxxxxxxxxxx 3 days ago 268MB -malcolmnetsec/redis 23.03.0 xxxxxxxxxxxx 3 days ago 34.2MB -malcolmnetsec/suricata 23.03.0 xxxxxxxxxxxx 3 days ago 278MB -malcolmnetsec/zeek 23.03.0 xxxxxxxxxxxx 3 days ago 1GB +malcolmnetsec/api kubernetes xxxxxxxxxxxx 3 days ago 158MB +malcolmnetsec/arkime kubernetes xxxxxxxxxxxx 3 days ago 816MB +malcolmnetsec/dashboards kubernetes xxxxxxxxxxxx 3 days ago 1.02GB +malcolmnetsec/dashboards-helper kubernetes xxxxxxxxxxxx 3 days ago 184MB +malcolmnetsec/file-monitor kubernetes xxxxxxxxxxxx 3 days ago 588MB +malcolmnetsec/file-upload kubernetes xxxxxxxxxxxx 3 days ago 259MB +malcolmnetsec/filebeat-oss kubernetes xxxxxxxxxxxx 3 days ago 624MB +malcolmnetsec/freq kubernetes xxxxxxxxxxxx 3 days ago 132MB +malcolmnetsec/htadmin kubernetes xxxxxxxxxxxx 3 days ago 242MB +malcolmnetsec/logstash-oss kubernetes xxxxxxxxxxxx 3 days ago 1.35GB +malcolmnetsec/name-map-ui kubernetes xxxxxxxxxxxx 3 days ago 143MB +malcolmnetsec/netbox kubernetes xxxxxxxxxxxx 3 days ago 1.01GB +malcolmnetsec/nginx-proxy kubernetes xxxxxxxxxxxx 3 days ago 121MB +malcolmnetsec/opensearch kubernetes xxxxxxxxxxxx 3 days ago 1.17GB +malcolmnetsec/pcap-capture kubernetes xxxxxxxxxxxx 3 days ago 121MB +malcolmnetsec/pcap-monitor kubernetes xxxxxxxxxxxx 3 days ago 213MB +malcolmnetsec/postgresql kubernetes xxxxxxxxxxxx 3 days ago 268MB +malcolmnetsec/redis kubernetes xxxxxxxxxxxx 3 days ago 34.2MB +malcolmnetsec/suricata kubernetes xxxxxxxxxxxx 3 days ago 278MB +malcolmnetsec/zeek kubernetes xxxxxxxxxxxx 3 days ago 1GB ``` ### Import from pre-packaged tarballs diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index 61785dcde..7767523c4 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -261,26 +261,26 @@ Pulling zeek ... done user@host:~/Malcolm$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE -malcolmnetsec/api 23.03.0 xxxxxxxxxxxx 3 days ago 158MB -malcolmnetsec/arkime 23.03.0 xxxxxxxxxxxx 3 days ago 816MB -malcolmnetsec/dashboards 23.03.0 xxxxxxxxxxxx 3 days ago 1.02GB -malcolmnetsec/dashboards-helper 23.03.0 xxxxxxxxxxxx 3 days ago 184MB -malcolmnetsec/file-monitor 23.03.0 xxxxxxxxxxxx 3 days ago 588MB -malcolmnetsec/file-upload 23.03.0 xxxxxxxxxxxx 3 days ago 259MB -malcolmnetsec/filebeat-oss 23.03.0 xxxxxxxxxxxx 3 days ago 624MB -malcolmnetsec/freq 23.03.0 xxxxxxxxxxxx 3 days ago 132MB -malcolmnetsec/htadmin 23.03.0 xxxxxxxxxxxx 3 days ago 242MB -malcolmnetsec/logstash-oss 23.03.0 xxxxxxxxxxxx 3 days ago 1.35GB -malcolmnetsec/name-map-ui 23.03.0 xxxxxxxxxxxx 3 days ago 143MB -malcolmnetsec/netbox 23.03.0 xxxxxxxxxxxx 3 days ago 1.01GB -malcolmnetsec/nginx-proxy 23.03.0 xxxxxxxxxxxx 3 days ago 121MB -malcolmnetsec/opensearch 23.03.0 xxxxxxxxxxxx 3 days ago 1.17GB -malcolmnetsec/pcap-capture 23.03.0 xxxxxxxxxxxx 3 days ago 121MB -malcolmnetsec/pcap-monitor 23.03.0 xxxxxxxxxxxx 3 days ago 213MB -malcolmnetsec/postgresql 23.03.0 xxxxxxxxxxxx 3 days ago 268MB -malcolmnetsec/redis 23.03.0 xxxxxxxxxxxx 3 days ago 34.2MB -malcolmnetsec/suricata 23.03.0 xxxxxxxxxxxx 3 days ago 278MB -malcolmnetsec/zeek 23.03.0 xxxxxxxxxxxx 3 days ago 1GB +malcolmnetsec/api kubernetes xxxxxxxxxxxx 3 days ago 158MB +malcolmnetsec/arkime kubernetes xxxxxxxxxxxx 3 days ago 816MB +malcolmnetsec/dashboards kubernetes xxxxxxxxxxxx 3 days ago 1.02GB +malcolmnetsec/dashboards-helper kubernetes xxxxxxxxxxxx 3 days ago 184MB +malcolmnetsec/file-monitor kubernetes xxxxxxxxxxxx 3 days ago 588MB +malcolmnetsec/file-upload kubernetes xxxxxxxxxxxx 3 days ago 259MB +malcolmnetsec/filebeat-oss kubernetes xxxxxxxxxxxx 3 days ago 624MB +malcolmnetsec/freq kubernetes xxxxxxxxxxxx 3 days ago 132MB +malcolmnetsec/htadmin kubernetes xxxxxxxxxxxx 3 days ago 242MB +malcolmnetsec/logstash-oss kubernetes xxxxxxxxxxxx 3 days ago 1.35GB +malcolmnetsec/name-map-ui kubernetes xxxxxxxxxxxx 3 days ago 143MB +malcolmnetsec/netbox kubernetes xxxxxxxxxxxx 3 days ago 1.01GB +malcolmnetsec/nginx-proxy kubernetes xxxxxxxxxxxx 3 days ago 121MB +malcolmnetsec/opensearch kubernetes xxxxxxxxxxxx 3 days ago 1.17GB +malcolmnetsec/pcap-capture kubernetes xxxxxxxxxxxx 3 days ago 121MB +malcolmnetsec/pcap-monitor kubernetes xxxxxxxxxxxx 3 days ago 213MB +malcolmnetsec/postgresql kubernetes xxxxxxxxxxxx 3 days ago 268MB +malcolmnetsec/redis kubernetes xxxxxxxxxxxx 3 days ago 34.2MB +malcolmnetsec/suricata kubernetes xxxxxxxxxxxx 3 days ago 278MB +malcolmnetsec/zeek kubernetes xxxxxxxxxxxx 3 days ago 1GB ``` Finally, we can start Malcolm. When Malcolm starts it will stream informational and debug messages to the console. If you wish, you can safely close the console or use `Ctrl+C` to stop these messages; Malcolm will continue running in the background. diff --git a/kubernetes/01-malcolm.yml b/kubernetes/01-malcolm.yml index 3aa7e11d7..f6700120f 100644 --- a/kubernetes/01-malcolm.yml +++ b/kubernetes/01-malcolm.yml @@ -52,7 +52,7 @@ spec: spec: containers: - name: nginx-proxy-container - image: malcolmnetsec/nginx-proxy:23.05.0 + image: malcolmnetsec/nginx-proxy:kubernetes imagePullPolicy: Always ports: - containerPort: 443 From f742c82879d919a81bccc521670f2aa49179746b Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 16 Mar 2023 15:10:48 -0600 Subject: [PATCH 005/239] reference correct github image --- docker-compose-standalone.yml | 46 +++++++++++++++++------------------ docker-compose.yml | 46 +++++++++++++++++------------------ kubernetes/01-malcolm.yml | 2 +- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index c751a5c44..a79f5df2a 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -359,7 +359,7 @@ x-pcap-capture-variables: &pcap-capture-variables services: opensearch: - image: malcolmnetsec/opensearch:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/opensearch:kubernetes restart: "no" stdin_open: false tty: true @@ -400,7 +400,7 @@ services: retries: 3 start_period: 180s dashboards-helper: - image: malcolmnetsec/dashboards-helper:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/dashboards-helper:kubernetes restart: "no" stdin_open: false tty: true @@ -431,7 +431,7 @@ services: retries: 3 start_period: 30s dashboards: - image: malcolmnetsec/dashboards:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/dashboards:kubernetes restart: "no" stdin_open: false tty: true @@ -456,7 +456,7 @@ services: retries: 3 start_period: 210s logstash: - image: malcolmnetsec/logstash-oss:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/logstash-oss:kubernetes restart: "no" stdin_open: false tty: true @@ -499,7 +499,7 @@ services: retries: 3 start_period: 600s filebeat: - image: malcolmnetsec/filebeat-oss:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/filebeat-oss:kubernetes restart: "no" stdin_open: false tty: true @@ -538,7 +538,7 @@ services: retries: 3 start_period: 60s arkime: - image: malcolmnetsec/arkime:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/arkime:kubernetes restart: "no" stdin_open: false tty: true @@ -576,7 +576,7 @@ services: retries: 3 start_period: 210s zeek: - image: malcolmnetsec/zeek:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/zeek:kubernetes restart: "no" stdin_open: false tty: true @@ -615,7 +615,7 @@ services: retries: 3 start_period: 60s zeek-live: - image: malcolmnetsec/zeek:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/zeek:kubernetes restart: "no" stdin_open: false tty: true @@ -647,7 +647,7 @@ services: - ./zeek-logs/extract_files:/zeek/extract_files - ./zeek/intel:/opt/zeek/share/zeek/site/intel suricata: - image: malcolmnetsec/suricata:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/suricata:kubernetes restart: "no" stdin_open: false tty: true @@ -684,7 +684,7 @@ services: retries: 3 start_period: 120s suricata-live: - image: malcolmnetsec/suricata:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/suricata:kubernetes restart: "no" stdin_open: false tty: true @@ -711,7 +711,7 @@ services: - ./suricata-logs:/var/log/suricata - ./suricata/rules:/opt/suricata/rules:ro file-monitor: - image: malcolmnetsec/file-monitor:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/file-monitor:kubernetes restart: "no" stdin_open: false tty: true @@ -735,7 +735,7 @@ services: retries: 3 start_period: 60s pcap-capture: - image: malcolmnetsec/pcap-capture:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/pcap-capture:kubernetes restart: "no" stdin_open: false tty: true @@ -757,7 +757,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/pcap pcap-monitor: - image: malcolmnetsec/pcap-monitor:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/pcap-monitor:kubernetes restart: "no" stdin_open: false tty: true @@ -783,7 +783,7 @@ services: retries: 3 start_period: 90s upload: - image: malcolmnetsec/file-upload:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/file-upload:kubernetes restart: "no" stdin_open: false tty: true @@ -811,7 +811,7 @@ services: retries: 3 start_period: 60s htadmin: - image: malcolmnetsec/htadmin:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/htadmin:kubernetes restart: "no" stdin_open: false tty: true @@ -835,7 +835,7 @@ services: retries: 3 start_period: 60s freq: - image: malcolmnetsec/freq:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/freq:kubernetes restart: "no" stdin_open: false tty: true @@ -856,7 +856,7 @@ services: retries: 3 start_period: 60s name-map-ui: - image: malcolmnetsec/name-map-ui:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/name-map-ui:kubernetes restart: "no" stdin_open: false tty: true @@ -877,7 +877,7 @@ services: retries: 3 start_period: 60s netbox: - image: malcolmnetsec/netbox:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/netbox:kubernetes restart: "no" stdin_open: false tty: true @@ -908,7 +908,7 @@ services: retries: 3 start_period: 120s netbox-postgres: - image: malcolmnetsec/postgresql:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/postgresql:kubernetes restart: "no" stdin_open: false tty: true @@ -931,7 +931,7 @@ services: retries: 3 start_period: 45s netbox-redis: - image: malcolmnetsec/redis:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/redis:kubernetes restart: "no" stdin_open: false tty: true @@ -958,7 +958,7 @@ services: retries: 3 start_period: 45s netbox-redis-cache: - image: malcolmnetsec/redis:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/redis:kubernetes restart: "no" stdin_open: false tty: true @@ -984,7 +984,7 @@ services: retries: 3 start_period: 45s api: - image: malcolmnetsec/api:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/api:kubernetes command: gunicorn --bind 0:5000 manage:app restart: "no" stdin_open: false @@ -1007,7 +1007,7 @@ services: retries: 3 start_period: 60s nginx-proxy: - image: malcolmnetsec/nginx-proxy:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/nginx-proxy:kubernetes restart: "no" stdin_open: false tty: true diff --git a/docker-compose.yml b/docker-compose.yml index 6e9ef58bd..27e410aaa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -362,7 +362,7 @@ services: build: context: . dockerfile: Dockerfiles/opensearch.Dockerfile - image: malcolmnetsec/opensearch:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/opensearch:kubernetes restart: "no" stdin_open: false tty: true @@ -406,7 +406,7 @@ services: build: context: . dockerfile: Dockerfiles/dashboards-helper.Dockerfile - image: malcolmnetsec/dashboards-helper:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/dashboards-helper:kubernetes restart: "no" stdin_open: false tty: true @@ -440,7 +440,7 @@ services: build: context: . dockerfile: Dockerfiles/dashboards.Dockerfile - image: malcolmnetsec/dashboards:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/dashboards:kubernetes restart: "no" stdin_open: false tty: true @@ -468,7 +468,7 @@ services: build: context: . dockerfile: Dockerfiles/logstash.Dockerfile - image: malcolmnetsec/logstash-oss:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/logstash-oss:kubernetes restart: "no" stdin_open: false tty: true @@ -518,7 +518,7 @@ services: build: context: . dockerfile: Dockerfiles/filebeat.Dockerfile - image: malcolmnetsec/filebeat-oss:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/filebeat-oss:kubernetes restart: "no" stdin_open: false tty: true @@ -560,7 +560,7 @@ services: build: context: . dockerfile: Dockerfiles/arkime.Dockerfile - image: malcolmnetsec/arkime:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/arkime:kubernetes restart: "no" stdin_open: false tty: true @@ -604,7 +604,7 @@ services: build: context: . dockerfile: Dockerfiles/zeek.Dockerfile - image: malcolmnetsec/zeek:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/zeek:kubernetes restart: "no" stdin_open: false tty: true @@ -647,7 +647,7 @@ services: build: context: . dockerfile: Dockerfiles/zeek.Dockerfile - image: malcolmnetsec/zeek:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/zeek:kubernetes restart: "no" stdin_open: false tty: true @@ -683,7 +683,7 @@ services: build: context: . dockerfile: Dockerfiles/suricata.Dockerfile - image: malcolmnetsec/suricata:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/suricata:kubernetes restart: "no" stdin_open: false tty: true @@ -723,7 +723,7 @@ services: build: context: . dockerfile: Dockerfiles/suricata.Dockerfile - image: malcolmnetsec/suricata:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/suricata:kubernetes restart: "no" stdin_open: false tty: true @@ -753,7 +753,7 @@ services: build: context: . dockerfile: Dockerfiles/file-monitor.Dockerfile - image: malcolmnetsec/file-monitor:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/file-monitor:kubernetes restart: "no" stdin_open: false tty: true @@ -780,7 +780,7 @@ services: build: context: . dockerfile: Dockerfiles/pcap-capture.Dockerfile - image: malcolmnetsec/pcap-capture:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/pcap-capture:kubernetes restart: "no" stdin_open: false tty: true @@ -805,7 +805,7 @@ services: build: context: . dockerfile: Dockerfiles/pcap-monitor.Dockerfile - image: malcolmnetsec/pcap-monitor:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/pcap-monitor:kubernetes restart: "no" stdin_open: false tty: true @@ -834,7 +834,7 @@ services: build: context: . dockerfile: Dockerfiles/file-upload.Dockerfile - image: malcolmnetsec/file-upload:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/file-upload:kubernetes restart: "no" stdin_open: false tty: true @@ -862,7 +862,7 @@ services: retries: 3 start_period: 60s htadmin: - image: malcolmnetsec/htadmin:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/htadmin:kubernetes build: context: . dockerfile: Dockerfiles/htadmin.Dockerfile @@ -889,7 +889,7 @@ services: retries: 3 start_period: 60s freq: - image: malcolmnetsec/freq:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/freq:kubernetes build: context: . dockerfile: Dockerfiles/freq.Dockerfile @@ -913,7 +913,7 @@ services: retries: 3 start_period: 60s name-map-ui: - image: malcolmnetsec/name-map-ui:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/name-map-ui:kubernetes build: context: . dockerfile: Dockerfiles/name-map-ui.Dockerfile @@ -937,7 +937,7 @@ services: retries: 3 start_period: 60s netbox: - image: malcolmnetsec/netbox:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/netbox:kubernetes build: context: . dockerfile: Dockerfiles/netbox.Dockerfile @@ -972,7 +972,7 @@ services: retries: 3 start_period: 120s netbox-postgres: - image: malcolmnetsec/postgresql:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/postgresql:kubernetes build: context: . dockerfile: Dockerfiles/postgresql.Dockerfile @@ -998,7 +998,7 @@ services: retries: 3 start_period: 45s netbox-redis: - image: malcolmnetsec/redis:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/redis:kubernetes build: context: . dockerfile: Dockerfiles/redis.Dockerfile @@ -1028,7 +1028,7 @@ services: retries: 3 start_period: 45s netbox-redis-cache: - image: malcolmnetsec/redis:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/redis:kubernetes build: context: . dockerfile: Dockerfiles/redis.Dockerfile @@ -1057,7 +1057,7 @@ services: retries: 3 start_period: 45s api: - image: malcolmnetsec/api:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/api:kubernetes build: context: . dockerfile: Dockerfiles/api.Dockerfile @@ -1086,7 +1086,7 @@ services: build: context: . dockerfile: Dockerfiles/nginx.Dockerfile - image: malcolmnetsec/nginx-proxy:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/nginx-proxy:kubernetes restart: "no" stdin_open: false tty: true diff --git a/kubernetes/01-malcolm.yml b/kubernetes/01-malcolm.yml index f6700120f..250b25529 100644 --- a/kubernetes/01-malcolm.yml +++ b/kubernetes/01-malcolm.yml @@ -52,7 +52,7 @@ spec: spec: containers: - name: nginx-proxy-container - image: malcolmnetsec/nginx-proxy:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/nginx-proxy:kubernetes imagePullPolicy: Always ports: - containerPort: 443 From 6ed02db417b44c092cfc2b172baa6c5fd9abc594 Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 16 Mar 2023 15:14:41 -0600 Subject: [PATCH 006/239] trigger branch build --- .trigger_workflow_build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.trigger_workflow_build b/.trigger_workflow_build index c69b966fd..b75e0fb2a 100644 --- a/.trigger_workflow_build +++ b/.trigger_workflow_build @@ -1,2 +1,2 @@ # this file exists solely for the purpose of being updated and seen by github to trigger a commit build action -2 +3 From 41eabac6989dc0aff019a857a1aee7845b9f525b Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:33:03 -0600 Subject: [PATCH 007/239] Add freq cont --- kubernetes/01-malcolm.yml | 145 +++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 57 deletions(-) diff --git a/kubernetes/01-malcolm.yml b/kubernetes/01-malcolm.yml index 3aa7e11d7..f1baa25be 100644 --- a/kubernetes/01-malcolm.yml +++ b/kubernetes/01-malcolm.yml @@ -2,25 +2,24 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: nginx-ingress - namespace: malcolm annotations: - kubernetes.io/ingress.class: "nginx" - nginx.ingress.kubernetes.io/backend-protocol: "https" + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/backend-protocol: https nginx.ingress.kubernetes.io/preserve-trailing-slash: "true" nginx.ingress.kubernetes.io/ssl-passthrough: "false" + name: nginx-ingress + namespace: malcolm spec: rules: - http: paths: - - path: / - pathType: Prefix - backend: + - backend: service: name: nginx-proxy port: number: 443 - + path: / + pathType: Prefix --- apiVersion: v1 kind: Service @@ -29,11 +28,21 @@ metadata: namespace: malcolm spec: ports: - - port: 443 - protocol: TCP + - port: 443 selector: app: malcolm-app - +--- +apiVersion: v1 +kind: Service +metadata: + name: freq + namespace: malcolm +spec: + ports: + - port: 10004 + selector: + app: malcolm-app + name: freq-container --- apiVersion: apps/v1 kind: Deployment @@ -41,44 +50,63 @@ metadata: name: malcolm-app namespace: malcolm spec: + replicas: 1 selector: matchLabels: app: malcolm-app - replicas: 1 template: metadata: labels: app: malcolm-app spec: containers: - - name: nginx-proxy-container - image: malcolmnetsec/nginx-proxy:23.05.0 + - env: + - name: PUID + value: "1000" + - name: PGID + value: "1000" + - name: PUSER_CA_TRUST + value: /var/local/ca-trust + - name: VIRTUAL_HOST + value: freq.malcolm.local + - name: FREQ_LOOKUP + value: "true" + - name: FREQ_SEVERITY_THRESHOLD + value: "2.0" + - name: TOTAL_MEGABYTES_SEVERITY_THRESHOLD + value: "1000" + - name: CONNECTION_SECONDS_SEVERITY_THRESHOLD + value: "3600" + - name: SENSITIVE_COUNTRY_CODES + value: AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ + image: ghcr.io/idaholab/malcolmnetsec/freq:kubernetes imagePullPolicy: Always + name: freq-container ports: - - containerPort: 443 - env: - - name: PUID - value: '1000' - - name: PGID - value: '1000' - - name: PUSER_CA_TRUST - value: '/var/local/ca-trust' - - name: NGINX_BASIC_AUTH - value: 'true' - - name: NGINX_LDAP_TLS_STUNNEL - value: 'false' - - name: NGINX_LDAP_TLS_STUNNEL_CHECK_HOST - value: '' - - name: NGINX_LDAP_TLS_STUNNEL_CHECK_IP - value: '' - - name: NGINX_LDAP_TLS_STUNNEL_VERIFY_LEVEL - value: '2' - - name: NGINX_SSL - value: 'true' - - name: NGINX_LOG_ACCESS_AND_ERRORS - value: 'false' - - name: CONFIG_MAP_DIR - value: 'configmap' + - containerPort: 10004 + - env: + - name: PUID + value: "1000" + - name: PGID + value: "1000" + - name: PUSER_CA_TRUST + value: /var/local/ca-trust + - name: NGINX_BASIC_AUTH + value: "true" + - name: NGINX_LDAP_TLS_STUNNEL + value: "false" + - name: NGINX_LDAP_TLS_STUNNEL_CHECK_HOST + - name: NGINX_LDAP_TLS_STUNNEL_CHECK_IP + - name: NGINX_LDAP_TLS_STUNNEL_VERIFY_LEVEL + value: "2" + - name: NGINX_SSL + value: "true" + - name: NGINX_LOG_ACCESS_AND_ERRORS + value: "false" + - name: CONFIG_MAP_DIR + value: configmap + image: malcolmnetsec/nginx-proxy:kubernetes + imagePullPolicy: Always livenessProbe: exec: command: @@ -86,30 +114,33 @@ spec: - --insecure - --silent - https://localhost:443 + failureThreshold: 10 initialDelaySeconds: 120 periodSeconds: 30 - timeoutSeconds: 15 successThreshold: 1 - failureThreshold: 10 + timeoutSeconds: 15 + name: nginx-proxy-container + ports: + - containerPort: 443 volumeMounts: - - name: etc-nginx-volume - mountPath: /etc/nginx/configmap - - name: var-local-catrust-volume - mountPath: /var/local/ca-trust/configmap - - name: etc-nginx-certs-volume - mountPath: /etc/nginx/certs/configmap - - name: etc-nginx-certs-pem-volume - mountPath: /etc/nginx/dhparam/configmap + - mountPath: /etc/nginx/configmap + name: etc-nginx-volume + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: /etc/nginx/certs/configmap + name: etc-nginx-certs-volume + - mountPath: /etc/nginx/dhparam/configmap + name: etc-nginx-certs-pem-volume volumes: - - name: etc-nginx-volume - configMap: + - configMap: name: etc-nginx - - name: var-local-catrust-volume - configMap: + name: etc-nginx-volume + - configMap: name: var-local-catrust - - name: etc-nginx-certs-volume - configMap: + name: var-local-catrust-volume + - configMap: name: etc-nginx-certs - - name: etc-nginx-certs-pem-volume - configMap: - name: etc-nginx-certs-pem \ No newline at end of file + name: etc-nginx-certs-volume + - configMap: + name: etc-nginx-certs-pem + name: etc-nginx-certs-pem-volume From 3397289eca339d0d84e6adf5fff4e25f38c2dd96 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:38:27 -0600 Subject: [PATCH 008/239] Add container --- kubernetes/01-malcolm.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kubernetes/01-malcolm.yml b/kubernetes/01-malcolm.yml index 79ed37207..30aefa1f8 100644 --- a/kubernetes/01-malcolm.yml +++ b/kubernetes/01-malcolm.yml @@ -60,7 +60,8 @@ spec: app: malcolm-app spec: containers: -<<<<<<< HEAD + - name: freq-container + image: ghcr.io/idaholab/malcolmnetsec/nginx-proxy:kubernetes - env: - name: PUID value: "1000" @@ -81,10 +82,8 @@ spec: - name: SENSITIVE_COUNTRY_CODES value: AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ image: ghcr.io/idaholab/malcolmnetsec/freq:kubernetes -======= - name: nginx-proxy-container image: ghcr.io/idaholab/malcolmnetsec/nginx-proxy:kubernetes ->>>>>>> becaaa2d32c13210d43033e3c2f7af86324b37c5 imagePullPolicy: Always name: freq-container ports: From 6dae32132f0cbf4fc3b5840960be2d2764f02e9c Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 16 Mar 2023 15:46:24 -0600 Subject: [PATCH 009/239] do the rsync correction --- shared/bin/docker-uid-gid-setup.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/bin/docker-uid-gid-setup.sh b/shared/bin/docker-uid-gid-setup.sh index d6375569f..be3cfe384 100755 --- a/shared/bin/docker-uid-gid-setup.sh +++ b/shared/bin/docker-uid-gid-setup.sh @@ -25,10 +25,10 @@ if [[ -n ${CONFIG_MAP_DIR} ]] && command -v rsync >/dev/null 2>&1; then awk '{print gsub("/","/"), $0}' | sort -n | cut -d' ' -f2- | \ while read CMDIR; do - rsync --recursive --mkpath \ + rsync --recursive --mkpath --copy-links \ "--usermap=*:${PUID:-${DEFAULT_UID}}" \ "--groupmap=*:${PGID:-${DEFAULT_GID}}" \ - --exclude="${CONFIG_MAP_DIR}"/ --exclude=.dockerignore --exclude=.gitignore \ + --exclude='..*' --exclude="${CONFIG_MAP_DIR}"/ --exclude=.dockerignore --exclude=.gitignore \ "${CMDIR}"/ "${CMDIR}"/../ # TODO - regarding ownership and permissions: From e2cb739c58addd9e551327a7e623bbb69092ab94 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:51:17 -0600 Subject: [PATCH 010/239] Try fix git --- kubernetes/01-malcolm.yml | 157 +++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 77 deletions(-) diff --git a/kubernetes/01-malcolm.yml b/kubernetes/01-malcolm.yml index 30aefa1f8..d1c31a502 100644 --- a/kubernetes/01-malcolm.yml +++ b/kubernetes/01-malcolm.yml @@ -2,24 +2,25 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: + name: nginx-ingress + namespace: malcolm annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/backend-protocol: https + kubernetes.io/ingress.class: "nginx" + nginx.ingress.kubernetes.io/backend-protocol: "https" nginx.ingress.kubernetes.io/preserve-trailing-slash: "true" nginx.ingress.kubernetes.io/ssl-passthrough: "false" - name: nginx-ingress - namespace: malcolm spec: rules: - http: paths: - - backend: + - path: / + pathType: Prefix + backend: service: name: nginx-proxy port: number: 443 - path: / - pathType: Prefix + --- apiVersion: v1 kind: Service @@ -28,9 +29,11 @@ metadata: namespace: malcolm spec: ports: - - port: 443 + - port: 443 + protocol: TCP selector: app: malcolm-app + --- apiVersion: v1 kind: Service @@ -39,10 +42,12 @@ metadata: namespace: malcolm spec: ports: - - port: 10004 + - port: 10004 + protocol: TCP selector: app: malcolm-app name: freq-container + --- apiVersion: apps/v1 kind: Deployment @@ -50,10 +55,10 @@ metadata: name: malcolm-app namespace: malcolm spec: - replicas: 1 selector: matchLabels: app: malcolm-app + replicas: 1 template: metadata: labels: @@ -61,56 +66,57 @@ spec: spec: containers: - name: freq-container - image: ghcr.io/idaholab/malcolmnetsec/nginx-proxy:kubernetes - - env: - - name: PUID - value: "1000" - - name: PGID - value: "1000" - - name: PUSER_CA_TRUST - value: /var/local/ca-trust - - name: VIRTUAL_HOST - value: freq.malcolm.local - - name: FREQ_LOOKUP - value: "true" - - name: FREQ_SEVERITY_THRESHOLD - value: "2.0" - - name: TOTAL_MEGABYTES_SEVERITY_THRESHOLD - value: "1000" - - name: CONNECTION_SECONDS_SEVERITY_THRESHOLD - value: "3600" - - name: SENSITIVE_COUNTRY_CODES - value: AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ image: ghcr.io/idaholab/malcolmnetsec/freq:kubernetes - - name: nginx-proxy-container - image: ghcr.io/idaholab/malcolmnetsec/nginx-proxy:kubernetes imagePullPolicy: Always - name: freq-container ports: - - containerPort: 10004 - - env: - - name: PUID - value: "1000" - - name: PGID - value: "1000" - - name: PUSER_CA_TRUST - value: /var/local/ca-trust - - name: NGINX_BASIC_AUTH - value: "true" - - name: NGINX_LDAP_TLS_STUNNEL - value: "false" - - name: NGINX_LDAP_TLS_STUNNEL_CHECK_HOST - - name: NGINX_LDAP_TLS_STUNNEL_CHECK_IP - - name: NGINX_LDAP_TLS_STUNNEL_VERIFY_LEVEL - value: "2" - - name: NGINX_SSL - value: "true" - - name: NGINX_LOG_ACCESS_AND_ERRORS - value: "false" - - name: CONFIG_MAP_DIR - value: configmap + - containerPort: 10004 + env: + - name: PUID + value: '1000' + - name: PGID + value: '1000' + - name: PUSER_CA_TRUST + value: '/var/local/ca-trust' + - name: VIRTUAL_HOST + value: 'freq.malcolm.local' + - name: FREQ_LOOKUP + value: 'true' + - name: FREQ_SEVERITY_THRESHOLD + value: '2.0' + - name: TOTAL_MEGABYTES_SEVERITY_THRESHOLD + value: 1000 + - name: CONNECTION_SECONDS_SEVERITY_THRESHOLD + value: 3600 + - name: SENSITIVE_COUNTRY_CODES + value: 'AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ' + - name: nginx-proxy-container image: malcolmnetsec/nginx-proxy:kubernetes imagePullPolicy: Always + ports: + - containerPort: 443 + env: + - name: PUID + value: '1000' + - name: PGID + value: '1000' + - name: PUSER_CA_TRUST + value: '/var/local/ca-trust' + - name: NGINX_BASIC_AUTH + value: 'true' + - name: NGINX_LDAP_TLS_STUNNEL + value: 'false' + - name: NGINX_LDAP_TLS_STUNNEL_CHECK_HOST + value: '' + - name: NGINX_LDAP_TLS_STUNNEL_CHECK_IP + value: '' + - name: NGINX_LDAP_TLS_STUNNEL_VERIFY_LEVEL + value: '2' + - name: NGINX_SSL + value: 'true' + - name: NGINX_LOG_ACCESS_AND_ERRORS + value: 'false' + - name: CONFIG_MAP_DIR + value: 'configmap' livenessProbe: exec: command: @@ -118,33 +124,30 @@ spec: - --insecure - --silent - https://localhost:443 - failureThreshold: 10 initialDelaySeconds: 120 periodSeconds: 30 - successThreshold: 1 timeoutSeconds: 15 - name: nginx-proxy-container - ports: - - containerPort: 443 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - - mountPath: /etc/nginx/configmap - name: etc-nginx-volume - - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume - - mountPath: /etc/nginx/certs/configmap - name: etc-nginx-certs-volume - - mountPath: /etc/nginx/dhparam/configmap - name: etc-nginx-certs-pem-volume + - name: etc-nginx-volume + mountPath: /etc/nginx/configmap + - name: var-local-catrust-volume + mountPath: /var/local/ca-trust/configmap + - name: etc-nginx-certs-volume + mountPath: /etc/nginx/certs/configmap + - name: etc-nginx-certs-pem-volume + mountPath: /etc/nginx/dhparam/configmap volumes: - - configMap: + - name: etc-nginx-volume + configMap: name: etc-nginx - name: etc-nginx-volume - - configMap: + - name: var-local-catrust-volume + configMap: name: var-local-catrust - name: var-local-catrust-volume - - configMap: + - name: etc-nginx-certs-volume + configMap: name: etc-nginx-certs - name: etc-nginx-certs-volume - - configMap: - name: etc-nginx-certs-pem - name: etc-nginx-certs-pem-volume + - name: etc-nginx-certs-pem-volume + configMap: + name: etc-nginx-certs-pem \ No newline at end of file From d8e3276897011a60b0c5c347b76757bfd26d9d15 Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 16 Mar 2023 15:56:15 -0600 Subject: [PATCH 011/239] fix docker image name in nginx and put environment vars in quotes --- kubernetes/01-malcolm.yml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/kubernetes/01-malcolm.yml b/kubernetes/01-malcolm.yml index d1c31a502..3e34995e5 100644 --- a/kubernetes/01-malcolm.yml +++ b/kubernetes/01-malcolm.yml @@ -47,7 +47,7 @@ spec: selector: app: malcolm-app name: freq-container - + --- apiVersion: apps/v1 kind: Deployment @@ -71,6 +71,8 @@ spec: ports: - containerPort: 10004 env: + - name: CONFIG_MAP_DIR + value: 'configmap' - name: PUID value: '1000' - name: PGID @@ -79,18 +81,18 @@ spec: value: '/var/local/ca-trust' - name: VIRTUAL_HOST value: 'freq.malcolm.local' - - name: FREQ_LOOKUP + - name: FREQ_LOOKUP value: 'true' - - name: FREQ_SEVERITY_THRESHOLD + - name: FREQ_SEVERITY_THRESHOLD value: '2.0' - - name: TOTAL_MEGABYTES_SEVERITY_THRESHOLD - value: 1000 - - name: CONNECTION_SECONDS_SEVERITY_THRESHOLD - value: 3600 - - name: SENSITIVE_COUNTRY_CODES + - name: TOTAL_MEGABYTES_SEVERITY_THRESHOLD + value: '1000' + - name: CONNECTION_SECONDS_SEVERITY_THRESHOLD + value: '3600' + - name: SENSITIVE_COUNTRY_CODES value: 'AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ' - name: nginx-proxy-container - image: malcolmnetsec/nginx-proxy:kubernetes + image: ghcr.io/idaholab/malcolmnetsec/nginx-proxy:kubernetes imagePullPolicy: Always ports: - containerPort: 443 From d317d43046d4aa78d538b1feac2584e497ef0b72 Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 16 Mar 2023 16:15:54 -0600 Subject: [PATCH 012/239] set FREQ_PORT (will remove later when we change env. name) and stdin/tty --- kubernetes/01-malcolm.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kubernetes/01-malcolm.yml b/kubernetes/01-malcolm.yml index 3e34995e5..16936299a 100644 --- a/kubernetes/01-malcolm.yml +++ b/kubernetes/01-malcolm.yml @@ -68,6 +68,8 @@ spec: - name: freq-container image: ghcr.io/idaholab/malcolmnetsec/freq:kubernetes imagePullPolicy: Always + stdin: false + tty: true ports: - containerPort: 10004 env: @@ -83,6 +85,8 @@ spec: value: 'freq.malcolm.local' - name: FREQ_LOOKUP value: 'true' + - name: FREQ_PORT + value: '10004' - name: FREQ_SEVERITY_THRESHOLD value: '2.0' - name: TOTAL_MEGABYTES_SEVERITY_THRESHOLD @@ -94,6 +98,8 @@ spec: - name: nginx-proxy-container image: ghcr.io/idaholab/malcolmnetsec/nginx-proxy:kubernetes imagePullPolicy: Always + stdin: false + tty: true ports: - containerPort: 443 env: From 74d5a7083a13c395f1c268e1abde33bba8bd3f8f Mon Sep 17 00:00:00 2001 From: SG Date: Fri, 17 Mar 2023 11:25:44 -0600 Subject: [PATCH 013/239] rename FREQ_PORT to FREQ_API_PORT --- Dockerfiles/freq.Dockerfile | 8 ++++---- freq-server/supervisord.conf | 2 +- kubernetes/01-malcolm.yml | 2 -- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Dockerfiles/freq.Dockerfile b/Dockerfiles/freq.Dockerfile index 8d9d4d7d8..9a03efed6 100644 --- a/Dockerfiles/freq.Dockerfile +++ b/Dockerfiles/freq.Dockerfile @@ -21,11 +21,11 @@ ENV PUSER_PRIV_DROP true ENV DEBIAN_FRONTEND noninteractive ENV TERM xterm -ARG FREQ_PORT=10004 +ARG FREQ_API_PORT=10004 ARG FREQ_LOOKUP=true -ENV FREQ_PORT $FREQ_PORT -ENV FREQ_LOOKUP $FREQ_LOOKUP +ENV FREQ_API_PORT $FREQ_API_PORT +ENV FREQ_LOOKUP $FREQ_LOOKUP ENV FREQ_URL "https://codeload.github.com/markbaggett/freq/tar.gz/master" @@ -60,7 +60,7 @@ ADD freq-server/supervisord.conf /etc/supervisord.conf WORKDIR /opt/freq_server -EXPOSE $FREQ_PORT +EXPOSE $FREQ_API_PORT ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] diff --git a/freq-server/supervisord.conf b/freq-server/supervisord.conf index ab6de5e9a..e6db46e37 100644 --- a/freq-server/supervisord.conf +++ b/freq-server/supervisord.conf @@ -17,7 +17,7 @@ supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface serverurl=unix:///tmp/supervisor.sock [program:freq] -command=/usr/bin/python3 /opt/freq_server/freq_server.py -ip 0.0.0.0 %(ENV_FREQ_PORT)s /opt/freq_server/freq_table.freq +command=/usr/bin/python3 /opt/freq_server/freq_server.py -ip 0.0.0.0 %(ENV_FREQ_API_PORT)s /opt/freq_server/freq_table.freq startsecs=5 startretries=2000000000 stopasgroup=true diff --git a/kubernetes/01-malcolm.yml b/kubernetes/01-malcolm.yml index 16936299a..ecec55f1b 100644 --- a/kubernetes/01-malcolm.yml +++ b/kubernetes/01-malcolm.yml @@ -85,8 +85,6 @@ spec: value: 'freq.malcolm.local' - name: FREQ_LOOKUP value: 'true' - - name: FREQ_PORT - value: '10004' - name: FREQ_SEVERITY_THRESHOLD value: '2.0' - name: TOTAL_MEGABYTES_SEVERITY_THRESHOLD From c0a1f5a5afbfda403718965be05214887916908e Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 17 Mar 2023 14:28:05 -0600 Subject: [PATCH 014/239] added order of operations for services --- kubernetes/services-dev-plan.md | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 kubernetes/services-dev-plan.md diff --git a/kubernetes/services-dev-plan.md b/kubernetes/services-dev-plan.md new file mode 100644 index 000000000..47b82f108 --- /dev/null +++ b/kubernetes/services-dev-plan.md @@ -0,0 +1,35 @@ +## Services for which Kubernetes manifests need to be developed + +See **support Malcolm deployment with Kubernetes** [idaholab/Malcolm#149](https://github.com/idaholab/Malcolm/issues/149) + +"Core services" are listed earlier in the list (i.e., we should probably approach the services roughly in this order). + +* nginx-proxy +* opensearch + - This is more complicated if we do OpenSearch in the container vs. [a remote instance](https://idaholab.github.io/Malcolm/docs/opensearch-instances.html#OpenSearchInstance). My recommendation is to do early development of this container with a remote instance (see the corresponding [`docker-compose.yml` section](https://github.com/idaholab/Malcolm/blob/0c14303f242ce1bae7e48b30ca7234c996930008/docker-compose-standalone.yml#L46-L68)) and then come back to it. We can still do the service/container with the `OPENSEARCH_LOCAL` variable set to `false`, which will cause the [`service_check_passthrough.sh`](https://github.com/idaholab/Malcolm/blob/main/shared/bin/service_check_passthrough.sh) script to get run at startup instead of the actual OpenSearch service. Note that [this requires](https://idaholab.github.io/Malcolm/docs/opensearch-instances.html#OpenSearchAuth) [LDAP authentication](https://idaholab.github.io/Malcolm/docs/authsetup.html#AuthLDAP), or to use basic authentication but ensure the accounts and passwords match on Malcolm and the remote OpenSearch instance. +* dashboards +* upload +* pcap-monitor +* arkime +* api +* dashboards-helper +* zeek +* suricata +* file-monitor +* filebeat +* freq + - This one really doesn't have many dependencies on other services, so it could be done whenever. +* logstash +* name-map-ui +* netbox-redis +* netbox-redis-cache +* netbox-postgres +* netbox +* htadmin + - Leaving this for towards the end since if we're using remote OpenSearch we'd be using LDAP anyway, and even if not we can use auth_setup to set a username/password. At the point this is done we'll no longer be able to use `configmap` for `nginx/htpasswd` (and `htadmin/config.ini`) as they'll need to be read/write. +* pcap-capture + - Not sure what live capture looks like at all in this scenario: what would the capture interface be? So leaving this to the very end. +* zeek-live + - See note for `pcap-capture`. +* suricata-live + - See note for `pcap-capture`. \ No newline at end of file From d068ae7f4e3a46c97d890a0be417ea9a82e4c281 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 20 Mar 2023 09:49:37 -0600 Subject: [PATCH 015/239] added service check passthrough --- Dockerfiles/api.Dockerfile | 11 ++++++++--- Dockerfiles/arkime.Dockerfile | 9 +++++++-- Dockerfiles/dashboards-helper.Dockerfile | 14 +++++++++----- Dockerfiles/dashboards.Dockerfile | 10 ++++++++-- Dockerfiles/file-monitor.Dockerfile | 9 +++++++-- Dockerfiles/file-upload.Dockerfile | 11 ++++++++--- Dockerfiles/filebeat.Dockerfile | 8 ++++++-- Dockerfiles/freq.Dockerfile | 8 ++++++-- Dockerfiles/htadmin.Dockerfile | 8 ++++++-- Dockerfiles/logstash.Dockerfile | 12 ++++++++---- Dockerfiles/name-map-ui.Dockerfile | 8 ++++++-- Dockerfiles/netbox.Dockerfile | 7 +++++-- Dockerfiles/nginx.Dockerfile | 7 +++++-- Dockerfiles/opensearch.Dockerfile | 13 ++++++++----- Dockerfiles/pcap-capture.Dockerfile | 10 +++++++--- Dockerfiles/pcap-monitor.Dockerfile | 8 ++++++-- Dockerfiles/postgresql.Dockerfile | 7 +++++-- Dockerfiles/redis.Dockerfile | 7 +++++-- Dockerfiles/suricata.Dockerfile | 7 ++++++- Dockerfiles/zeek.Dockerfile | 9 +++++++-- shared/bin/service_check_passthrough.sh | 20 ++++++++------------ 21 files changed, 141 insertions(+), 62 deletions(-) diff --git a/Dockerfiles/api.Dockerfile b/Dockerfiles/api.Dockerfile index 60c7c98bb..501917ad7 100644 --- a/Dockerfiles/api.Dockerfile +++ b/Dockerfiles/api.Dockerfile @@ -73,13 +73,14 @@ COPY ./api "${APP_HOME}" COPY scripts/malcolm_common.py "${APP_HOME}"/ COPY shared/bin/opensearch_status.sh "${APP_HOME}"/ -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ + RUN apt-get -q update \ && apt-get -y -q --no-install-recommends upgrade \ && apt-get -y -q --no-install-recommends install curl netcat rsync tini \ && python3 -m pip install --upgrade pip \ && python3 -m pip install --no-cache /wheels/* \ - && chmod 755 /usr/local/bin/docker-uid-gid-setup.sh \ && groupadd --gid ${DEFAULT_GID} ${PGROUP} \ && useradd -M --uid ${DEFAULT_UID} --gid ${DEFAULT_GID} --home "${HOME}" ${PUSER} \ && chown -R ${PUSER}:${PGROUP} "${HOME}" \ @@ -89,7 +90,11 @@ RUN apt-get -q update \ EXPOSE 5000 -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "${APP_HOME}/entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh", \ + "/malcolm/api/entrypoint.sh"] # to be populated at build-time: ARG BUILD_DATE diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index 33e03b7c2..4b9fee8ac 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -176,7 +176,8 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # add configuration and scripts -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ ADD arkime/scripts /opt/ ADD shared/bin/pcap_processor.py /opt/ ADD shared/bin/pcap_utils.py /opt/ @@ -219,7 +220,11 @@ ENV PATH="/opt:$ARKIME_DIR/bin:${PATH}" EXPOSE 8000 8005 8081 WORKDIR $ARKIME_DIR -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/opt/docker_entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh", \ + "/opt/docker_entrypoint.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/dashboards-helper.Dockerfile b/Dockerfiles/dashboards-helper.Dockerfile index 2b070b928..d5d4e21f9 100644 --- a/Dockerfiles/dashboards-helper.Dockerfile +++ b/Dockerfiles/dashboards-helper.Dockerfile @@ -63,10 +63,11 @@ ADD dashboards/maps /opt/maps ADD dashboards/scripts /data/ ADD dashboards/supervisord.conf /etc/supervisord.conf ADD dashboards/templates /opt/templates -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ -ADD shared/bin/opensearch_status.sh /data/ -ADD shared/bin/opensearch_index_size_prune.py /data/ -ADD shared/bin/opensearch_read_only.py /data/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/opensearch_status.sh /data/ +COPY --chmod=755 shared/bin/opensearch_index_size_prune.py /data/ +COPY --chmod=755 shared/bin/opensearch_read_only.py /data/ ADD scripts/malcolm_common.py /data/ RUN apk update --no-cache && \ @@ -100,7 +101,10 @@ RUN apk update --no-cache && \ EXPOSE $OFFLINE_REGION_MAPS_PORT -ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/sbin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/dashboards.Dockerfile b/Dockerfiles/dashboards.Dockerfile index 31777e95e..a281868f1 100644 --- a/Dockerfiles/dashboards.Dockerfile +++ b/Dockerfiles/dashboards.Dockerfile @@ -131,8 +131,10 @@ RUN yum upgrade -y && \ yum clean all && \ rm -rf /var/cache/yum +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --chmod=755 dashboards/scripts/docker_entrypoint.sh /usr/local/bin/ ADD dashboards/opensearch_dashboards.yml /usr/share/opensearch-dashboards/config/opensearch_dashboards.orig.yml -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ ADD dashboards/scripts/docker_entrypoint.sh /usr/local/bin/ ADD scripts/malcolm_common.py /usr/local/bin/ @@ -150,7 +152,11 @@ ADD docs/images/favicon/favicon32.png /usr/share/opensearch-dashboards/src/core/ ADD docs/images/favicon/apple-touch-icon-precomposed.png /usr/share/opensearch-dashboards/src/core/server/core_app/assets/favicons/apple-touch-icon.png -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker_entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh", \ + "/usr/local/bin/docker_entrypoint.sh"] CMD ["/usr/share/opensearch-dashboards/opensearch-dashboards-docker-entrypoint.sh"] diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index 71e728b74..3bc6f0270 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -201,7 +201,8 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l ln -r -s /usr/local/bin/zeek_carve_scanner.py /usr/local/bin/capa_scan.py && \ echo "0 */6 * * * /bin/bash /usr/local/bin/capa-update.sh\n0 */6 * * * /usr/local/bin/yara_rules_setup.sh -r \"${YARA_RULES_SRC_DIR}\" -y \"${YARA_RULES_DIR}\"" > ${SUPERCRONIC_CRONTAB} -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ ADD shared/bin/zeek_carve*.py /usr/local/bin/ ADD file-monitor/supervisord.conf /etc/supervisord.conf ADD file-monitor/docker-entrypoint.sh /docker-entrypoint.sh @@ -225,7 +226,11 @@ VOLUME ["$YARA_RULES_SRC_DIR"] EXPOSE 3310 EXPOSE $EXTRACTED_FILE_HTTP_SERVER_PORT -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/docker-entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh", \ + "/docker-entrypoint.sh"] CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/file-upload.Dockerfile b/Dockerfiles/file-upload.Dockerfile index cdb40b217..ff51d5f85 100644 --- a/Dockerfiles/file-upload.Dockerfile +++ b/Dockerfiles/file-upload.Dockerfile @@ -72,9 +72,10 @@ RUN apt-get -q update && \ apt-get clean -y -q && \ rm -rf /var/lib/apt/lists/* -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --chmod=755 file-upload/docker-entrypoint.sh /docker-entrypoint.sh ADD docs/images/logo/Malcolm_banner.png /var/www/upload/Malcolm_banner.png -ADD file-upload/docker-entrypoint.sh /docker-entrypoint.sh ADD file-upload/jquery-file-upload/bootstrap.min.css /var/www/upload/bower_components/bootstrap/dist/css/bootstrap.min.css ADD file-upload/jquery-file-upload/index.html /var/www/upload/index.html ADD file-upload/jquery-file-upload/index.php /var/www/upload/server/php/index.php @@ -101,7 +102,11 @@ RUN mkdir -p /var/run/sshd /var/www/upload/server/php/chroot /run/php && \ VOLUME [ "/var/www/upload/server/php/chroot/files" ] EXPOSE 22 80 -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/docker-entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh", \ + "/docker-entrypoint.sh"] CMD ["/usr/bin/supervisord", "-c", "/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index 3090e047e..46335d6e2 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -102,7 +102,8 @@ RUN apt-get -q update && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ ADD filebeat/filebeat.yml /usr/share/filebeat/filebeat.yml ADD filebeat/filebeat-nginx.yml /usr/share/filebeat-nginx/filebeat-nginx.yml ADD filebeat/filebeat-tcp.yml /usr/share/filebeat-tcp/filebeat-tcp.yml @@ -157,7 +158,10 @@ ENV FILEBEAT_ZEEK_DIR "/zeek/" VOLUME ["/usr/share/filebeat/data", "/usr/share/filebeat-nginx/data", "/usr/share/filebeat-tcp/data"] -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/freq.Dockerfile b/Dockerfiles/freq.Dockerfile index 835a577d7..efc342e1f 100644 --- a/Dockerfiles/freq.Dockerfile +++ b/Dockerfiles/freq.Dockerfile @@ -55,14 +55,18 @@ RUN apt-get -q update && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ ADD freq-server/supervisord.conf /etc/supervisord.conf WORKDIR /opt/freq_server EXPOSE $FREQ_API_PORT -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/htadmin.Dockerfile b/Dockerfiles/htadmin.Dockerfile index 457929ae2..93ca8e341 100644 --- a/Dockerfiles/htadmin.Dockerfile +++ b/Dockerfiles/htadmin.Dockerfile @@ -76,7 +76,8 @@ RUN apt-get -q update && \ apt-get clean -y -q && \ rm -rf /var/lib/apt/lists/* /var/cache/* /tmp/* /var/tmp/* /var/www/html -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ ADD docs/images/favicon/favicon.ico /var/www/htadmin/ ADD htadmin/supervisord.conf /supervisord.conf ADD htadmin/htadmin.sh /usr/local/bin/ @@ -86,7 +87,10 @@ ADD htadmin/nginx/sites-available/default /etc/nginx/sites-available/default EXPOSE 80 -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] CMD ["/usr/bin/supervisord", "-c", "/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/logstash.Dockerfile b/Dockerfiles/logstash.Dockerfile index 0d3fc882e..d127ab3fa 100644 --- a/Dockerfiles/logstash.Dockerfile +++ b/Dockerfiles/logstash.Dockerfile @@ -74,9 +74,10 @@ RUN set -x && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/bin/jruby \ /root/.cache /root/.gem /root/.bundle -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ -ADD shared/bin/manuf-oui-parse.py /usr/local/bin/ -ADD shared/bin/jdk-cacerts-auto-import.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/manuf-oui-parse.py /usr/local/bin/ +COPY --chmod=755 shared/bin/jdk-cacerts-auto-import.sh /usr/local/bin/ ADD logstash/maps/*.yaml /etc/ ADD logstash/config/log4j2.properties /usr/share/logstash/config/ ADD logstash/config/logstash.yml /usr/share/logstash/config/logstash.orig.yml @@ -118,7 +119,10 @@ EXPOSE 5044 EXPOSE 9001 EXPOSE 9600 -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/name-map-ui.Dockerfile b/Dockerfiles/name-map-ui.Dockerfile index fcc0a0a5a..d68f1749d 100644 --- a/Dockerfiles/name-map-ui.Dockerfile +++ b/Dockerfiles/name-map-ui.Dockerfile @@ -61,14 +61,18 @@ VOLUME /var/www/html WORKDIR /var/www/html -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ COPY name-map-ui/site/ /var/www/html/ COPY docs/images/logo/Malcolm_banner.png /var/www/html/ COPY docs/images/favicon/favicon.ico /var/www/html/ EXPOSE 8080 -ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/sbin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/netbox.Dockerfile b/Dockerfiles/netbox.Dockerfile index 8e1a369a9..17f1a8094 100644 --- a/Dockerfiles/netbox.Dockerfile +++ b/Dockerfiles/netbox.Dockerfile @@ -80,11 +80,14 @@ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ COPY --chmod=755 netbox/scripts/* /usr/local/bin/ COPY --chmod=644 netbox/supervisord.conf /etc/supervisord.conf COPY --chmod=644 netbox/*-defaults.json /etc/ -COPY --from=pierrezemb/gostatic --chmod=755 /goStatic /usr/bin/goStatic +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic EXPOSE 9001 -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/service_check_passthrough.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] CMD ["/opt/netbox/docker-entrypoint.sh", "/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/nginx.Dockerfile b/Dockerfiles/nginx.Dockerfile index b78d3bf4d..024101833 100644 --- a/Dockerfiles/nginx.Dockerfile +++ b/Dockerfiles/nginx.Dockerfile @@ -224,7 +224,7 @@ COPY --from=jwilder/nginx-proxy:alpine /etc/nginx/network_internal.conf /etc/ngi COPY --from=jwilder/nginx-proxy:alpine /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/ COPY --from=docbuild /site/_site /usr/share/nginx/html/readme -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ ADD nginx/scripts /usr/local/bin/ ADD nginx/*.conf /etc/nginx/ ADD nginx/supervisord.conf /etc/ @@ -233,7 +233,10 @@ ADD docs/images/icon/favicon.ico /usr/share/nginx/html/favicon.ico VOLUME ["/etc/nginx/certs", "/etc/nginx/dhparam"] -ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker_entrypoint.sh"] +ENTRYPOINT ["/sbin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/docker_entrypoint.sh"] CMD ["supervisord", "-c", "/etc/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/opensearch.Dockerfile b/Dockerfiles/opensearch.Dockerfile index 2caa74faf..9634a4fe8 100644 --- a/Dockerfiles/opensearch.Dockerfile +++ b/Dockerfiles/opensearch.Dockerfile @@ -54,14 +54,17 @@ RUN yum install -y openssl util-linux procps rsync && \ sed -i '/^[[:space:]]*runOpensearch.*/i /usr/local/bin/jdk-cacerts-auto-import.sh || true' /usr/share/opensearch/opensearch-docker-entrypoint.sh -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ -ADD shared/bin/jdk-cacerts-auto-import.sh /usr/local/bin/ -COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/docker-entrypoint.sh -COPY --from=pierrezemb/gostatic --chmod=755 /goStatic /usr/bin/goStatic +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/jdk-cacerts-auto-import.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic VOLUME ["/var/local/ca-trust"] -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker-entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] CMD ["/usr/share/opensearch/opensearch-docker-entrypoint.sh"] diff --git a/Dockerfiles/pcap-capture.Dockerfile b/Dockerfiles/pcap-capture.Dockerfile index 39ebea6b2..1855c1cc9 100644 --- a/Dockerfiles/pcap-capture.Dockerfile +++ b/Dockerfiles/pcap-capture.Dockerfile @@ -52,8 +52,9 @@ ENV PCAP_PATH $PCAP_PATH ENV PCAP_FILTER $PCAP_FILTER ENV PCAP_SNAPLEN $PCAP_SNAPLEN -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ -ADD shared/bin/nic-capture-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/nic-capture-setup.sh /usr/local/bin/ ADD pcap-capture/supervisord.conf /etc/supervisord.conf ADD pcap-capture/scripts/*.sh /usr/local/bin/ ADD pcap-capture/templates/*.template /etc/supervisor.d/ @@ -89,7 +90,10 @@ RUN apt-get -q update && \ WORKDIR "$PCAP_PATH" -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] CMD ["/usr/local/bin/supervisor.sh"] diff --git a/Dockerfiles/pcap-monitor.Dockerfile b/Dockerfiles/pcap-monitor.Dockerfile index af421681f..fa16fc82d 100644 --- a/Dockerfiles/pcap-monitor.Dockerfile +++ b/Dockerfiles/pcap-monitor.Dockerfile @@ -64,7 +64,8 @@ RUN apt-get -q update && \ groupadd --gid ${DEFAULT_GID} ${PGROUP} && \ useradd -M --uid ${DEFAULT_UID} --gid ${DEFAULT_GID} ${PUSER} -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ ADD pcap-monitor/supervisord.conf /etc/supervisord.conf ADD pcap-monitor/scripts/ /usr/local/bin/ ADD shared/bin/pcap_watcher.py /usr/local/bin/ @@ -73,7 +74,10 @@ ADD scripts/malcolm_common.py /usr/local/bin/ EXPOSE 30441 -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/postgresql.Dockerfile b/Dockerfiles/postgresql.Dockerfile index 24e26eabf..449b0697e 100644 --- a/Dockerfiles/postgresql.Dockerfile +++ b/Dockerfiles/postgresql.Dockerfile @@ -23,7 +23,7 @@ ENV TERM xterm COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ -COPY --from=pierrezemb/gostatic --chmod=755 /goStatic /usr/bin/goStatic +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic RUN apk update --no-cache && \ apk upgrade --no-cache && \ @@ -40,7 +40,10 @@ RUN apk update --no-cache && \ USER root -ENTRYPOINT ["/sbin/tini", "--", "/usr/bin/docker-uid-gid-setup.sh", "/usr/local/bin/service_check_passthrough.sh"] +ENTRYPOINT ["/sbin/tini", \ + "--", \ + "/usr/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] CMD ["/usr/bin/docker-entrypoint.sh", "postgres"] diff --git a/Dockerfiles/redis.Dockerfile b/Dockerfiles/redis.Dockerfile index 49088ca9a..9151fd9cf 100644 --- a/Dockerfiles/redis.Dockerfile +++ b/Dockerfiles/redis.Dockerfile @@ -22,7 +22,7 @@ ENV TERM xterm COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ -COPY --from=pierrezemb/gostatic --chmod=755 /goStatic /usr/bin/goStatic +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic RUN apk update --no-cache && \ apk upgrade --no-cache && \ @@ -31,7 +31,10 @@ RUN apk update --no-cache && \ WORKDIR /home/${PUSER} -ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/service_check_passthrough.sh"] +ENTRYPOINT ["/sbin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] # to be populated at build-time: ARG BUILD_DATE diff --git a/Dockerfiles/suricata.Dockerfile b/Dockerfiles/suricata.Dockerfile index 48f48c379..992c96477 100644 --- a/Dockerfiles/suricata.Dockerfile +++ b/Dockerfiles/suricata.Dockerfile @@ -124,6 +124,7 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l COPY --chmod=644 shared/bin/pcap_utils.py /usr/local/bin/ COPY --chmod=644 suricata/supervisord.conf /etc/supervisord.conf COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ COPY --chmod=755 shared/bin/nic-capture-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/pcap_processor.py /usr/local/bin/ COPY --chmod=755 shared/bin/suricata_config_populate.py /usr/local/bin/ @@ -180,6 +181,10 @@ VOLUME ["$SURICATA_RUN_DIR"] WORKDIR $SURICATA_RUN_DIR -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker_entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/service_check_passthrough.sh", \ + "/usr/local/bin/docker_entrypoint.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index b4dc385bf..8ca652d64 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -149,7 +149,8 @@ RUN export DEBARCH=$(dpkg --print-architecture) && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/cache/*/* # add configuration and scripts -ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ ADD shared/bin/pcap_processor.py /usr/local/bin/ ADD shared/bin/pcap_utils.py /usr/local/bin/ ADD shared/bin/zeek*threat*.py ${ZEEK_DIR}/bin/ @@ -274,7 +275,11 @@ ENV PUSER_CHOWN "$ZEEK_DIR" VOLUME ["${ZEEK_DIR}/share/zeek/site/intel"] -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker_entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", \ + "--", \ + "/usr/local/bin/docker-uid-gid-setup.sh", \ + "/usr/local/bin/docker_entrypoint.sh", \ + "/usr/local/bin/service_check_passthrough.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/shared/bin/service_check_passthrough.sh b/shared/bin/service_check_passthrough.sh index cac188740..2aa75b2e4 100755 --- a/shared/bin/service_check_passthrough.sh +++ b/shared/bin/service_check_passthrough.sh @@ -100,7 +100,7 @@ fi if [[ -n "$SERVICE" ]]; then if [[ -z "$PORT" ]]; then if [[ "$SERVICE" == "api" ]]; then - PORT=500 + PORT=5000 elif [[ "$SERVICE" == "arkime" ]]; then PORT=8005 elif [[ "$SERVICE" == "dashboards" ]]; then @@ -121,17 +121,13 @@ if [[ -n "$SERVICE" ]]; then PORT=9200 fi fi - if [[ -z "$FORMAT" ]]; then - if [[ "$SERVICE" == "api" ]]; then - FORMAT=json - elif [[ "$SERVICE" == "logstash" ]]; then - FORMAT=json - elif [[ "$SERVICE" == "netbox" ]]; then - FORMAT=json - elif [[ "$SERVICE" == "opensearch" ]]; then - FORMAT=json - fi - fi + [[ -z "$FORMAT" ]] && \ + ([[ "$SERVICE" == "api" ]] || \ + [[ "$SERVICE" == "dashboards-helper" ]] || \ + [[ "$SERVICE" == "freq" ]] || \ + [[ "$SERVICE" == "logstash" ]] || \ + [[ "$SERVICE" == "netbox" ]] || \ + [[ "$SERVICE" == "opensearch" ]]) && FORMAT=json fi [[ -z "$PORT" ]] && PORT=80 [[ -z "$FORMAT" ]] && FORMAT=http From 617288db5f6a9111567dc521710476a849a40f16 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 20 Mar 2023 09:50:27 -0600 Subject: [PATCH 016/239] trigger build --- .trigger_workflow_build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.trigger_workflow_build b/.trigger_workflow_build index b75e0fb2a..50520d4f0 100644 --- a/.trigger_workflow_build +++ b/.trigger_workflow_build @@ -1,2 +1,2 @@ # this file exists solely for the purpose of being updated and seen by github to trigger a commit build action -3 +4 From 6efaaa0e77445e788602cc3e47b43a88bdaf4937 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 20 Mar 2023 11:06:04 -0600 Subject: [PATCH 017/239] fix freq --- kubernetes/01-malcolm.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/kubernetes/01-malcolm.yml b/kubernetes/01-malcolm.yml index c3018c645..883a7facf 100644 --- a/kubernetes/01-malcolm.yml +++ b/kubernetes/01-malcolm.yml @@ -46,7 +46,6 @@ spec: protocol: TCP selector: app: malcolm-app - name: freq-container --- apiVersion: apps/v1 From c18ef0f436d04521a3554ca5ac79363bbfc388fc Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 20 Mar 2023 11:23:32 -0600 Subject: [PATCH 018/239] manually specify service name as argument in entrypoint --- Dockerfiles/api.Dockerfile | 1 + Dockerfiles/arkime.Dockerfile | 1 + Dockerfiles/dashboards-helper.Dockerfile | 3 ++- Dockerfiles/dashboards.Dockerfile | 1 + Dockerfiles/file-monitor.Dockerfile | 1 + Dockerfiles/file-upload.Dockerfile | 1 + Dockerfiles/filebeat.Dockerfile | 3 ++- Dockerfiles/freq.Dockerfile | 3 ++- Dockerfiles/htadmin.Dockerfile | 3 ++- Dockerfiles/logstash.Dockerfile | 3 ++- Dockerfiles/name-map-ui.Dockerfile | 3 ++- Dockerfiles/netbox.Dockerfile | 3 ++- Dockerfiles/opensearch.Dockerfile | 3 ++- Dockerfiles/pcap-capture.Dockerfile | 3 ++- Dockerfiles/pcap-monitor.Dockerfile | 3 ++- Dockerfiles/postgresql.Dockerfile | 3 ++- Dockerfiles/redis.Dockerfile | 3 ++- Dockerfiles/suricata.Dockerfile | 1 + Dockerfiles/zeek.Dockerfile | 3 ++- 19 files changed, 32 insertions(+), 13 deletions(-) diff --git a/Dockerfiles/api.Dockerfile b/Dockerfiles/api.Dockerfile index 501917ad7..093b666c6 100644 --- a/Dockerfiles/api.Dockerfile +++ b/Dockerfiles/api.Dockerfile @@ -94,6 +94,7 @@ ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "api", \ "/malcolm/api/entrypoint.sh"] # to be populated at build-time: diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index 4b9fee8ac..7b2a02566 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -224,6 +224,7 @@ ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "arkime", \ "/opt/docker_entrypoint.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/dashboards-helper.Dockerfile b/Dockerfiles/dashboards-helper.Dockerfile index d5d4e21f9..5992f723b 100644 --- a/Dockerfiles/dashboards-helper.Dockerfile +++ b/Dockerfiles/dashboards-helper.Dockerfile @@ -104,7 +104,8 @@ EXPOSE $OFFLINE_REGION_MAPS_PORT ENTRYPOINT ["/sbin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "dashboards-helper"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/dashboards.Dockerfile b/Dockerfiles/dashboards.Dockerfile index a281868f1..3141cc8c6 100644 --- a/Dockerfiles/dashboards.Dockerfile +++ b/Dockerfiles/dashboards.Dockerfile @@ -156,6 +156,7 @@ ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "dashboards", \ "/usr/local/bin/docker_entrypoint.sh"] CMD ["/usr/share/opensearch-dashboards/opensearch-dashboards-docker-entrypoint.sh"] diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index 3bc6f0270..c9b188b21 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -230,6 +230,7 @@ ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "file-monitor", \ "/docker-entrypoint.sh"] CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/file-upload.Dockerfile b/Dockerfiles/file-upload.Dockerfile index ff51d5f85..d4bf5935d 100644 --- a/Dockerfiles/file-upload.Dockerfile +++ b/Dockerfiles/file-upload.Dockerfile @@ -106,6 +106,7 @@ ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "upload", \ "/docker-entrypoint.sh"] CMD ["/usr/bin/supervisord", "-c", "/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index 46335d6e2..57b9460a5 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -161,7 +161,8 @@ VOLUME ["/usr/share/filebeat/data", "/usr/share/filebeat-nginx/data", "/usr/shar ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "filebeat"] CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/freq.Dockerfile b/Dockerfiles/freq.Dockerfile index efc342e1f..0a22d8a1c 100644 --- a/Dockerfiles/freq.Dockerfile +++ b/Dockerfiles/freq.Dockerfile @@ -66,7 +66,8 @@ EXPOSE $FREQ_API_PORT ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "freq"] CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/htadmin.Dockerfile b/Dockerfiles/htadmin.Dockerfile index 93ca8e341..32f532b55 100644 --- a/Dockerfiles/htadmin.Dockerfile +++ b/Dockerfiles/htadmin.Dockerfile @@ -90,7 +90,8 @@ EXPOSE 80 ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "htadmin"] CMD ["/usr/bin/supervisord", "-c", "/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/logstash.Dockerfile b/Dockerfiles/logstash.Dockerfile index d127ab3fa..962f45c0d 100644 --- a/Dockerfiles/logstash.Dockerfile +++ b/Dockerfiles/logstash.Dockerfile @@ -122,7 +122,8 @@ EXPOSE 9600 ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "logstash"] CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/name-map-ui.Dockerfile b/Dockerfiles/name-map-ui.Dockerfile index d68f1749d..13a394626 100644 --- a/Dockerfiles/name-map-ui.Dockerfile +++ b/Dockerfiles/name-map-ui.Dockerfile @@ -72,7 +72,8 @@ EXPOSE 8080 ENTRYPOINT ["/sbin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "name-map-ui"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/netbox.Dockerfile b/Dockerfiles/netbox.Dockerfile index 17f1a8094..aa748d54d 100644 --- a/Dockerfiles/netbox.Dockerfile +++ b/Dockerfiles/netbox.Dockerfile @@ -87,7 +87,8 @@ EXPOSE 9001 ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "netbox"] CMD ["/opt/netbox/docker-entrypoint.sh", "/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/opensearch.Dockerfile b/Dockerfiles/opensearch.Dockerfile index 9634a4fe8..be897a523 100644 --- a/Dockerfiles/opensearch.Dockerfile +++ b/Dockerfiles/opensearch.Dockerfile @@ -64,7 +64,8 @@ VOLUME ["/var/local/ca-trust"] ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "opensearch"] CMD ["/usr/share/opensearch/opensearch-docker-entrypoint.sh"] diff --git a/Dockerfiles/pcap-capture.Dockerfile b/Dockerfiles/pcap-capture.Dockerfile index 1855c1cc9..dff0b3cfa 100644 --- a/Dockerfiles/pcap-capture.Dockerfile +++ b/Dockerfiles/pcap-capture.Dockerfile @@ -93,7 +93,8 @@ WORKDIR "$PCAP_PATH" ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "pcap-capture"] CMD ["/usr/local/bin/supervisor.sh"] diff --git a/Dockerfiles/pcap-monitor.Dockerfile b/Dockerfiles/pcap-monitor.Dockerfile index fa16fc82d..28302c7b4 100644 --- a/Dockerfiles/pcap-monitor.Dockerfile +++ b/Dockerfiles/pcap-monitor.Dockerfile @@ -77,7 +77,8 @@ EXPOSE 30441 ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "pcapmon"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/postgresql.Dockerfile b/Dockerfiles/postgresql.Dockerfile index 449b0697e..1c740f3d3 100644 --- a/Dockerfiles/postgresql.Dockerfile +++ b/Dockerfiles/postgresql.Dockerfile @@ -43,7 +43,8 @@ USER root ENTRYPOINT ["/sbin/tini", \ "--", \ "/usr/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "netbox-postgres"] CMD ["/usr/bin/docker-entrypoint.sh", "postgres"] diff --git a/Dockerfiles/redis.Dockerfile b/Dockerfiles/redis.Dockerfile index 9151fd9cf..6585f2ea2 100644 --- a/Dockerfiles/redis.Dockerfile +++ b/Dockerfiles/redis.Dockerfile @@ -34,7 +34,8 @@ WORKDIR /home/${PUSER} ENTRYPOINT ["/sbin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "netbox-redis"] # to be populated at build-time: ARG BUILD_DATE diff --git a/Dockerfiles/suricata.Dockerfile b/Dockerfiles/suricata.Dockerfile index 992c96477..ea6c09390 100644 --- a/Dockerfiles/suricata.Dockerfile +++ b/Dockerfiles/suricata.Dockerfile @@ -185,6 +185,7 @@ ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "suricata", \ "/usr/local/bin/docker_entrypoint.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index 8ca652d64..ffa5af04b 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -279,7 +279,8 @@ ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ "/usr/local/bin/docker_entrypoint.sh", \ - "/usr/local/bin/service_check_passthrough.sh"] + "/usr/local/bin/service_check_passthrough.sh", \ + "-s", "zeek"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] From 80bcd2dee04ac6f0f6d05c29760e7ea07b54e0f2 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 20 Mar 2023 14:09:44 -0600 Subject: [PATCH 019/239] work in progress for environment variables etc --- kubernetes/00-ingress.yml | 22 ++ kubernetes/01-malcolm.yml | 158 -------- kubernetes/02-opensearch.yml | 47 +++ kubernetes/03-dashboards.yml | 47 +++ kubernetes/04-upload.yml | 50 +++ kubernetes/05-pcap-monitor.yml | 49 +++ kubernetes/06-arkime.yml | 51 +++ kubernetes/07-api.yml | 47 +++ kubernetes/08-dashboards-helper.yml | 49 +++ kubernetes/09-zeek.yml | 36 ++ kubernetes/10-suricata.yml | 36 ++ kubernetes/11-file-monitor.yml | 47 +++ kubernetes/12-filebeat.yml | 55 +++ kubernetes/13-logstash.yml | 60 +++ kubernetes/14-name-map-ui.yml | 45 +++ kubernetes/15-netbox-redis.yml | 47 +++ kubernetes/16-netbox-redis-cache.yml | 47 +++ kubernetes/17-netbox-postgres.yml | 47 +++ kubernetes/18-netbox.yml | 56 +++ kubernetes/19-htadmin.yml | 47 +++ kubernetes/20-pcap-capture.yml | 32 ++ kubernetes/21-zeek-live.yml | 38 ++ kubernetes/22-suricata-live.yml | 38 ++ kubernetes/23-freq.yml | 44 +++ kubernetes/99-nginx-proxy.yml | 80 ++++ kubernetes/arkime.env | 8 + kubernetes/auth.env | 13 + kubernetes/common-beats.env | 3 + kubernetes/common-lookup.env | 16 + kubernetes/common-upload.env | 21 ++ kubernetes/dashboards-helper.env | 12 + kubernetes/filebeat.env | 26 ++ kubernetes/logstash.env | 15 + kubernetes/netbox.env | 15 + kubernetes/nginx.env | 9 + kubernetes/opensearch.env | 38 ++ kubernetes/pcap-capture.env | 23 ++ kubernetes/process.env | 7 + kubernetes/ssl.env | 3 + kubernetes/suricata-live.env | 6 + kubernetes/suricata-offline.env | 10 + kubernetes/suricata.env | 10 + kubernetes/zeek-live.env | 3 + kubernetes/zeek-offline.env | 10 + kubernetes/zeek.env | 65 ++++ nginx/nginx.conf | 524 +++++++++++++-------------- 46 files changed, 1691 insertions(+), 421 deletions(-) create mode 100644 kubernetes/00-ingress.yml delete mode 100644 kubernetes/01-malcolm.yml create mode 100644 kubernetes/02-opensearch.yml create mode 100644 kubernetes/03-dashboards.yml create mode 100644 kubernetes/04-upload.yml create mode 100644 kubernetes/05-pcap-monitor.yml create mode 100644 kubernetes/06-arkime.yml create mode 100644 kubernetes/07-api.yml create mode 100644 kubernetes/08-dashboards-helper.yml create mode 100644 kubernetes/09-zeek.yml create mode 100644 kubernetes/10-suricata.yml create mode 100644 kubernetes/11-file-monitor.yml create mode 100644 kubernetes/12-filebeat.yml create mode 100644 kubernetes/13-logstash.yml create mode 100644 kubernetes/14-name-map-ui.yml create mode 100644 kubernetes/15-netbox-redis.yml create mode 100644 kubernetes/16-netbox-redis-cache.yml create mode 100644 kubernetes/17-netbox-postgres.yml create mode 100644 kubernetes/18-netbox.yml create mode 100644 kubernetes/19-htadmin.yml create mode 100644 kubernetes/20-pcap-capture.yml create mode 100644 kubernetes/21-zeek-live.yml create mode 100644 kubernetes/22-suricata-live.yml create mode 100644 kubernetes/23-freq.yml create mode 100644 kubernetes/99-nginx-proxy.yml create mode 100644 kubernetes/arkime.env create mode 100644 kubernetes/auth.env create mode 100644 kubernetes/common-beats.env create mode 100644 kubernetes/common-lookup.env create mode 100644 kubernetes/common-upload.env create mode 100644 kubernetes/dashboards-helper.env create mode 100644 kubernetes/filebeat.env create mode 100644 kubernetes/logstash.env create mode 100644 kubernetes/netbox.env create mode 100644 kubernetes/nginx.env create mode 100644 kubernetes/opensearch.env create mode 100644 kubernetes/pcap-capture.env create mode 100644 kubernetes/process.env create mode 100644 kubernetes/ssl.env create mode 100644 kubernetes/suricata-live.env create mode 100644 kubernetes/suricata-offline.env create mode 100644 kubernetes/suricata.env create mode 100644 kubernetes/zeek-live.env create mode 100644 kubernetes/zeek-offline.env create mode 100644 kubernetes/zeek.env diff --git a/kubernetes/00-ingress.yml b/kubernetes/00-ingress.yml new file mode 100644 index 000000000..60d3925e3 --- /dev/null +++ b/kubernetes/00-ingress.yml @@ -0,0 +1,22 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: nginx-ingress + namespace: malcolm + annotations: + kubernetes.io/ingress.class: "nginx" + nginx.ingress.kubernetes.io/backend-protocol: "https" + nginx.ingress.kubernetes.io/preserve-trailing-slash: "true" + nginx.ingress.kubernetes.io/ssl-passthrough: "false" +spec: + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: nginx-proxy + port: + number: 443 \ No newline at end of file diff --git a/kubernetes/01-malcolm.yml b/kubernetes/01-malcolm.yml deleted file mode 100644 index 883a7facf..000000000 --- a/kubernetes/01-malcolm.yml +++ /dev/null @@ -1,158 +0,0 @@ ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: nginx-ingress - namespace: malcolm - annotations: - kubernetes.io/ingress.class: "nginx" - nginx.ingress.kubernetes.io/backend-protocol: "https" - nginx.ingress.kubernetes.io/preserve-trailing-slash: "true" - nginx.ingress.kubernetes.io/ssl-passthrough: "false" -spec: - rules: - - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: nginx-proxy - port: - number: 443 - ---- -apiVersion: v1 -kind: Service -metadata: - name: nginx-proxy - namespace: malcolm -spec: - ports: - - port: 443 - protocol: TCP - selector: - app: malcolm-app - ---- -apiVersion: v1 -kind: Service -metadata: - name: freq - namespace: malcolm -spec: - ports: - - port: 10004 - protocol: TCP - selector: - app: malcolm-app - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: malcolm-app - namespace: malcolm -spec: - selector: - matchLabels: - app: malcolm-app - replicas: 1 - template: - metadata: - labels: - app: malcolm-app - spec: - containers: - - name: freq-container - image: ghcr.io/idaholab/malcolm/freq:kubernetes - imagePullPolicy: Always - stdin: false - tty: true - ports: - - containerPort: 10004 - env: - - name: CONFIG_MAP_DIR - value: 'configmap' - - name: PUID - value: '1000' - - name: PGID - value: '1000' - - name: PUSER_CA_TRUST - value: '/var/local/ca-trust' - - name: VIRTUAL_HOST - value: 'freq.malcolm.local' - - name: FREQ_LOOKUP - value: 'true' - - name: FREQ_SEVERITY_THRESHOLD - value: '2.0' - - name: TOTAL_MEGABYTES_SEVERITY_THRESHOLD - value: '1000' - - name: CONNECTION_SECONDS_SEVERITY_THRESHOLD - value: '3600' - - name: SENSITIVE_COUNTRY_CODES - value: 'AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ' - - name: nginx-proxy-container - image: ghcr.io/idaholab/malcolm/nginx-proxy:kubernetes - imagePullPolicy: Always - stdin: false - tty: true - ports: - - containerPort: 443 - env: - - name: PUID - value: '1000' - - name: PGID - value: '1000' - - name: PUSER_CA_TRUST - value: '/var/local/ca-trust' - - name: NGINX_BASIC_AUTH - value: 'true' - - name: NGINX_LDAP_TLS_STUNNEL - value: 'false' - - name: NGINX_LDAP_TLS_STUNNEL_CHECK_HOST - value: '' - - name: NGINX_LDAP_TLS_STUNNEL_CHECK_IP - value: '' - - name: NGINX_LDAP_TLS_STUNNEL_VERIFY_LEVEL - value: '2' - - name: NGINX_SSL - value: 'true' - - name: NGINX_LOG_ACCESS_AND_ERRORS - value: 'false' - - name: CONFIG_MAP_DIR - value: 'configmap' - livenessProbe: - exec: - command: - - curl - - --insecure - - --silent - - https://localhost:443 - initialDelaySeconds: 120 - periodSeconds: 30 - timeoutSeconds: 15 - successThreshold: 1 - failureThreshold: 10 - volumeMounts: - - name: etc-nginx-volume - mountPath: /etc/nginx/configmap - - name: var-local-catrust-volume - mountPath: /var/local/ca-trust/configmap - - name: etc-nginx-certs-volume - mountPath: /etc/nginx/certs/configmap - - name: etc-nginx-certs-pem-volume - mountPath: /etc/nginx/dhparam/configmap - volumes: - - name: etc-nginx-volume - configMap: - name: etc-nginx - - name: var-local-catrust-volume - configMap: - name: var-local-catrust - - name: etc-nginx-certs-volume - configMap: - name: etc-nginx-certs - - name: etc-nginx-certs-pem-volume - configMap: - name: etc-nginx-certs-pem \ No newline at end of file diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml new file mode 100644 index 000000000..7005e7061 --- /dev/null +++ b/kubernetes/02-opensearch.yml @@ -0,0 +1,47 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: opensearch + namespace: malcolm +spec: + ports: + - port: 9200 + protocol: TCP + selector: + name: opensearch-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: opensearch-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: opensearch-deployment + replicas: 1 + template: + metadata: + labels: + name: opensearch-deployment + spec: + containers: + - name: opensearch-container + image: ghcr.io/idaholab/malcolm/opensearch:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 9200 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: opensearch-env + env: + - name: OPENSEARCH_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/03-dashboards.yml b/kubernetes/03-dashboards.yml new file mode 100644 index 000000000..2da98b889 --- /dev/null +++ b/kubernetes/03-dashboards.yml @@ -0,0 +1,47 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: dashboards + namespace: malcolm +spec: + ports: + - port: 5601 + protocol: TCP + selector: + name: dashboards-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dashboards-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: dashboards-deployment + replicas: 1 + template: + metadata: + labels: + name: dashboards-deployment + spec: + containers: + - name: dashboards-container + image: ghcr.io/idaholab/malcolm/dashboards:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 5601 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: opensearch-env + env: + - name: DASHBOARDS_DISABLED + value: "true" diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml new file mode 100644 index 000000000..6e52fde3f --- /dev/null +++ b/kubernetes/04-upload.yml @@ -0,0 +1,50 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: upload + namespace: malcolm +spec: + ports: + - port: 22 + protocol: TCP + name: ssh + - port: 80 + protocol: TCP + name: http + selector: + name: upload-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: upload-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: upload-deployment + replicas: 1 + template: + metadata: + labels: + name: upload-deployment + spec: + containers: + - name: upload-container + image: ghcr.io/idaholab/malcolm/file-upload:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 22 + - containerPort: 80 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + env: + - name: UPLOAD_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml new file mode 100644 index 000000000..28bade031 --- /dev/null +++ b/kubernetes/05-pcap-monitor.yml @@ -0,0 +1,49 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: pcapmon + namespace: malcolm +spec: + ports: + - port: 30441 + protocol: TCP + selector: + name: pcap-monitor-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pcap-monitor-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: pcap-monitor-deployment + replicas: 1 + template: + metadata: + labels: + name: pcap-monitor-deployment + spec: + containers: + - name: pcap-monitor-container + image: ghcr.io/idaholab/malcolm/pcap-monitor:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 30441 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: opensearch-env + - configMapRef: + name: common-upload-env + env: + - name: PCAPMON_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml new file mode 100644 index 000000000..bcf06f907 --- /dev/null +++ b/kubernetes/06-arkime.yml @@ -0,0 +1,51 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: arkime + namespace: malcolm +spec: + ports: + - port: 8005 + protocol: TCP + selector: + name: arkime-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: arkime-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: arkime-deployment + replicas: 1 + template: + metadata: + labels: + name: arkime-deployment + spec: + containers: + - name: arkime-container + image: ghcr.io/idaholab/malcolm/arkime:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 8005 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: opensearch-env + - configMapRef: + name: common-upload-env + - configMapRef: + name: arkime-env + env: + - name: ARKIME_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/07-api.yml b/kubernetes/07-api.yml new file mode 100644 index 000000000..9dc444f2a --- /dev/null +++ b/kubernetes/07-api.yml @@ -0,0 +1,47 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: api + namespace: malcolm +spec: + ports: + - port: 5000 + protocol: TCP + selector: + name: api-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: api-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: api-deployment + replicas: 1 + template: + metadata: + labels: + name: api-deployment + spec: + containers: + - name: api-container + image: ghcr.io/idaholab/malcolm/api:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 5000 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: opensearch-env + env: + - name: API_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/08-dashboards-helper.yml b/kubernetes/08-dashboards-helper.yml new file mode 100644 index 000000000..b69de6f65 --- /dev/null +++ b/kubernetes/08-dashboards-helper.yml @@ -0,0 +1,49 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: dashboards-helper + namespace: malcolm +spec: + ports: + - port: 28991 + protocol: TCP + selector: + name: dashboards-helper-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dashboards-helper-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: dashboards-helper-deployment + replicas: 1 + template: + metadata: + labels: + name: dashboards-helper-deployment + spec: + containers: + - name: dashboards-helper-container + image: ghcr.io/idaholab/malcolm/dashboards-helper:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 28991 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: opensearch-env + - configMapRef: + name: dashboards-helper-env + env: + - name: DASHBOARDS_HELPER_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/09-zeek.yml b/kubernetes/09-zeek.yml new file mode 100644 index 000000000..21b27eb88 --- /dev/null +++ b/kubernetes/09-zeek.yml @@ -0,0 +1,36 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zeek-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: zeek-deployment + replicas: 1 + template: + metadata: + labels: + name: zeek-deployment + spec: + containers: + - name: zeek-container + image: ghcr.io/idaholab/malcolm/zeek:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: common-upload-env + - configMapRef: + name: zeek-env + - configMapRef: + name: zeek-offline-env + env: + - name: ZEEK_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/10-suricata.yml b/kubernetes/10-suricata.yml new file mode 100644 index 000000000..97e97668d --- /dev/null +++ b/kubernetes/10-suricata.yml @@ -0,0 +1,36 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: suricata-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: suricata-deployment + replicas: 1 + template: + metadata: + labels: + name: suricata-deployment + spec: + containers: + - name: suricata-container + image: ghcr.io/idaholab/malcolm/suricata:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: common-upload-env + - configMapRef: + name: suricata-env + - configMapRef: + name: suricata-offline-env + env: + - name: SURICATA_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/11-file-monitor.yml b/kubernetes/11-file-monitor.yml new file mode 100644 index 000000000..a6c5d7488 --- /dev/null +++ b/kubernetes/11-file-monitor.yml @@ -0,0 +1,47 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: file-monitor + namespace: malcolm +spec: + ports: + - port: 3310 + protocol: TCP + selector: + name: file-monitor-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: file-monitor-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: file-monitor-deployment + replicas: 1 + template: + metadata: + labels: + name: file-monitor-deployment + spec: + containers: + - name: file-monitor-container + image: ghcr.io/idaholab/malcolm/file-monitor:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 3310 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: zeek-env + env: + - name: FILE_MONITOR_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml new file mode 100644 index 000000000..d212b3878 --- /dev/null +++ b/kubernetes/12-filebeat.yml @@ -0,0 +1,55 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: filebeat + namespace: malcolm +spec: + ports: + - port: 5045 + protocol: TCP + selector: + name: filebeat-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: filebeat-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: filebeat-deployment + replicas: 1 + template: + metadata: + labels: + name: filebeat-deployment + spec: + containers: + - name: filebeat-container + image: ghcr.io/idaholab/malcolm/filebeat-oss:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 5045 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: nginx-env + - configMapRef: + name: opensearch-env + - configMapRef: + name: filebeat-env + - configMapRef: + name: common-upload-env + - configMapRef: + name: common-beats-env + env: + - name: FILEBEAT_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml new file mode 100644 index 000000000..a37594d35 --- /dev/null +++ b/kubernetes/13-logstash.yml @@ -0,0 +1,60 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: logstash + namespace: malcolm +spec: + ports: + - port: 5044 + protocol: TCP + name: lumberjack + - port: 9600 + protocol: TCP + name: api + selector: + name: logstash-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: logstash-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: logstash-deployment + replicas: 1 + template: + metadata: + labels: + name: logstash-deployment + spec: + containers: + - name: logstash-container + image: ghcr.io/idaholab/malcolm/logstash-oss:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 5044 + - containerPort: 9600 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: opensearch-env + - configMapRef: + name: netbox-env + - configMapRef: + name: logstash-env + - configMapRef: + name: common-beats-env + - configMapRef: + name: common-lookup-env + env: + - name: LOGSTASH_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/14-name-map-ui.yml b/kubernetes/14-name-map-ui.yml new file mode 100644 index 000000000..5f9515eac --- /dev/null +++ b/kubernetes/14-name-map-ui.yml @@ -0,0 +1,45 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: name-map-ui + namespace: malcolm +spec: + ports: + - port: 9200 + protocol: TCP + selector: + name: name-map-ui-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: name-map-ui-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: name-map-ui-deployment + replicas: 1 + template: + metadata: + labels: + name: name-map-ui-deployment + spec: + containers: + - name: name-map-ui-container + image: ghcr.io/idaholab/malcolm/name-map-ui:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 9200 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + env: + - name: NAME_MAP_UI_DISABLED + value: "true" diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml new file mode 100644 index 000000000..e75b79dd6 --- /dev/null +++ b/kubernetes/15-netbox-redis.yml @@ -0,0 +1,47 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: netbox-redis + namespace: malcolm +spec: + ports: + - port: 6379 + protocol: TCP + selector: + name: netbox-redis-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: netbox-redis-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: netbox-redis-deployment + replicas: 1 + template: + metadata: + labels: + name: netbox-redis-deployment + spec: + containers: + - name: netbox-redis-container + image: ghcr.io/idaholab/malcolm/redis:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 6379 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: netbox-env + env: + - name: NETBOX_REDIS_DISABLED + value: "true" diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml new file mode 100644 index 000000000..c5d8bfe7d --- /dev/null +++ b/kubernetes/16-netbox-redis-cache.yml @@ -0,0 +1,47 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: netbox-redis-cache + namespace: malcolm +spec: + ports: + - port: 6379 + protocol: TCP + selector: + name: netbox-redis-cache-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: netbox-redis-cache-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: netbox-redis-cache-deployment + replicas: 1 + template: + metadata: + labels: + name: netbox-redis-cache-deployment + spec: + containers: + - name: netbox-redis-cache-container + image: ghcr.io/idaholab/malcolm/redis:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 6379 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: netbox-env + env: + - name: NETBOX_REDIS_CACHE_DISABLED + value: "true" diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml new file mode 100644 index 000000000..8e49d37f6 --- /dev/null +++ b/kubernetes/17-netbox-postgres.yml @@ -0,0 +1,47 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: netbox-postgres + namespace: malcolm +spec: + ports: + - port: 5432 + protocol: TCP + selector: + name: netbox-postgres-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: netbox-postgres-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: netbox-postgres-deployment + replicas: 1 + template: + metadata: + labels: + name: netbox-postgres-deployment + spec: + containers: + - name: netbox-postgres-container + image: ghcr.io/idaholab/malcolm/postgresql:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 5432 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: netbox-env + env: + - name: NETBOX_POSTGRES_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml new file mode 100644 index 000000000..c1d8d84c7 --- /dev/null +++ b/kubernetes/18-netbox.yml @@ -0,0 +1,56 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: netbox + namespace: malcolm +spec: + ports: + - port: 8080 + protocol: TCP + name: eightyeighty + - port: 8081 + protocol: TCP + name: eightyeightyone + - port: 9001 + protocol: TCP + name: ninethousandone + selector: + name: netbox-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: netbox-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: netbox-deployment + replicas: 1 + template: + metadata: + labels: + name: netbox-deployment + spec: + containers: + - name: netbox-container + image: ghcr.io/idaholab/malcolm/netbox:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 8080 + - containerPort: 8081 + - containerPort: 9001 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: netbox-env + env: + - name: NETBOX_DISABLED + value: "true" diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml new file mode 100644 index 000000000..83cec7b2f --- /dev/null +++ b/kubernetes/19-htadmin.yml @@ -0,0 +1,47 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: htadmin + namespace: malcolm +spec: + ports: + - port: 80 + protocol: TCP + selector: + name: htadmin-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: htadmin-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: htadmin-deployment + replicas: 1 + template: + metadata: + labels: + name: htadmin-deployment + spec: + containers: + - name: htadmin-container + image: ghcr.io/idaholab/malcolm/htadmin:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 80 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: auth-env + env: + - name: HTADMIN_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/20-pcap-capture.yml b/kubernetes/20-pcap-capture.yml new file mode 100644 index 000000000..79ebe450d --- /dev/null +++ b/kubernetes/20-pcap-capture.yml @@ -0,0 +1,32 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pcap-capture-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: pcap-capture-deployment + replicas: 1 + template: + metadata: + labels: + name: pcap-capture-deployment + spec: + containers: + - name: pcap-capture-container + image: ghcr.io/idaholab/malcolm/pcap-capture:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: pcap-capture-env + env: + - name: PCAP_CAPTURE_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/21-zeek-live.yml b/kubernetes/21-zeek-live.yml new file mode 100644 index 000000000..c7f16eb8a --- /dev/null +++ b/kubernetes/21-zeek-live.yml @@ -0,0 +1,38 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zeek-live-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: zeek-live-deployment + replicas: 1 + template: + metadata: + labels: + name: zeek-live-deployment + spec: + containers: + - name: zeek-live-container + image: ghcr.io/idaholab/malcolm/zeek:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: common-upload-env + - configMapRef: + name: zeek-env + - configMapRef: + name: zeek-live-env + - configMapRef: + name: pcap-capture-env + env: + - name: ZEEK_LIVE_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/22-suricata-live.yml b/kubernetes/22-suricata-live.yml new file mode 100644 index 000000000..1eb60635a --- /dev/null +++ b/kubernetes/22-suricata-live.yml @@ -0,0 +1,38 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: suricata-live-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: suricata-live-deployment + replicas: 1 + template: + metadata: + labels: + name: suricata-live-deployment + spec: + containers: + - name: suricata-live-container + image: ghcr.io/idaholab/malcolm/suricata:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: common-upload-env + - configMapRef: + name: suricata-env + - configMapRef: + name: suricata-live-env + - configMapRef: + name: pcap-capture-env + env: + - name: SURICATA_LIVE_DISABLED + value: "true" \ No newline at end of file diff --git a/kubernetes/23-freq.yml b/kubernetes/23-freq.yml new file mode 100644 index 000000000..e80133663 --- /dev/null +++ b/kubernetes/23-freq.yml @@ -0,0 +1,44 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: freq + namespace: malcolm +spec: + ports: + - port: 10004 + protocol: TCP + selector: + name: freq-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: freq-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: freq-deployment + replicas: 1 + template: + metadata: + labels: + name: freq-deployment + spec: + containers: + - name: freq-container + image: ghcr.io/idaholab/malcolm/freq:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 10004 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: common-lookup-env diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml new file mode 100644 index 000000000..1d4eac8c1 --- /dev/null +++ b/kubernetes/99-nginx-proxy.yml @@ -0,0 +1,80 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx-proxy + namespace: malcolm +spec: + ports: + - port: 443 + protocol: TCP + selector: + name: nginx-proxy-deployment + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-proxy-deployment + namespace: malcolm +spec: + selector: + matchLabels: + name: nginx-proxy-deployment + replicas: 1 + template: + metadata: + labels: + name: nginx-proxy-deployment + spec: + containers: + - name: nginx-proxy-container + image: ghcr.io/idaholab/malcolm/nginx-proxy:kubernetes + imagePullPolicy: Always + stdin: false + tty: true + ports: + - containerPort: 443 + envFrom: + - configMapRef: + name: process-env + - configMapRef: + name: ssl-env + - configMapRef: + name: auth-env + - configMapRef: + name: nginx-env + livenessProbe: + exec: + command: + - curl + - --insecure + - --silent + - https://localhost:443 + initialDelaySeconds: 120 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 + volumeMounts: + - name: etc-nginx-volume + mountPath: /etc/nginx/configmap + - name: var-local-catrust-volume + mountPath: /var/local/ca-trust/configmap + - name: etc-nginx-certs-volume + mountPath: /etc/nginx/certs/configmap + - name: etc-nginx-certs-pem-volume + mountPath: /etc/nginx/dhparam/configmap + volumes: + - name: etc-nginx-volume + configMap: + name: etc-nginx + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: etc-nginx-certs-volume + configMap: + name: etc-nginx-certs + - name: etc-nginx-certs-pem-volume + configMap: + name: etc-nginx-certs-pem diff --git a/kubernetes/arkime.env b/kubernetes/arkime.env new file mode 100644 index 000000000..5eea566bb --- /dev/null +++ b/kubernetes/arkime.env @@ -0,0 +1,8 @@ +# Whether or not Arkime is allowed to delete uploaded/captured PCAP (see +# https://arkime.com/faq#pcap-deletion) +MANAGE_PCAP_FILES=false +# The number of Arkime capture processes allowed to run concurrently +ARKIME_ANALYZE_PCAP_THREADS=1 +# MaxMind GeoIP database update API key (see +# https://support.maxmind.com/hc/en-us/articles/4407116112539-Using-License-Keys) +MAXMIND_GEOIP_DB_LICENSE_KEY=0 diff --git a/kubernetes/auth.env b/kubernetes/auth.env new file mode 100644 index 000000000..0251d796f --- /dev/null +++ b/kubernetes/auth.env @@ -0,0 +1,13 @@ +# authentication method: encrypted HTTP basic authentication ('true') vs LDAP ('false') +NGINX_BASIC_AUTH=true +# NGINX LDAP (NGINX_BASIC_AUTH=false) can support LDAP, LDAPS, or LDAP+StartTLS. +# For StartTLS, set NGINX_LDAP_TLS_STUNNEL=true to issue the StartTLS command +# and use stunnel to tunnel the connection. +NGINX_LDAP_TLS_STUNNEL=false +# stunnel will require and verify certificates for StartTLS when one or more +# trusted CA certificate files are placed in the ./nginx/ca-trust directory. +# For additional security, hostname or IP address checking of the associated +# CA certificate(s) can be enabled by providing these values. +NGINX_LDAP_TLS_STUNNEL_CHECK_HOST= +NGINX_LDAP_TLS_STUNNEL_CHECK_IP= +NGINX_LDAP_TLS_STUNNEL_VERIFY_LEVEL=2 \ No newline at end of file diff --git a/kubernetes/common-beats.env b/kubernetes/common-beats.env new file mode 100644 index 000000000..2158d7502 --- /dev/null +++ b/kubernetes/common-beats.env @@ -0,0 +1,3 @@ +# Whether or not Logstash will use require encrypted communications for any external +# Beats-based forwarders from which it will accept logs +BEATS_SSL=true diff --git a/kubernetes/common-lookup.env b/kubernetes/common-lookup.env new file mode 100644 index 000000000..d31312cc5 --- /dev/null +++ b/kubernetes/common-lookup.env @@ -0,0 +1,16 @@ +# Whether or not domain names (from DNS queries and SSL server names) will be assigned entropy scores +# as calculated by freq +FREQ_LOOKUP=true +# When severity scoring is enabled, this variable indicates the entropy threshold for +# assigning severity to events with entropy scores calculated by freq; +# a lower value will only assign severity scores to fewer domain names with higher entropy +FREQ_SEVERITY_THRESHOLD=2.0 +# When severity scoring is enabled, this variable indicates the size threshold (in megabytes) +# for assigning severity to large connections or file transfers +TOTAL_MEGABYTES_SEVERITY_THRESHOLD=1000 +# When severity scoring is enabled, this variable indicates the duration threshold (in seconds) +# for assigning severity to long connections +CONNECTION_SECONDS_SEVERITY_THRESHOLD=3600 +# When severity scoring is enabled, this variable defines a comma-separated list of +# sensitive countries (using ISO 3166-1 alpha-2 codes) +SENSITIVE_COUNTRY_CODES=AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ diff --git a/kubernetes/common-upload.env b/kubernetes/common-upload.env new file mode 100644 index 000000000..edc9d577d --- /dev/null +++ b/kubernetes/common-upload.env @@ -0,0 +1,21 @@ +# Whether or not to automatically apply tags based (on the PCAP filename) to network traffic metadata +# parsed from uploaded PCAP files +AUTO_TAG=true +# The node name (e.g., the hostname of this machine running Malcolm) to associate with +# network traffic metadata +PCAP_NODE_NAME=malcolm +# Whether or not to enable debug output for processing uploaded/captured PCAP files +PCAP_PIPELINE_DEBUG=false +# Whether or not to enable very verbose debug output for processing uploaded/captured PCAP files +PCAP_PIPELINE_DEBUG_EXTRA=false +# Whether or not PCAP files extant in ./pcap/ will be ignored on startup +PCAP_PIPELINE_IGNORE_PREEXISTING=false +# 'pcap-monitor' to match the name of the container providing the uploaded/captured PCAP file +# monitoring service +PCAP_MONITOR_HOST=pcap-monitor +# The age (in minutes) at which already-processed log files containing network traffic metadata should +# be pruned from the filesystem +LOG_CLEANUP_MINUTES=360 +# The age (in minutes) at which the compressed archives containing already-processed log files should +# be pruned from the filesystem +ZIP_CLEANUP_MINUTES=720 \ No newline at end of file diff --git a/kubernetes/dashboards-helper.env b/kubernetes/dashboards-helper.env new file mode 100644 index 000000000..5a9474e8b --- /dev/null +++ b/kubernetes/dashboards-helper.env @@ -0,0 +1,12 @@ +# Whether or not to set OpenSearch Dashboards to dark mode +DASHBOARDS_DARKMODE=true +# The maximum cumulative size of OpenSearch indices containing network traffic metadata +# (arkime_sessions3-*) before which the oldest indices will be deleted ('' to disable +# storage-based index pruning). +OPENSEARCH_INDEX_SIZE_PRUNE_LIMIT=0 +# Whether to determine the "oldest" indices for storage-based index pruning by creation +# date/time ('true') or index name ('false') +OPENSEARCH_INDEX_SIZE_PRUNE_NAME_SORT=false +# Parameters for the OpenSearch repository used for index snapshots +ISM_SNAPSHOT_COMPRESSED=false +ISM_SNAPSHOT_REPO=logs \ No newline at end of file diff --git a/kubernetes/filebeat.env b/kubernetes/filebeat.env new file mode 100644 index 000000000..6745b7e97 --- /dev/null +++ b/kubernetes/filebeat.env @@ -0,0 +1,26 @@ +# filebeat parameters used for monitoring log files containing network traffic metadata +# (see https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-log.html) +FILEBEAT_SCAN_FREQUENCY=10s +FILEBEAT_CLEAN_INACTIVE=180m +FILEBEAT_IGNORE_OLDER=120m +FILEBEAT_CLOSE_INACTIVE=120s +FILEBEAT_CLOSE_INACTIVE_LIVE=90m +FILEBEAT_CLOSE_RENAMED=true +FILEBEAT_CLOSE_REMOVED=true +FILEBEAT_CLOSE_EOF=true +FILEBEAT_CLEAN_REMOVED=true +# Whether or not to expose a filebeat TCP input listener (see +# https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-tcp.html) +FILEBEAT_TCP_LISTEN=false +# Log format expected for events sent to the filebeat TCP input listener ('json' or 'raw') +FILEBEAT_TCP_LOG_FORMAT=raw +# Source field name to parse (when FILEBEAT_TCP_LOG_FORMAT is 'json') for events sent to the +# filebeat TCP input listener +FILEBEAT_TCP_PARSE_SOURCE_FIELD=message +# Target field name to store decoded JSON fields (when FILEBEAT_TCP_LOG_FORMAT is 'json') for +# events sent to the filebeat TCP input listener +FILEBEAT_TCP_PARSE_TARGET_FIELD= +# Name of field to drop (if it exists) in events sent to the filebeat TCP input listener +FILEBEAT_TCP_PARSE_DROP_FIELD= +# Tag to append to events sent to the filebeat TCP input listener +FILEBEAT_TCP_TAG=_malcolm_beats \ No newline at end of file diff --git a/kubernetes/logstash.env b/kubernetes/logstash.env new file mode 100644 index 000000000..5eff3eabe --- /dev/null +++ b/kubernetes/logstash.env @@ -0,0 +1,15 @@ +# Parameters for tuning Logstash pipelines (see +# https://www.elastic.co/guide/en/logstash/current/logstash-settings-file.html) +pipeline.workers=3 +pipeline.batch.size=75 +pipeline.batch.delay=50 +# Whether or not Logstash will map MAC addresses to vendors for MAC addresses +LOGSTASH_OUI_LOOKUP=true +# Whether or not Logstash will perform severity scoring on network traffic metadata +LOGSTASH_SEVERITY_SCORING=true +# Whether or not Logstash will perform a reverse DNS lookup for external IP addresses +LOGSTASH_REVERSE_DNS=false +# Whether or not Logstash will enrich network traffic metadata directly from net-map.json +LOGSTASH_NETWORK_MAP_ENRICHMENT=true +# Whether or not Logstash will enrich network traffic metadata via NetBox API calls +LOGSTASH_NETBOX_ENRICHMENT=false \ No newline at end of file diff --git a/kubernetes/netbox.env b/kubernetes/netbox.env new file mode 100644 index 000000000..aff0c64fa --- /dev/null +++ b/kubernetes/netbox.env @@ -0,0 +1,15 @@ +# Parameters related to NetBox (and supporting tools). Note that other more specific parameters +# can also be configured in the env_file files for netbox* services +# The name of the default "site" to be created upon NetBox initialization, and to be queried +# for enrichment (see LOGSTASH_NETBOX_ENRICHMENT) +NETBOX_DEFAULT_SITE=Malcolm +# Whether to disable Malcolm's NetBox instance ('true') or not ('false') +NETBOX_DISABLED=true +NETBOX_POSTGRES_DISABLED=true +NETBOX_REDIS_DISABLED=true +NETBOX_REDIS_CACHE_DISABLED=true +# Whether or not to periodically query network traffic metadata and use it to populate NetBox +NETBOX_CRON=false +# If using the NetBox interface to create API tokens, set this +# (see https://docs.djangoproject.com/en/4.1/ref/settings/#csrf-trusted-origins) +# CSRF_TRUSTED_ORIGINS=https://malcolm.example.org \ No newline at end of file diff --git a/kubernetes/nginx.env b/kubernetes/nginx.env new file mode 100644 index 000000000..1d36a0782 --- /dev/null +++ b/kubernetes/nginx.env @@ -0,0 +1,9 @@ +# Whether or not nginx should use HTTPS. This is almost CERTAINLY what you want. +# The only case you may want to set this to false is if you're using another +# reverse proxy in front of Malcolm. Even if set to 'false', NGINX will still +# listen on port 443 (it just won't be encrypted). If you change this, you'll +# probably want to change "0.0.0.0:443:443" to something like +# "127.0.0.1:80:443" in the ports section for the nginx-proxy service. +NGINX_SSL=true +# Whether or not to write nginx's access.log and error.log to OpenSearch +NGINX_LOG_ACCESS_AND_ERRORS=false \ No newline at end of file diff --git a/kubernetes/opensearch.env b/kubernetes/opensearch.env new file mode 100644 index 000000000..c0ffeceb2 --- /dev/null +++ b/kubernetes/opensearch.env @@ -0,0 +1,38 @@ +# Used in various services to define the connection to the OpenSearch document store. +# Whether or not Malcolm will start and use its own local OpenSearch instance as its +# primary data store. Set to 'false' if you're connecting to another OpenSearch +# cluster, in which case the other environment variables in this section must also +# be set with the connection parameters. +OPENSEARCH_LOCAL=true +# URL for connecting to OpenSearch instance. When using Malcolm's internal instance +# of OpenSearch (i.e., OPENSEARCH_LOCAL is 'true') this should be +# 'http://opensearch:9200', otherwise specify the primary remote instance URL +# in the format 'protocol://host:port'. +OPENSEARCH_URL=http://opensearch:9200 +# Used when OPENSEARCH_LOCAL is 'false', the cURL-formatted config file contains login +# credentials for the primary OpenSearch instance. It can be generated for you by the +# ./scripts/auth_setup script. The notable parameters expected from this file would be +# user (with a "user:password" value) and "insecure" (if the certificate verification +# setting below is 'false'). See cURL config file format at +# https://everything.curl.dev/cmdline/configfile. This file is bind mounted locally +# from .opensearch.primary.curlrc as /var/local/opensearch.primary.curlrc +OPENSEARCH_CREDS_CONFIG_FILE=/var/local/opensearch.primary.curlrc +# Whether or not connections to the primary remote OpenSearch instance require full +# TLS certificate validation for the connection (this may fail if using self-signed +# certificates). +OPENSEARCH_SSL_CERTIFICATE_VERIFICATION=false +# Whether or not Malcolm's Logstash instance will forward logs to a secondary remote +# OpenSearch instance in addition to the (local or remote) primary instance. +OPENSEARCH_SECONDARY=false +# URL for connecting to the secondary remote OpenSearch instance, specified +# in the format 'protocol://host:port'. +OPENSEARCH_SECONDARY_URL= +# Used when OPENSEARCH_SECONDARY is 'true', the cURL-formatted config file contains login +# credentials for the secondary OpenSearch instance. The comments describing +# OPENSEARCH_CREDS_CONFIG_FILE above also apply here. This file is bind mounted locally +# from .opensearch.secondary.curlrc as /var/local/opensearch.secondary.curlrc +OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE=/var/local/opensearch.secondary.curlrc +# Whether or not connections to the secondary remote OpenSearch instance require full +# TLS certificate validation for the connection (this may fail if using self-signed +# certificates). +OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION=false diff --git a/kubernetes/pcap-capture.env b/kubernetes/pcap-capture.env new file mode 100644 index 000000000..997bf2b8b --- /dev/null +++ b/kubernetes/pcap-capture.env @@ -0,0 +1,23 @@ +# Whether or not netsniff-ng should create PCAP files from live traffic on a local +# interface for analysis by Arkime capture (should be 'false' if PCAP_ENABLE_TCPDUMP +# is 'true') +PCAP_ENABLE_NETSNIFF=false +# Whether or not tcpdump should create PCAP files from live traffic on a local +# interface for analysis by Arkime capture (should be 'false' if PCAP_ENABLE_NETSNIFF +# is 'true') +PCAP_ENABLE_TCPDUMP=false +# Specifies local network interface(s) for local packet capture if PCAP_ENABLE_NETSNIFF, +# PCAP_ENABLE_TCPDUMP, ZEEK_LIVE_CAPTURE or SURICATA_LIVE_CAPTURE are 'true +PCAP_IFACE=lo +# Whether or not ethtool will disable NIC hardware offloading features and adjust +# ring buffer sizes for capture interface(s) (should be 'true' if the interface(s) are +# being used for capture only, 'false' if they are being used for management/communication) +PCAP_IFACE_TWEAK=false +# Specifies how large a locally-captured PCAP file can become (in megabytes) before +# it is closed for processing and a new PCAP file created +PCAP_ROTATE_MEGABYTES=4096 +# Specifies a time interval (in minutes) after which a locally-captured PCAP file +# will be closed for processing and a new PCAP file created +PCAP_ROTATE_MINUTES=10 +# Specifies a tcpdump-style filter expression for local packet capture ('' to capture all traffic) +PCAP_FILTER= \ No newline at end of file diff --git a/kubernetes/process.env b/kubernetes/process.env new file mode 100644 index 000000000..ca0a1923f --- /dev/null +++ b/kubernetes/process.env @@ -0,0 +1,7 @@ +# docker containers will run processes as unprivileged user with UID:GID +PUID=1000 +PGID=1000 +# for debugging container init via tini (https://github.com/krallin/tini) +TINI_VERBOSITY=1 +# for handling configmap files/directories +CONFIG_MAP_DIR=configmap \ No newline at end of file diff --git a/kubernetes/ssl.env b/kubernetes/ssl.env new file mode 100644 index 000000000..44a500741 --- /dev/null +++ b/kubernetes/ssl.env @@ -0,0 +1,3 @@ +# When possible, docker containers will automatically add trusted CA certificate files +# found in the ./nginx/ca-trust directory (which is bind mounted to /ca-trust). +PUSER_CA_TRUST=/var/local/ca-trust \ No newline at end of file diff --git a/kubernetes/suricata-live.env b/kubernetes/suricata-live.env new file mode 100644 index 000000000..ce27ffc52 --- /dev/null +++ b/kubernetes/suricata-live.env @@ -0,0 +1,6 @@ +# Whether or not Suricata should monitor live traffic on a local +# interface (PCAP_IFACE variable below specifies capture interfaces) +SURICATA_LIVE_CAPTURE=false +# Specifies the Suricata runmode for live capture (see +# https://suricata.readthedocs.io/en/latest/performance/runmodes.html) +SURICATA_RUNMODE=workers \ No newline at end of file diff --git a/kubernetes/suricata-offline.env b/kubernetes/suricata-offline.env new file mode 100644 index 000000000..318150c8f --- /dev/null +++ b/kubernetes/suricata-offline.env @@ -0,0 +1,10 @@ +# Whether or not Suricata should analyze uploaded PCAP files +SURICATA_AUTO_ANALYZE_PCAP_FILES=true +# The number of Suricata processes for analyzing uploaded PCAP files allowed +# to run concurrently +SURICATA_AUTO_ANALYZE_PCAP_THREADS=1 +# Whether or not Suricata should analyze captured PCAP files captured +# by netsniff-ng/tcpdump (see PCAP_ENABLE_NETSNIFF and PCAP_ENABLE_TCPDUMP +# below). If SURICATA_LIVE_CAPTURE is true, this should be false: otherwise +# Suricata will see duplicate traffic. +SURICATA_ROTATED_PCAP=true \ No newline at end of file diff --git a/kubernetes/suricata.env b/kubernetes/suricata.env new file mode 100644 index 000000000..a1dcad99a --- /dev/null +++ b/kubernetes/suricata.env @@ -0,0 +1,10 @@ +# Whether or not the default Suricata ruleset will be ignored and only custom rules used +SURICATA_CUSTOM_RULES_ONLY=false +SURICATA_UPDATE_RULES=false +SURICATA_UPDATE_DEBUG=false +SURICATA_UPDATE_ETOPEN=true +# suricata_config_populate.py can use MANY more environment variables to tweak +# suricata.yaml (see https://github.com/OISF/suricata/blob/master/suricata.yaml.in and +# https://suricata.readthedocs.io/en/latest/configuration/suricata-yaml.html). +# DEFAULT_VARS in that script defines those variables (albeit without the +# required `SURICATA_` prefixing each) diff --git a/kubernetes/zeek-live.env b/kubernetes/zeek-live.env new file mode 100644 index 000000000..04c81dd29 --- /dev/null +++ b/kubernetes/zeek-live.env @@ -0,0 +1,3 @@ +# Whether or not Zeek should monitor live traffic on a local +# interface (PCAP_IFACE variable below specifies capture interfaces) +ZEEK_LIVE_CAPTURE=false diff --git a/kubernetes/zeek-offline.env b/kubernetes/zeek-offline.env new file mode 100644 index 000000000..10e0ed363 --- /dev/null +++ b/kubernetes/zeek-offline.env @@ -0,0 +1,10 @@ +# Whether or not Zeek should analyze uploaded PCAP files +ZEEK_AUTO_ANALYZE_PCAP_FILES=true +# The number of Zeek processes for analyzing uploaded PCAP files allowed +# to run concurrently +ZEEK_AUTO_ANALYZE_PCAP_THREADS=1 +# Whether or not Zeek should analyze captured PCAP files captured +# by netsniff-ng/tcpdump (see PCAP_ENABLE_NETSNIFF and PCAP_ENABLE_TCPDUMP +# below). If ZEEK_LIVE_CAPTURE is true, this should be false: otherwise +# Zeek will see duplicate traffic. +ZEEK_ROTATED_PCAP=true \ No newline at end of file diff --git a/kubernetes/zeek.env b/kubernetes/zeek.env new file mode 100644 index 000000000..cdf8139a5 --- /dev/null +++ b/kubernetes/zeek.env @@ -0,0 +1,65 @@ +# Specifies the value for Zeek's Intel::item_expiration timeout (-1min to disable) +ZEEK_INTEL_ITEM_EXPIRATION=-1min +# When querying a TAXII or MISP feed, only process threat indicators that have +# been created or modified since the time represented by this value; +# it may be either a fixed date/time (01/01/2021) or relative interval (30 days ago) +ZEEK_INTEL_FEED_SINCE= +# Specifies a cron expression indicating the refresh interval for generating the +# Zeek Intelligence Framework files ('' disables automatic refresh) +ZEEK_INTEL_REFRESH_CRON_EXPRESSION= +# Determines the file extraction behavior for file transfers detected by Zeek +ZEEK_EXTRACTOR_MODE=none +# Whether or not files extant in ./zeek-logs/extract_files/ will be ignored on startup +EXTRACTED_FILE_IGNORE_EXISTING=false +# Determines the behavior for preservation of Zeek-extracted files +EXTRACTED_FILE_PRESERVATION=quarantined +# The minimum size (in bytes) for files to be extracted by Zeek +EXTRACTED_FILE_MIN_BYTES=64 +# The maximum size (in bytes) for files to be extracted by Zeek +EXTRACTED_FILE_MAX_BYTES=134217728 +# A VirusTotal Public API v.20 used to submit hashes of Zeek-extracted files +VTOT_API2_KEY=0 +# Rate limiting for VirusTotal, ClamAV, YARA and capa with Zeek-extracted files +VTOT_REQUESTS_PER_MINUTE=4 +CLAMD_MAX_REQUESTS=8 +YARA_MAX_REQUESTS=8 +CAPA_MAX_REQUESTS=4 +# Whether or not YARA will scan Zeek-extracted files +EXTRACTED_FILE_ENABLE_YARA=false +# Whether or not the default YARA ruleset will be ignored and only custom rules used +EXTRACTED_FILE_YARA_CUSTOM_ONLY=false +# Whether or not capa will scan Zeek-extracted executables +EXTRACTED_FILE_ENABLE_CAPA=false +# Whether or not capa will be extra verbose +EXTRACTED_FILE_CAPA_VERBOSE=false +# Whether or not ClamAV will scan Zeek-extracted executables +EXTRACTED_FILE_ENABLE_CLAMAV=false +# Whether or not to regularly update rule definitions for file scanning engines +EXTRACTED_FILE_UPDATE_RULES=false +# Whether or not to enable debug output for Zeek-extracted file scanning +EXTRACTED_FILE_PIPELINE_DEBUG=false +# Whether or not to enable very verbose debug output for Zeek-extracted file scanning +EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA=false +# Whether or not to serve the directory containing Zeek-extracted over HTTP at ./extracted-files/ +EXTRACTED_FILE_HTTP_SERVER_ENABLE=false +# Whether or not Zeek-extracted files served over HTTP will be AES-256-CBC-encrypted +EXTRACTED_FILE_HTTP_SERVER_ENCRYPT=true +# Specifies the AES-256-CBC decryption password for encrypted Zeek-extracted files served over HTTP +EXTRACTED_FILE_HTTP_SERVER_KEY=quarantined +# Environment variables for tweaking Zeek at runtime (see local.zeek) +# Set to any non-blank value to disable the corresponding feature +ZEEK_DISABLE_HASH_ALL_FILES= +ZEEK_DISABLE_LOG_PASSWORDS= +ZEEK_DISABLE_SSL_VALIDATE_CERTS= +ZEEK_DISABLE_TRACK_ALL_ASSETS= +ZEEK_DISABLE_BEST_GUESS_ICS=true +ZEEK_DISABLE_SPICY_DHCP=true +ZEEK_DISABLE_SPICY_DNS=true +ZEEK_DISABLE_SPICY_HTTP=true +ZEEK_DISABLE_SPICY_IPSEC= +ZEEK_DISABLE_SPICY_LDAP= +ZEEK_DISABLE_SPICY_OPENVPN= +ZEEK_DISABLE_SPICY_STUN= +ZEEK_DISABLE_SPICY_TAILSCALE= +ZEEK_DISABLE_SPICY_TFTP= +ZEEK_DISABLE_SPICY_WIREGUARD= \ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf index e1c65df2e..d73d9c868 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -29,61 +29,61 @@ http { # if LDAP authentication is enabled, this will configure the ldap_server section include /etc/nginx/nginx_ldap_rt.conf; -## upstream arkime { -## server arkime:8005; -## } -## -## upstream api { -## server api:5000; -## } -## -## upstream upload { -## server upload:80; -## } -## -## upstream htadmin { -## server htadmin:80; -## } -## -## upstream dashboards { -## server dashboards:5601; -## } -## -## upstream dashboards-maps { -## server dashboards-helper:28991; -## } -## -## upstream opensearch { -## server opensearch:9200; -## } -## -## upstream logstash-stats { -## server logstash:9600; -## } -## -## upstream name-map-ui { -## server name-map-ui:8080; -## } -## -## upstream netbox { -## server netbox:8080; -## } -## -## upstream extracted-file-http-server { -## server file-monitor:8440; -## } + upstream arkime { + server arkime:8005; + } + + upstream api { + server api:5000; + } + + upstream upload { + server upload:80; + } + + upstream htadmin { + server htadmin:80; + } + + upstream dashboards { + server dashboards:5601; + } + + upstream dashboards-maps { + server dashboards-helper:28991; + } + + upstream opensearch { + server opensearch:9200; + } + + upstream logstash-stats { + server logstash:9600; + } + + upstream name-map-ui { + server name-map-ui:8080; + } + + upstream netbox { + server netbox:8080; + } + + upstream extracted-file-http-server { + server file-monitor:8440; + } # htadmin (htpasswd/user management) -## server { -## listen 488; -## include /etc/nginx/nginx_ssl_config.conf; -## -## location / { -## proxy_pass http://htadmin; -## proxy_redirect off; -## proxy_set_header Host htadmin.malcolm.local; -## } -## } + server { + listen 488; + include /etc/nginx/nginx_ssl_config.conf; + + location / { + proxy_pass http://htadmin; + proxy_redirect off; + proxy_set_header Host htadmin.malcolm.local; + } + } # Main web interface server { @@ -99,224 +99,222 @@ http { try_files $uri $uri/index.html; } -## # Malcolm file upload -## location /upload { -## proxy_http_version 1.1; -## proxy_set_header Connection ""; -## proxy_pass http://upload/; -## proxy_redirect off; -## proxy_set_header Host upload.malcolm.local; -## proxy_request_buffering off; -## proxy_buffering off; -## client_max_body_size 50G; -## } -## location /server/php { -## proxy_http_version 1.1; -## proxy_set_header Connection ""; -## proxy_pass http://upload/server/php/; -## proxy_redirect off; -## proxy_set_header Host upload.malcolm.local; -## proxy_request_buffering off; -## proxy_buffering off; -## client_max_body_size 50G; -## } -## -## # Logstash statistics -## location ~* ^/logstash\b(.*) { -## proxy_pass http://logstash-stats/_node/stats$1; -## proxy_redirect off; -## proxy_set_header Host arkime.malcolm.local; -## } -## -## # Arkime -> Dashboards shortcut -## location ~* ^/idark2dash(.*) { -## -## set $filter_start_time now-1d; -## if ($arg_start != '') { -## set $filter_start_time \'$arg_start\'; -## } -## -## set $filter_stop_time now; -## if ($arg_stop != '') { -## set $filter_stop_time \'$arg_stop\'; -## } -## -## set $filter_field undefined; -## if ($arg_field != '') { -## set $filter_field $arg_field; -## } -## -## set $filter_value undefined; -## if ($arg_value != '') { -## set $filter_value $arg_value; -## } -## -## rewrite ^/idark2dash/(.*) /dashboards/app/discover#/?_g=(refreshInterval:(pause:!t,value:0),time:(from:$filter_start_time,mode:absolute,to:$filter_stop_time))&_a=(columns:!(_source),filters:!((meta:(alias:!n,disabled:!f,index:'sessions2-*',key:$filter_field,negate:!f,params:(query:'$filter_value',type:phrase),type:phrase,value:'$filter_value'),query:(match:($filter_field:(query:'$filter_value',type:phrase))))),index:'sessions2-*',interval:auto,query:(language:lucene,query:''),sort:!(firstPacket,desc)) redirect; -## proxy_pass http://dashboards; -## proxy_redirect off; -## proxy_set_header Host dashboards.malcolm.local; -## } -## -## # Dashboards -> Arkime shortcut -## location ~* /iddash2ark/(.*) { -## rewrite ^.*/iddash2ark/(.*) /sessions?expression=($1) redirect; -## proxy_pass https://arkime; -## proxy_ssl_verify off; -## proxy_redirect off; -## proxy_set_header Host arkime.malcolm.local; -## proxy_set_header http_auth_http_user $authenticated_user; -## proxy_set_header Authorization ""; -## } -## -## # Dashboards/Arkime -> extracted file download -## location ~* /dl-extracted-files/(.*) { -## rewrite ^.*/dl-extracted-files/(.*) /extracted-files/$1 redirect; -## proxy_pass http://extracted-file-http-server; -## proxy_redirect off; -## proxy_set_header Host file-monitor.malcolm.local; -## } -## -## # already prepended /dashboards to match the server.basePath in OpenSearch Dashboards's YML config file -## location /dashboards { -## proxy_pass http://dashboards; -## proxy_redirect off; -## proxy_set_header Host dashboards.malcolm.local; -## } -## -## # offline region maps for dashboards -## location /world.geojson { -## proxy_pass http://dashboards-maps; -## proxy_redirect off; -## proxy_set_header Host dashboards-helper.malcolm.local; -## } -## -## # name-map-ui (UI for mapping names to network hosts and subnets) -## location /name-map-ui { -## proxy_pass http://name-map-ui/; -## proxy_redirect off; -## proxy_set_header Host name-map-ui.malcolm.local; -## proxy_cache off; -## } -## -## location ~* ^/extracted-files\b(.*) { -## proxy_pass http://extracted-file-http-server$1; -## proxy_redirect off; -## proxy_set_header Host file-monitor.malcolm.local; -## } -## -## # netbox -## location /netbox { -## proxy_pass http://netbox; -## proxy_redirect off; -## proxy_set_header Host netbox.malcolm.local; -## proxy_set_header X-Forwarded-Host $http_host; -## proxy_set_header X-Real-IP $remote_addr; -## proxy_set_header X-Forwarded-Proto $scheme; -## proxy_set_header X-Remote-Auth $authenticated_user; -## } -## -## # favicon, logos, banners, etc. -## include /etc/nginx/nginx_image_aliases.conf; -## -## # Fix cyberchef JS module(s) -## # https://localhost/arkime/session/190924-KgO9H30qhdREw7ltsDXn1Rgp/modules/Regex.js -## location ~* ^/arkime/session/.*/(modules/.*\.js) { -## proxy_hide_header Content-Type; -## proxy_set_header Content-Type "application/javascript"; -## add_header Content-Type "application/javascript"; -## default_type application/javascript; -## add_header X-Content-Type-Options 'nosniff'; -## proxy_pass https://arkime/cyberchef/$1; -## proxy_ssl_verify off; -## proxy_redirect off; -## proxy_set_header Host arkime.malcolm.local; -## proxy_set_header http_auth_http_user $authenticated_user; -## proxy_set_header Authorization ""; -## } -## -## # Malcolm API -## location /mapi { -## proxy_pass http://api/; -## proxy_redirect off; -## proxy_set_header Host api.malcolm.local; -## } + # Malcolm file upload + location /upload { + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_pass http://upload/; + proxy_redirect off; + proxy_set_header Host upload.malcolm.local; + proxy_request_buffering off; + proxy_buffering off; + client_max_body_size 50G; + } + location /server/php { + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_pass http://upload/server/php/; + proxy_redirect off; + proxy_set_header Host upload.malcolm.local; + proxy_request_buffering off; + proxy_buffering off; + client_max_body_size 50G; + } + + # Logstash statistics + location ~* ^/logstash\b(.*) { + proxy_pass http://logstash-stats/_node/stats$1; + proxy_redirect off; + proxy_set_header Host arkime.malcolm.local; + } + + # Arkime -> Dashboards shortcut + location ~* ^/idark2dash(.*) { + + set $filter_start_time now-1d; + if ($arg_start != '') { + set $filter_start_time \'$arg_start\'; + } + + set $filter_stop_time now; + if ($arg_stop != '') { + set $filter_stop_time \'$arg_stop\'; + } + + set $filter_field undefined; + if ($arg_field != '') { + set $filter_field $arg_field; + } + + set $filter_value undefined; + if ($arg_value != '') { + set $filter_value $arg_value; + } + + rewrite ^/idark2dash/(.*) /dashboards/app/discover#/?_g=(refreshInterval:(pause:!t,value:0),time:(from:$filter_start_time,mode:absolute,to:$filter_stop_time))&_a=(columns:!(_source),filters:!((meta:(alias:!n,disabled:!f,index:'sessions2-*',key:$filter_field,negate:!f,params:(query:'$filter_value',type:phrase),type:phrase,value:'$filter_value'),query:(match:($filter_field:(query:'$filter_value',type:phrase))))),index:'sessions2-*',interval:auto,query:(language:lucene,query:''),sort:!(firstPacket,desc)) redirect; + proxy_pass http://dashboards; + proxy_redirect off; + proxy_set_header Host dashboards.malcolm.local; + } + + # Dashboards -> Arkime shortcut + location ~* /iddash2ark/(.*) { + rewrite ^.*/iddash2ark/(.*) /sessions?expression=($1) redirect; + proxy_pass https://arkime; + proxy_ssl_verify off; + proxy_redirect off; + proxy_set_header Host arkime.malcolm.local; + proxy_set_header http_auth_http_user $authenticated_user; + proxy_set_header Authorization ""; + } + + # Dashboards/Arkime -> extracted file download + location ~* /dl-extracted-files/(.*) { + rewrite ^.*/dl-extracted-files/(.*) /extracted-files/$1 redirect; + proxy_pass http://extracted-file-http-server; + proxy_redirect off; + proxy_set_header Host file-monitor.malcolm.local; + } + + # already prepended /dashboards to match the server.basePath in OpenSearch Dashboards's YML config file + location /dashboards { + proxy_pass http://dashboards; + proxy_redirect off; + proxy_set_header Host dashboards.malcolm.local; + } + + # offline region maps for dashboards + location /world.geojson { + proxy_pass http://dashboards-maps; + proxy_redirect off; + proxy_set_header Host dashboards-helper.malcolm.local; + } + + # name-map-ui (UI for mapping names to network hosts and subnets) + location /name-map-ui { + proxy_pass http://name-map-ui/; + proxy_redirect off; + proxy_set_header Host name-map-ui.malcolm.local; + proxy_cache off; + } + + location ~* ^/extracted-files\b(.*) { + proxy_pass http://extracted-file-http-server$1; + proxy_redirect off; + proxy_set_header Host file-monitor.malcolm.local; + } + + # netbox + location /netbox { + proxy_pass http://netbox; + proxy_redirect off; + proxy_set_header Host netbox.malcolm.local; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Remote-Auth $authenticated_user; + } + + # favicon, logos, banners, etc. + include /etc/nginx/nginx_image_aliases.conf; + + # Fix cyberchef JS module(s) + # https://localhost/arkime/session/190924-KgO9H30qhdREw7ltsDXn1Rgp/modules/Regex.js + location ~* ^/arkime/session/.*/(modules/.*\.js) { + proxy_hide_header Content-Type; + proxy_set_header Content-Type "application/javascript"; + add_header Content-Type "application/javascript"; + default_type application/javascript; + add_header X-Content-Type-Options 'nosniff'; + proxy_pass https://arkime/cyberchef/$1; + proxy_ssl_verify off; + proxy_redirect off; + proxy_set_header Host arkime.malcolm.local; + proxy_set_header http_auth_http_user $authenticated_user; + proxy_set_header Authorization ""; + } + + # Malcolm API + location /mapi { + proxy_pass http://api/; + proxy_redirect off; + proxy_set_header Host api.malcolm.local; + } # Arkime location / { - default_type text/plain; - return 418 "418 I'm a teapot\n"; -## proxy_pass https://arkime; -## proxy_ssl_verify off; -## proxy_redirect off; -## proxy_set_header Host arkime.malcolm.local; -## proxy_set_header http_auth_http_user $authenticated_user; -## proxy_set_header Authorization ""; + proxy_pass https://arkime; + proxy_ssl_verify off; + proxy_redirect off; + proxy_set_header Host arkime.malcolm.local; + proxy_set_header http_auth_http_user $authenticated_user; + proxy_set_header Authorization ""; } } # OpenSearch dashboards interface -## server { -## listen 5601; -## include /etc/nginx/nginx_ssl_config.conf; -## -## # use either auth_basic or auth_ldap -## include /etc/nginx/nginx_auth_rt.conf; -## -## # favicon, logos, banners, etc. -## include /etc/nginx/nginx_image_aliases.conf; -## -## # Dashboards -> Arkime shortcut -## location ~* /iddash2ark/(.*) { -## rewrite ^.*/iddash2ark/(.*) /sessions?expression=($1) redirect; -## proxy_pass https://arkime; -## proxy_ssl_verify off; -## proxy_redirect off; -## proxy_set_header Host arkime.malcolm.local; -## proxy_set_header http_auth_http_user $authenticated_user; -## proxy_set_header Authorization ""; -## } -## -## # Dashboards -> extracted file download -## location ~* /dl-extracted-files/(.*) { -## rewrite ^.*/dl-extracted-files/(.*) /extracted-files/$1 redirect; -## proxy_pass http://extracted-file-http-server; -## proxy_redirect off; -## proxy_set_header Host file-monitor.malcolm.local; -## } -## -## # already prepended /dashboards to match the server.basePath in OpenSearch Dashboards' YML config file -## location /dashboards { -## proxy_pass http://dashboards; -## proxy_redirect off; -## proxy_set_header Host dashboards.malcolm.local; -## } -## -## # otherwise prepend /dashboards to match the server.basePath in OpenSearch Dashboards' YML config file -## location / { -## rewrite ^/(.*) /dashboards/$1; -## proxy_pass http://dashboards; -## proxy_redirect off; -## proxy_set_header Host dashboards.malcolm.local; -## } -## } + server { + listen 5601; + include /etc/nginx/nginx_ssl_config.conf; + + # use either auth_basic or auth_ldap + include /etc/nginx/nginx_auth_rt.conf; + + # favicon, logos, banners, etc. + include /etc/nginx/nginx_image_aliases.conf; + + # Dashboards -> Arkime shortcut + location ~* /iddash2ark/(.*) { + rewrite ^.*/iddash2ark/(.*) /sessions?expression=($1) redirect; + proxy_pass https://arkime; + proxy_ssl_verify off; + proxy_redirect off; + proxy_set_header Host arkime.malcolm.local; + proxy_set_header http_auth_http_user $authenticated_user; + proxy_set_header Authorization ""; + } + + # Dashboards -> extracted file download + location ~* /dl-extracted-files/(.*) { + rewrite ^.*/dl-extracted-files/(.*) /extracted-files/$1 redirect; + proxy_pass http://extracted-file-http-server; + proxy_redirect off; + proxy_set_header Host file-monitor.malcolm.local; + } + + # already prepended /dashboards to match the server.basePath in OpenSearch Dashboards' YML config file + location /dashboards { + proxy_pass http://dashboards; + proxy_redirect off; + proxy_set_header Host dashboards.malcolm.local; + } + + # otherwise prepend /dashboards to match the server.basePath in OpenSearch Dashboards' YML config file + location / { + rewrite ^/(.*) /dashboards/$1; + proxy_pass http://dashboards; + proxy_redirect off; + proxy_set_header Host dashboards.malcolm.local; + } + } # OpenSearch API -## server { -## listen 9200; -## include /etc/nginx/nginx_ssl_config.conf; -## -## # use either auth_basic or auth_ldap -## include /etc/nginx/nginx_auth_rt.conf; -## -## # favicon, logos, banners, etc. -## include /etc/nginx/nginx_image_aliases.conf; -## -## location / { -## proxy_pass http://opensearch; -## proxy_redirect off; -## proxy_set_header Host os.malcolm.local; -## client_max_body_size 50m; -## } -## } + server { + listen 9200; + include /etc/nginx/nginx_ssl_config.conf; + + # use either auth_basic or auth_ldap + include /etc/nginx/nginx_auth_rt.conf; + + # favicon, logos, banners, etc. + include /etc/nginx/nginx_image_aliases.conf; + + location / { + proxy_pass http://opensearch; + proxy_redirect off; + proxy_set_header Host os.malcolm.local; + client_max_body_size 50m; + } + } } From 8aa25e055d90620f1e32646b3c823aed21eeca51 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 20 Mar 2023 14:33:12 -0600 Subject: [PATCH 020/239] work in progress with services --- kubernetes/02-opensearch.yml | 4 +++- kubernetes/03-dashboards.yml | 2 ++ kubernetes/04-upload.yml | 4 +++- kubernetes/05-pcap-monitor.yml | 2 +- kubernetes/06-arkime.yml | 4 +++- kubernetes/07-api.yml | 4 +++- kubernetes/08-dashboards-helper.yml | 4 +++- kubernetes/09-zeek.yml | 2 +- kubernetes/10-suricata.yml | 2 +- kubernetes/11-file-monitor.yml | 4 +++- kubernetes/12-filebeat.yml | 2 +- kubernetes/13-logstash.yml | 2 +- kubernetes/14-name-map-ui.yml | 2 ++ kubernetes/15-netbox-redis.yml | 2 ++ kubernetes/16-netbox-redis-cache.yml | 2 ++ kubernetes/17-netbox-postgres.yml | 4 +++- kubernetes/18-netbox.yml | 2 ++ kubernetes/19-htadmin.yml | 4 +++- kubernetes/20-pcap-capture.yml | 2 +- kubernetes/21-zeek-live.yml | 2 +- kubernetes/22-suricata-live.yml | 2 +- kubernetes/23-freq.yml | 3 +++ 22 files changed, 45 insertions(+), 16 deletions(-) diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index 7005e7061..c5bcbe1ee 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -44,4 +44,6 @@ spec: name: opensearch-env env: - name: OPENSEARCH_DISABLED - value: "true" \ No newline at end of file + value: "true" + - name: VIRTUAL_HOST + value: "os.malcolm.local" \ No newline at end of file diff --git a/kubernetes/03-dashboards.yml b/kubernetes/03-dashboards.yml index 2da98b889..917316003 100644 --- a/kubernetes/03-dashboards.yml +++ b/kubernetes/03-dashboards.yml @@ -45,3 +45,5 @@ spec: env: - name: DASHBOARDS_DISABLED value: "true" + - name: VIRTUAL_HOST + value: "dashboards.malcolm.local" diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 6e52fde3f..2395eaa9f 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -47,4 +47,6 @@ spec: name: ssl-env env: - name: UPLOAD_DISABLED - value: "true" \ No newline at end of file + value: "true" + - name: VIRTUAL_HOST + value: "upload.malcolm.local" \ No newline at end of file diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml index 28bade031..f896a2b46 100644 --- a/kubernetes/05-pcap-monitor.yml +++ b/kubernetes/05-pcap-monitor.yml @@ -46,4 +46,4 @@ spec: name: common-upload-env env: - name: PCAPMON_DISABLED - value: "true" \ No newline at end of file + value: "true" diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index bcf06f907..5450431f6 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -48,4 +48,6 @@ spec: name: arkime-env env: - name: ARKIME_DISABLED - value: "true" \ No newline at end of file + value: "true" + - name: VIRTUAL_HOST + value: "arkime.malcolm.local" \ No newline at end of file diff --git a/kubernetes/07-api.yml b/kubernetes/07-api.yml index 9dc444f2a..64262cb86 100644 --- a/kubernetes/07-api.yml +++ b/kubernetes/07-api.yml @@ -44,4 +44,6 @@ spec: name: opensearch-env env: - name: API_DISABLED - value: "true" \ No newline at end of file + value: "true" + - name: VIRTUAL_HOST + value: "api.malcolm.local" \ No newline at end of file diff --git a/kubernetes/08-dashboards-helper.yml b/kubernetes/08-dashboards-helper.yml index b69de6f65..31873c054 100644 --- a/kubernetes/08-dashboards-helper.yml +++ b/kubernetes/08-dashboards-helper.yml @@ -46,4 +46,6 @@ spec: name: dashboards-helper-env env: - name: DASHBOARDS_HELPER_DISABLED - value: "true" \ No newline at end of file + value: "true" + - name: VIRTUAL_HOST + value: "dashboards-helper.malcolm.local" \ No newline at end of file diff --git a/kubernetes/09-zeek.yml b/kubernetes/09-zeek.yml index 21b27eb88..57709445f 100644 --- a/kubernetes/09-zeek.yml +++ b/kubernetes/09-zeek.yml @@ -33,4 +33,4 @@ spec: name: zeek-offline-env env: - name: ZEEK_DISABLED - value: "true" \ No newline at end of file + value: "true" diff --git a/kubernetes/10-suricata.yml b/kubernetes/10-suricata.yml index 97e97668d..86a66ca5c 100644 --- a/kubernetes/10-suricata.yml +++ b/kubernetes/10-suricata.yml @@ -33,4 +33,4 @@ spec: name: suricata-offline-env env: - name: SURICATA_DISABLED - value: "true" \ No newline at end of file + value: "true" diff --git a/kubernetes/11-file-monitor.yml b/kubernetes/11-file-monitor.yml index a6c5d7488..0786bb593 100644 --- a/kubernetes/11-file-monitor.yml +++ b/kubernetes/11-file-monitor.yml @@ -44,4 +44,6 @@ spec: name: zeek-env env: - name: FILE_MONITOR_DISABLED - value: "true" \ No newline at end of file + value: "true" + - name: VIRTUAL_HOST + value: "file-monitor.malcolm.local" \ No newline at end of file diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml index d212b3878..3835913fa 100644 --- a/kubernetes/12-filebeat.yml +++ b/kubernetes/12-filebeat.yml @@ -52,4 +52,4 @@ spec: name: common-beats-env env: - name: FILEBEAT_DISABLED - value: "true" \ No newline at end of file + value: "true" diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index a37594d35..d151392a1 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -57,4 +57,4 @@ spec: name: common-lookup-env env: - name: LOGSTASH_DISABLED - value: "true" \ No newline at end of file + value: "true" diff --git a/kubernetes/14-name-map-ui.yml b/kubernetes/14-name-map-ui.yml index 5f9515eac..216745e1f 100644 --- a/kubernetes/14-name-map-ui.yml +++ b/kubernetes/14-name-map-ui.yml @@ -43,3 +43,5 @@ spec: env: - name: NAME_MAP_UI_DISABLED value: "true" + - name: VIRTUAL_HOST + value: "name-map-ui.malcolm.local" diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index e75b79dd6..e12d53f2a 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -45,3 +45,5 @@ spec: env: - name: NETBOX_REDIS_DISABLED value: "true" + - name: VIRTUAL_HOST + value: "netbox-redis.malcolm.local" \ No newline at end of file diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index c5d8bfe7d..9fd92f102 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -45,3 +45,5 @@ spec: env: - name: NETBOX_REDIS_CACHE_DISABLED value: "true" + - name: VIRTUAL_HOST + value: "netbox-redis-cache.malcolm.local" \ No newline at end of file diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index 8e49d37f6..4247e98d3 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -44,4 +44,6 @@ spec: name: netbox-env env: - name: NETBOX_POSTGRES_DISABLED - value: "true" \ No newline at end of file + value: "true" + - name: VIRTUAL_HOST + value: "netbox-postgres.malcolm.local" \ No newline at end of file diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index c1d8d84c7..98c0d6f6d 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -54,3 +54,5 @@ spec: env: - name: NETBOX_DISABLED value: "true" + - name: VIRTUAL_HOST + value: "netbox.malcolm.local" diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml index 83cec7b2f..7ff5cc37f 100644 --- a/kubernetes/19-htadmin.yml +++ b/kubernetes/19-htadmin.yml @@ -44,4 +44,6 @@ spec: name: auth-env env: - name: HTADMIN_DISABLED - value: "true" \ No newline at end of file + value: "true" + - name: VIRTUAL_HOST + value: "htadmin.malcolm.local" \ No newline at end of file diff --git a/kubernetes/20-pcap-capture.yml b/kubernetes/20-pcap-capture.yml index 79ebe450d..b7fee5855 100644 --- a/kubernetes/20-pcap-capture.yml +++ b/kubernetes/20-pcap-capture.yml @@ -29,4 +29,4 @@ spec: name: pcap-capture-env env: - name: PCAP_CAPTURE_DISABLED - value: "true" \ No newline at end of file + value: "true" diff --git a/kubernetes/21-zeek-live.yml b/kubernetes/21-zeek-live.yml index c7f16eb8a..72f13dc52 100644 --- a/kubernetes/21-zeek-live.yml +++ b/kubernetes/21-zeek-live.yml @@ -35,4 +35,4 @@ spec: name: pcap-capture-env env: - name: ZEEK_LIVE_DISABLED - value: "true" \ No newline at end of file + value: "true" diff --git a/kubernetes/22-suricata-live.yml b/kubernetes/22-suricata-live.yml index 1eb60635a..a7d61375e 100644 --- a/kubernetes/22-suricata-live.yml +++ b/kubernetes/22-suricata-live.yml @@ -35,4 +35,4 @@ spec: name: pcap-capture-env env: - name: SURICATA_LIVE_DISABLED - value: "true" \ No newline at end of file + value: "true" diff --git a/kubernetes/23-freq.yml b/kubernetes/23-freq.yml index e80133663..93eead00f 100644 --- a/kubernetes/23-freq.yml +++ b/kubernetes/23-freq.yml @@ -42,3 +42,6 @@ spec: name: ssl-env - configMapRef: name: common-lookup-env + env: + - name: VIRTUAL_HOST + value: "freq.malcolm.local" \ No newline at end of file From 1e407f571b942710105e3088e6b61a3668f9c282 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 20 Mar 2023 15:20:19 -0600 Subject: [PATCH 021/239] Use slightly smarter fallback HTTP server --- Dockerfiles/api.Dockerfile | 1 + Dockerfiles/arkime.Dockerfile | 1 + Dockerfiles/dashboards-helper.Dockerfile | 1 + Dockerfiles/dashboards.Dockerfile | 1 + Dockerfiles/file-monitor.Dockerfile | 1 + Dockerfiles/file-upload.Dockerfile | 1 + Dockerfiles/filebeat.Dockerfile | 1 + Dockerfiles/freq.Dockerfile | 1 + Dockerfiles/htadmin.Dockerfile | 1 + Dockerfiles/logstash.Dockerfile | 1 + Dockerfiles/name-map-ui.Dockerfile | 1 + Dockerfiles/netbox.Dockerfile | 1 + Dockerfiles/pcap-capture.Dockerfile | 1 + Dockerfiles/pcap-monitor.Dockerfile | 1 + Dockerfiles/suricata.Dockerfile | 1 + Dockerfiles/zeek.Dockerfile | 1 + 16 files changed, 16 insertions(+) diff --git a/Dockerfiles/api.Dockerfile b/Dockerfiles/api.Dockerfile index 093b666c6..894b0ad60 100644 --- a/Dockerfiles/api.Dockerfile +++ b/Dockerfiles/api.Dockerfile @@ -75,6 +75,7 @@ COPY shared/bin/opensearch_status.sh "${APP_HOME}"/ COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic RUN apt-get -q update \ && apt-get -y -q --no-install-recommends upgrade \ diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index 7b2a02566..7ca954217 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -178,6 +178,7 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l # add configuration and scripts COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic ADD arkime/scripts /opt/ ADD shared/bin/pcap_processor.py /opt/ ADD shared/bin/pcap_utils.py /opt/ diff --git a/Dockerfiles/dashboards-helper.Dockerfile b/Dockerfiles/dashboards-helper.Dockerfile index 5992f723b..caf4a4eba 100644 --- a/Dockerfiles/dashboards-helper.Dockerfile +++ b/Dockerfiles/dashboards-helper.Dockerfile @@ -65,6 +65,7 @@ ADD dashboards/supervisord.conf /etc/supervisord.conf ADD dashboards/templates /opt/templates COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic COPY --chmod=755 shared/bin/opensearch_status.sh /data/ COPY --chmod=755 shared/bin/opensearch_index_size_prune.py /data/ COPY --chmod=755 shared/bin/opensearch_read_only.py /data/ diff --git a/Dockerfiles/dashboards.Dockerfile b/Dockerfiles/dashboards.Dockerfile index 3141cc8c6..8db8f05a5 100644 --- a/Dockerfiles/dashboards.Dockerfile +++ b/Dockerfiles/dashboards.Dockerfile @@ -133,6 +133,7 @@ RUN yum upgrade -y && \ COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic COPY --chmod=755 dashboards/scripts/docker_entrypoint.sh /usr/local/bin/ ADD dashboards/opensearch_dashboards.yml /usr/share/opensearch-dashboards/config/opensearch_dashboards.orig.yml ADD dashboards/scripts/docker_entrypoint.sh /usr/local/bin/ diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index c9b188b21..0b0772d90 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -203,6 +203,7 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic ADD shared/bin/zeek_carve*.py /usr/local/bin/ ADD file-monitor/supervisord.conf /etc/supervisord.conf ADD file-monitor/docker-entrypoint.sh /docker-entrypoint.sh diff --git a/Dockerfiles/file-upload.Dockerfile b/Dockerfiles/file-upload.Dockerfile index d4bf5935d..20f8a3538 100644 --- a/Dockerfiles/file-upload.Dockerfile +++ b/Dockerfiles/file-upload.Dockerfile @@ -74,6 +74,7 @@ RUN apt-get -q update && \ COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic COPY --chmod=755 file-upload/docker-entrypoint.sh /docker-entrypoint.sh ADD docs/images/logo/Malcolm_banner.png /var/www/upload/Malcolm_banner.png ADD file-upload/jquery-file-upload/bootstrap.min.css /var/www/upload/bower_components/bootstrap/dist/css/bootstrap.min.css diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index 57b9460a5..5634c3042 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -104,6 +104,7 @@ RUN apt-get -q update && \ COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic ADD filebeat/filebeat.yml /usr/share/filebeat/filebeat.yml ADD filebeat/filebeat-nginx.yml /usr/share/filebeat-nginx/filebeat-nginx.yml ADD filebeat/filebeat-tcp.yml /usr/share/filebeat-tcp/filebeat-tcp.yml diff --git a/Dockerfiles/freq.Dockerfile b/Dockerfiles/freq.Dockerfile index 0a22d8a1c..e11bdb862 100644 --- a/Dockerfiles/freq.Dockerfile +++ b/Dockerfiles/freq.Dockerfile @@ -57,6 +57,7 @@ RUN apt-get -q update && \ COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic ADD freq-server/supervisord.conf /etc/supervisord.conf WORKDIR /opt/freq_server diff --git a/Dockerfiles/htadmin.Dockerfile b/Dockerfiles/htadmin.Dockerfile index 32f532b55..ad7497573 100644 --- a/Dockerfiles/htadmin.Dockerfile +++ b/Dockerfiles/htadmin.Dockerfile @@ -78,6 +78,7 @@ RUN apt-get -q update && \ COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic ADD docs/images/favicon/favicon.ico /var/www/htadmin/ ADD htadmin/supervisord.conf /supervisord.conf ADD htadmin/htadmin.sh /usr/local/bin/ diff --git a/Dockerfiles/logstash.Dockerfile b/Dockerfiles/logstash.Dockerfile index 962f45c0d..23337ebf1 100644 --- a/Dockerfiles/logstash.Dockerfile +++ b/Dockerfiles/logstash.Dockerfile @@ -76,6 +76,7 @@ RUN set -x && \ COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic COPY --chmod=755 shared/bin/manuf-oui-parse.py /usr/local/bin/ COPY --chmod=755 shared/bin/jdk-cacerts-auto-import.sh /usr/local/bin/ ADD logstash/maps/*.yaml /etc/ diff --git a/Dockerfiles/name-map-ui.Dockerfile b/Dockerfiles/name-map-ui.Dockerfile index 13a394626..228a5038f 100644 --- a/Dockerfiles/name-map-ui.Dockerfile +++ b/Dockerfiles/name-map-ui.Dockerfile @@ -63,6 +63,7 @@ WORKDIR /var/www/html COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic COPY name-map-ui/site/ /var/www/html/ COPY docs/images/logo/Malcolm_banner.png /var/www/html/ COPY docs/images/favicon/favicon.ico /var/www/html/ diff --git a/Dockerfiles/netbox.Dockerfile b/Dockerfiles/netbox.Dockerfile index aa748d54d..ded2d4a4e 100644 --- a/Dockerfiles/netbox.Dockerfile +++ b/Dockerfiles/netbox.Dockerfile @@ -77,6 +77,7 @@ RUN apt-get -q update && \ COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic COPY --chmod=755 netbox/scripts/* /usr/local/bin/ COPY --chmod=644 netbox/supervisord.conf /etc/supervisord.conf COPY --chmod=644 netbox/*-defaults.json /etc/ diff --git a/Dockerfiles/pcap-capture.Dockerfile b/Dockerfiles/pcap-capture.Dockerfile index dff0b3cfa..bbf4688b7 100644 --- a/Dockerfiles/pcap-capture.Dockerfile +++ b/Dockerfiles/pcap-capture.Dockerfile @@ -54,6 +54,7 @@ ENV PCAP_SNAPLEN $PCAP_SNAPLEN COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic COPY --chmod=755 shared/bin/nic-capture-setup.sh /usr/local/bin/ ADD pcap-capture/supervisord.conf /etc/supervisord.conf ADD pcap-capture/scripts/*.sh /usr/local/bin/ diff --git a/Dockerfiles/pcap-monitor.Dockerfile b/Dockerfiles/pcap-monitor.Dockerfile index 28302c7b4..51f5d1183 100644 --- a/Dockerfiles/pcap-monitor.Dockerfile +++ b/Dockerfiles/pcap-monitor.Dockerfile @@ -66,6 +66,7 @@ RUN apt-get -q update && \ COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic ADD pcap-monitor/supervisord.conf /etc/supervisord.conf ADD pcap-monitor/scripts/ /usr/local/bin/ ADD shared/bin/pcap_watcher.py /usr/local/bin/ diff --git a/Dockerfiles/suricata.Dockerfile b/Dockerfiles/suricata.Dockerfile index ea6c09390..2d0c9e720 100644 --- a/Dockerfiles/suricata.Dockerfile +++ b/Dockerfiles/suricata.Dockerfile @@ -125,6 +125,7 @@ COPY --chmod=644 shared/bin/pcap_utils.py /usr/local/bin/ COPY --chmod=644 suricata/supervisord.conf /etc/supervisord.conf COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic COPY --chmod=755 shared/bin/nic-capture-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/pcap_processor.py /usr/local/bin/ COPY --chmod=755 shared/bin/suricata_config_populate.py /usr/local/bin/ diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index ffa5af04b..6255b42a8 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -151,6 +151,7 @@ RUN export DEBARCH=$(dpkg --print-architecture) && \ # add configuration and scripts COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic ADD shared/bin/pcap_processor.py /usr/local/bin/ ADD shared/bin/pcap_utils.py /usr/local/bin/ ADD shared/bin/zeek*threat*.py ${ZEEK_DIR}/bin/ From 273cd892c78deb682492b3fe8e97c06fe6dce26c Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 20 Mar 2023 15:56:36 -0600 Subject: [PATCH 022/239] work in progress --- Dockerfiles/postgresql.Dockerfile | 2 +- Dockerfiles/redis.Dockerfile | 2 +- kubernetes/02-opensearch.yml | 2 +- kubernetes/04-upload.yml | 2 +- kubernetes/06-arkime.yml | 2 +- kubernetes/07-api.yml | 2 +- kubernetes/08-dashboards-helper.yml | 2 +- kubernetes/11-file-monitor.yml | 2 +- kubernetes/15-netbox-redis.yml | 4 +--- kubernetes/16-netbox-redis-cache.yml | 4 +--- kubernetes/17-netbox-postgres.yml | 4 +--- kubernetes/18-netbox.yml | 2 -- kubernetes/19-htadmin.yml | 2 +- kubernetes/21-zeek-live.yml | 2 +- kubernetes/22-suricata-live.yml | 2 +- kubernetes/23-freq.yml | 2 +- 16 files changed, 15 insertions(+), 23 deletions(-) diff --git a/Dockerfiles/postgresql.Dockerfile b/Dockerfiles/postgresql.Dockerfile index 1c740f3d3..ea4e14f69 100644 --- a/Dockerfiles/postgresql.Dockerfile +++ b/Dockerfiles/postgresql.Dockerfile @@ -44,7 +44,7 @@ ENTRYPOINT ["/sbin/tini", \ "--", \ "/usr/bin/docker-uid-gid-setup.sh", \ "/usr/local/bin/service_check_passthrough.sh", \ - "-s", "netbox-postgres"] + "-s", "netbox"] CMD ["/usr/bin/docker-entrypoint.sh", "postgres"] diff --git a/Dockerfiles/redis.Dockerfile b/Dockerfiles/redis.Dockerfile index 6585f2ea2..a25c17c72 100644 --- a/Dockerfiles/redis.Dockerfile +++ b/Dockerfiles/redis.Dockerfile @@ -35,7 +35,7 @@ ENTRYPOINT ["/sbin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ "/usr/local/bin/service_check_passthrough.sh", \ - "-s", "netbox-redis"] + "-s", "netbox"] # to be populated at build-time: ARG BUILD_DATE diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index c5bcbe1ee..ffba73248 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -46,4 +46,4 @@ spec: - name: OPENSEARCH_DISABLED value: "true" - name: VIRTUAL_HOST - value: "os.malcolm.local" \ No newline at end of file + value: "os.malcolm.local" diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 2395eaa9f..ed577ac95 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -49,4 +49,4 @@ spec: - name: UPLOAD_DISABLED value: "true" - name: VIRTUAL_HOST - value: "upload.malcolm.local" \ No newline at end of file + value: "upload.malcolm.local" diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index 5450431f6..ef29252c6 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -50,4 +50,4 @@ spec: - name: ARKIME_DISABLED value: "true" - name: VIRTUAL_HOST - value: "arkime.malcolm.local" \ No newline at end of file + value: "arkime.malcolm.local" diff --git a/kubernetes/07-api.yml b/kubernetes/07-api.yml index 64262cb86..324f7f181 100644 --- a/kubernetes/07-api.yml +++ b/kubernetes/07-api.yml @@ -46,4 +46,4 @@ spec: - name: API_DISABLED value: "true" - name: VIRTUAL_HOST - value: "api.malcolm.local" \ No newline at end of file + value: "api.malcolm.local" diff --git a/kubernetes/08-dashboards-helper.yml b/kubernetes/08-dashboards-helper.yml index 31873c054..a093117a3 100644 --- a/kubernetes/08-dashboards-helper.yml +++ b/kubernetes/08-dashboards-helper.yml @@ -48,4 +48,4 @@ spec: - name: DASHBOARDS_HELPER_DISABLED value: "true" - name: VIRTUAL_HOST - value: "dashboards-helper.malcolm.local" \ No newline at end of file + value: "dashboards-helper.malcolm.local" diff --git a/kubernetes/11-file-monitor.yml b/kubernetes/11-file-monitor.yml index 0786bb593..ccd21230b 100644 --- a/kubernetes/11-file-monitor.yml +++ b/kubernetes/11-file-monitor.yml @@ -46,4 +46,4 @@ spec: - name: FILE_MONITOR_DISABLED value: "true" - name: VIRTUAL_HOST - value: "file-monitor.malcolm.local" \ No newline at end of file + value: "file-monitor.malcolm.local" diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index e12d53f2a..fd4a28ff3 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -43,7 +43,5 @@ spec: - configMapRef: name: netbox-env env: - - name: NETBOX_REDIS_DISABLED - value: "true" - name: VIRTUAL_HOST - value: "netbox-redis.malcolm.local" \ No newline at end of file + value: "netbox-redis.malcolm.local" diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index 9fd92f102..d913be028 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -43,7 +43,5 @@ spec: - configMapRef: name: netbox-env env: - - name: NETBOX_REDIS_CACHE_DISABLED - value: "true" - name: VIRTUAL_HOST - value: "netbox-redis-cache.malcolm.local" \ No newline at end of file + value: "netbox-redis-cache.malcolm.local" diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index 4247e98d3..3dfbc93bf 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -43,7 +43,5 @@ spec: - configMapRef: name: netbox-env env: - - name: NETBOX_POSTGRES_DISABLED - value: "true" - name: VIRTUAL_HOST - value: "netbox-postgres.malcolm.local" \ No newline at end of file + value: "netbox-postgres.malcolm.local" diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index 98c0d6f6d..2b86a98ca 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -52,7 +52,5 @@ spec: - configMapRef: name: netbox-env env: - - name: NETBOX_DISABLED - value: "true" - name: VIRTUAL_HOST value: "netbox.malcolm.local" diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml index 7ff5cc37f..0f3195707 100644 --- a/kubernetes/19-htadmin.yml +++ b/kubernetes/19-htadmin.yml @@ -46,4 +46,4 @@ spec: - name: HTADMIN_DISABLED value: "true" - name: VIRTUAL_HOST - value: "htadmin.malcolm.local" \ No newline at end of file + value: "htadmin.malcolm.local" diff --git a/kubernetes/21-zeek-live.yml b/kubernetes/21-zeek-live.yml index 72f13dc52..6026a5529 100644 --- a/kubernetes/21-zeek-live.yml +++ b/kubernetes/21-zeek-live.yml @@ -34,5 +34,5 @@ spec: - configMapRef: name: pcap-capture-env env: - - name: ZEEK_LIVE_DISABLED + - name: ZEEK_DISABLED value: "true" diff --git a/kubernetes/22-suricata-live.yml b/kubernetes/22-suricata-live.yml index a7d61375e..e68c16f3b 100644 --- a/kubernetes/22-suricata-live.yml +++ b/kubernetes/22-suricata-live.yml @@ -34,5 +34,5 @@ spec: - configMapRef: name: pcap-capture-env env: - - name: SURICATA_LIVE_DISABLED + - name: SURICATA_DISABLED value: "true" diff --git a/kubernetes/23-freq.yml b/kubernetes/23-freq.yml index 93eead00f..a72ca4485 100644 --- a/kubernetes/23-freq.yml +++ b/kubernetes/23-freq.yml @@ -44,4 +44,4 @@ spec: name: common-lookup-env env: - name: VIRTUAL_HOST - value: "freq.malcolm.local" \ No newline at end of file + value: "freq.malcolm.local" From 44ff108c3974aef4ad634b6f9f93616d69060d76 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 21 Mar 2023 11:54:39 -0600 Subject: [PATCH 023/239] for idaholab/Malcolm#165, remove name-map-ui container --- .../name-map-ui-build-and-push-ghcr.yml | 63 --- Dockerfiles/logstash.Dockerfile | 2 - Dockerfiles/name-map-ui.Dockerfile | 89 ---- _config.yml | 1 - docker-compose-standalone.yml | 25 - docker-compose.yml | 28 -- docs/README.md | 7 +- docs/arkime.md | 2 +- docs/components.md | 1 - docs/contributing-local-modifications.md | 4 - docs/development.md | 4 - docs/host-and-subnet-mapping.md | 52 -- .../screenshots/malcolm_name_map_ui.png | Bin 254066 -> 0 bytes docs/malcolm-config.md | 3 +- docs/malcolm-upgrade.md | 2 +- docs/quickstart.md | 3 - docs/severity.md | 4 +- docs/ubuntu-install-example.md | 4 - kubernetes/14-name-map-ui.yml | 47 -- kubernetes/logstash.env | 2 - kubernetes/services-dev-plan.md | 1 - logstash/scripts/ip-to-segment-logstash.py | 305 ------------ logstash/scripts/logstash-start.sh | 11 - .../panel/launcher-19/16343117417.desktop | 12 - .../xfce-perchannel-xml/xfce4-panel.xml | 6 - .../applications/malcolm-mapping.desktop | 11 - name-map-ui/config/fpm-pool.conf | 56 --- name-map-ui/config/nginx.conf | 92 ---- name-map-ui/config/php.ini | 7 - .../config/supervisor_logstash_ctl.conf | 10 - name-map-ui/config/supervisor_netbox_ctl.conf | 10 - name-map-ui/config/supervisord.conf | 72 --- name-map-ui/scripts/name-map-save-watch.sh | 22 - name-map-ui/site/index.html | 466 ------------------ name-map-ui/site/mapping.css | 229 --------- name-map-ui/site/restart-logstash.php | 7 - name-map-ui/site/upload.html | 8 - name-map-ui/site/upload.php | 57 --- nginx/nginx.conf | 12 - scripts/build.sh | 3 +- scripts/control.py | 1 - scripts/demo/reset_and_auto_populate.sh | 2 +- scripts/install.py | 8 - scripts/malcolm_appliance_packager.sh | 1 - shared/bin/service_check_passthrough.sh | 2 - 45 files changed, 8 insertions(+), 1746 deletions(-) delete mode 100644 .github/workflows/name-map-ui-build-and-push-ghcr.yml delete mode 100644 Dockerfiles/name-map-ui.Dockerfile delete mode 100644 docs/host-and-subnet-mapping.md delete mode 100644 docs/images/screenshots/malcolm_name_map_ui.png delete mode 100644 kubernetes/14-name-map-ui.yml delete mode 100755 logstash/scripts/ip-to-segment-logstash.py delete mode 100644 malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/panel/launcher-19/16343117417.desktop delete mode 100644 malcolm-iso/config/includes.chroot/usr/share/applications/malcolm-mapping.desktop delete mode 100644 name-map-ui/config/fpm-pool.conf delete mode 100644 name-map-ui/config/nginx.conf delete mode 100644 name-map-ui/config/php.ini delete mode 100644 name-map-ui/config/supervisor_logstash_ctl.conf delete mode 100644 name-map-ui/config/supervisor_netbox_ctl.conf delete mode 100644 name-map-ui/config/supervisord.conf delete mode 100755 name-map-ui/scripts/name-map-save-watch.sh delete mode 100644 name-map-ui/site/index.html delete mode 100644 name-map-ui/site/mapping.css delete mode 100644 name-map-ui/site/restart-logstash.php delete mode 100644 name-map-ui/site/upload.html delete mode 100644 name-map-ui/site/upload.php diff --git a/.github/workflows/name-map-ui-build-and-push-ghcr.yml b/.github/workflows/name-map-ui-build-and-push-ghcr.yml deleted file mode 100644 index b40eec80f..000000000 --- a/.github/workflows/name-map-ui-build-and-push-ghcr.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: name-map-ui-build-and-push-ghcr - -on: - push: - branches: - - main - - development - - kubernetes - paths: - - 'name-map-ui/**' - - 'Dockerfiles/name-map-ui.Dockerfile' - - 'shared/bin/*' - - '.trigger_workflow_build' - workflow_dispatch: - repository_dispatch: - -jobs: - docker: - runs-on: ubuntu-22.04 - permissions: - actions: write - packages: write - contents: read - steps: - - - name: Cancel previous run in progress - uses: styfle/cancel-workflow-action@0.11.0 - with: - ignore_sha: true - all_but_latest: true - access_token: ${{ secrets.GITHUB_TOKEN }} - - - name: Checkout - uses: actions/checkout@v3 - - - name: Extract branch name - shell: bash - run: echo "branch=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_OUTPUT - id: extract_branch - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - with: - driver-opts: | - image=moby/buildkit:master - - - name: Log in to registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - file: ./Dockerfiles/name-map-ui.Dockerfile - push: true - tags: ghcr.io/${{ github.repository_owner }}/malcolm/name-map-ui:${{ steps.extract_branch.outputs.branch }} diff --git a/Dockerfiles/logstash.Dockerfile b/Dockerfiles/logstash.Dockerfile index 23337ebf1..598a74789 100644 --- a/Dockerfiles/logstash.Dockerfile +++ b/Dockerfiles/logstash.Dockerfile @@ -27,7 +27,6 @@ ARG LOGSTASH_PARSE_PIPELINE_ADDRESSES=zeek-parse,suricata-parse,beats-parse ARG LOGSTASH_OPENSEARCH_PIPELINE_ADDRESS_INTERNAL=internal-os ARG LOGSTASH_OPENSEARCH_PIPELINE_ADDRESS_EXTERNAL=external-os ARG LOGSTASH_OPENSEARCH_OUTPUT_PIPELINE_ADDRESSES=internal-os,external-os -ARG LOGSTASH_NETWORK_MAP_ENRICHMENT=true ARG LOGSTASH_NETBOX_ENRICHMENT=false ARG LOGSTASH_NETBOX_ENRICHMENT_VERBOSE=false ARG LOGSTASH_NETBOX_ENRICHMENT_LOOKUP_SERVICE=true @@ -37,7 +36,6 @@ ENV LOGSTASH_PARSE_PIPELINE_ADDRESSES $LOGSTASH_PARSE_PIPELINE_ADDRESSES ENV LOGSTASH_OPENSEARCH_PIPELINE_ADDRESS_INTERNAL $LOGSTASH_OPENSEARCH_PIPELINE_ADDRESS_INTERNAL ENV LOGSTASH_OPENSEARCH_PIPELINE_ADDRESS_EXTERNAL $LOGSTASH_OPENSEARCH_PIPELINE_ADDRESS_EXTERNAL ENV LOGSTASH_OPENSEARCH_OUTPUT_PIPELINE_ADDRESSES $LOGSTASH_OPENSEARCH_OUTPUT_PIPELINE_ADDRESSES -ENV LOGSTASH_NETWORK_MAP_ENRICHMENT $LOGSTASH_NETWORK_MAP_ENRICHMENT ENV LOGSTASH_NETBOX_ENRICHMENT $LOGSTASH_NETBOX_ENRICHMENT ENV LOGSTASH_NETBOX_ENRICHMENT_VERBOSE $LOGSTASH_NETBOX_ENRICHMENT_VERBOSE ENV LOGSTASH_NETBOX_ENRICHMENT_LOOKUP_SERVICE $LOGSTASH_NETBOX_ENRICHMENT_LOOKUP_SERVICE diff --git a/Dockerfiles/name-map-ui.Dockerfile b/Dockerfiles/name-map-ui.Dockerfile deleted file mode 100644 index 228a5038f..000000000 --- a/Dockerfiles/name-map-ui.Dockerfile +++ /dev/null @@ -1,89 +0,0 @@ -FROM alpine:3.17 - -# Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. -LABEL maintainer="malcolm@inl.gov" -LABEL org.opencontainers.image.authors='malcolm@inl.gov' -LABEL org.opencontainers.image.url='https://github.com/idaholab/Malcolm' -LABEL org.opencontainers.image.documentation='https://github.com/idaholab/Malcolm/blob/main/README.md' -LABEL org.opencontainers.image.source='https://github.com/idaholab/Malcolm' -LABEL org.opencontainers.image.vendor='Idaho National Laboratory' -LABEL org.opencontainers.image.title='ghcr.io/idaholab/malcolm/name-map-ui' -LABEL org.opencontainers.image.description='Malcolm container providing a user interface for mapping names to network hosts and subnets' - -ARG DEFAULT_UID=1000 -ARG DEFAULT_GID=1000 -ENV DEFAULT_UID $DEFAULT_UID -ENV DEFAULT_GID $DEFAULT_GID -ENV PUSER "nginxsrv" -ENV PGROUP "nginxsrv" -ENV PUSER_PRIV_DROP true -ENV PUSER_CHOWN "/var/www/html;/var/lib/nginx;/var/log/nginx" - -ENV TERM xterm - -ENV JQUERY_VERSION 1.6.4 -ENV LISTJS_VERSION v1.5.0 - -RUN apk update --no-cache && \ - apk upgrade --no-cache && \ - apk --no-cache add bash php81 php81-fpm php81-mysqli php81-json php81-openssl php81-curl php81-fileinfo \ - php81-zlib php81-xml php81-phar php81-intl php81-dom php81-xmlreader php81-ctype php81-session \ - php81-mbstring php81-gd nginx supervisor curl inotify-tools file psmisc rsync shadow openssl tini - -COPY name-map-ui/config/nginx.conf /etc/nginx/nginx.conf -COPY name-map-ui/config/fpm-pool.conf /etc/php81/php-fpm.d/www.conf -COPY name-map-ui/config/php.ini /etc/php81/conf.d/custom.ini -COPY name-map-ui/config/supervisord.conf /etc/supervisord.conf -COPY name-map-ui/config/supervisor_logstash_ctl.conf /etc/supervisor/logstash/supervisord.conf -COPY name-map-ui/config/supervisor_netbox_ctl.conf /etc/supervisor/netbox/supervisord.conf -COPY name-map-ui/scripts/*.sh /usr/local/bin/ - -RUN curl -sSL -o /tmp/jquery.min.js "https://code.jquery.com/jquery-${JQUERY_VERSION}.min.js" && \ - curl -sSL -o /tmp/list.min.js "https://raw.githubusercontent.com/javve/list.js/${LISTJS_VERSION}/dist/list.min.js" && \ - rm -rf /etc/nginx/conf.d/default.conf /var/www/html/* && \ - mkdir -p /var/www/html/upload /var/www/html/maps && \ - cd /var/www/html && \ - mv /tmp/jquery.min.js /tmp/list.min.js ./ && \ - chmod 644 ./jquery.min.js ./list.min.js && \ - ln -s . name-map-ui && \ - addgroup -g ${DEFAULT_GID} ${PGROUP} ; \ - adduser -D -H -u ${DEFAULT_UID} -h /var/www/html -s /sbin/nologin -G ${PGROUP} -g ${PUSER} ${PUSER} ; \ - addgroup ${PUSER} nginx ; \ - addgroup ${PUSER} shadow ; \ - addgroup ${PUSER} tty ; \ - addgroup nginx tty ; \ - chown -R ${PUSER}:${PGROUP} /var/www/html && \ - chown -R ${PUSER}:${PGROUP} /var/lib/nginx && \ - chown -R ${PUSER}:${PGROUP} /var/log/nginx && \ - chmod 755 /usr/local/bin/*.sh - -VOLUME /var/www/html - -WORKDIR /var/www/html - -COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ -COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ -COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic -COPY name-map-ui/site/ /var/www/html/ -COPY docs/images/logo/Malcolm_banner.png /var/www/html/ -COPY docs/images/favicon/favicon.ico /var/www/html/ - -EXPOSE 8080 - -ENTRYPOINT ["/sbin/tini", \ - "--", \ - "/usr/local/bin/docker-uid-gid-setup.sh", \ - "/usr/local/bin/service_check_passthrough.sh", \ - "-s", "name-map-ui"] - -CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] - - -# to be populated at build-time: -ARG BUILD_DATE -ARG MALCOLM_VERSION -ARG VCS_REVISION - -LABEL org.opencontainers.image.created=$BUILD_DATE -LABEL org.opencontainers.image.version=$MALCOLM_VERSION -LABEL org.opencontainers.image.revision=$VCS_REVISION diff --git a/_config.yml b/_config.yml index 0e92a8819..3bb94c98f 100644 --- a/_config.yml +++ b/_config.yml @@ -75,7 +75,6 @@ exclude: - htadmin - logstash - malcolm-iso - - name-map-ui - net-map.json - netbox - nginx diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 301715a2b..965434c8a 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -235,8 +235,6 @@ x-logstash-variables: &logstash-variables LOGSTASH_SEVERITY_SCORING : 'true' # Whether or not Logstash will perform a reverse DNS lookup for external IP addresses LOGSTASH_REVERSE_DNS : 'false' - # Whether or not Logstash will enrich network traffic metadata directly from net-map.json - LOGSTASH_NETWORK_MAP_ENRICHMENT : 'true' # Whether or not Logstash will enrich network traffic metadata via NetBox API calls LOGSTASH_NETBOX_ENRICHMENT : 'false' @@ -491,7 +489,6 @@ services: - ./logstash/certs/ca.crt:/certs/ca.crt:ro - ./logstash/certs/server.crt:/certs/server.crt:ro - ./logstash/certs/server.key:/certs/server.key:ro - - ./net-map.json:/usr/share/logstash/config/net-map.json:ro healthcheck: test: ["CMD", "curl", "--silent", "--fail", "http://localhost:9600"] interval: 30s @@ -855,27 +852,6 @@ services: timeout: 15s retries: 3 start_period: 60s - name-map-ui: - image: ghcr.io/idaholab/malcolm/name-map-ui:kubernetes - restart: "no" - stdin_open: false - tty: true - hostname: name-map-ui - networks: - - default - environment: - << : *process-variables - << : *ssl-variables - VIRTUAL_HOST : 'name-map-ui.malcolm.local' - volumes: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./net-map.json:/var/www/html/maps/net-map.json:rw - healthcheck: - test: ["CMD", "curl", "--silent", "--fail", "http://localhost:8080/fpm-ping"] - interval: 30s - timeout: 15s - retries: 3 - start_period: 60s netbox: image: ghcr.io/idaholab/malcolm/netbox:kubernetes restart: "no" @@ -1025,7 +1001,6 @@ services: - dashboards - file-monitor - htadmin - - name-map-ui - netbox - upload ports: diff --git a/docker-compose.yml b/docker-compose.yml index cb769a7d8..8e2d4344d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -235,8 +235,6 @@ x-logstash-variables: &logstash-variables LOGSTASH_SEVERITY_SCORING : 'true' # Whether or not Logstash will perform a reverse DNS lookup for external IP addresses LOGSTASH_REVERSE_DNS : 'false' - # Whether or not Logstash will enrich network traffic metadata directly from net-map.json - LOGSTASH_NETWORK_MAP_ENRICHMENT : 'true' # Whether or not Logstash will enrich network traffic metadata via NetBox API calls LOGSTASH_NETBOX_ENRICHMENT : 'false' @@ -507,7 +505,6 @@ services: - ./logstash/certs/ca.crt:/certs/ca.crt:ro - ./logstash/certs/server.crt:/certs/server.crt:ro - ./logstash/certs/server.key:/certs/server.key:ro - - ./net-map.json:/usr/share/logstash/config/net-map.json:ro healthcheck: test: ["CMD", "curl", "--silent", "--fail", "http://localhost:9600"] interval: 30s @@ -912,30 +909,6 @@ services: timeout: 15s retries: 3 start_period: 60s - name-map-ui: - image: ghcr.io/idaholab/malcolm/name-map-ui:kubernetes - build: - context: . - dockerfile: Dockerfiles/name-map-ui.Dockerfile - restart: "no" - stdin_open: false - tty: true - hostname: name-map-ui - networks: - - default - environment: - << : *process-variables - << : *ssl-variables - VIRTUAL_HOST : 'name-map-ui.malcolm.local' - volumes: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./net-map.json:/var/www/html/maps/net-map.json:rw - healthcheck: - test: ["CMD", "curl", "--silent", "--fail", "http://localhost:8080/fpm-ping"] - interval: 30s - timeout: 15s - retries: 3 - start_period: 60s netbox: image: ghcr.io/idaholab/malcolm/netbox:kubernetes build: @@ -1104,7 +1077,6 @@ services: - dashboards - file-monitor - htadmin - - name-map-ui - netbox - upload ports: diff --git a/docs/README.md b/docs/README.md index d3eadbb88..59f38d16e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,7 +4,7 @@ Malcolm processes network traffic data in the form of packet capture (docs/PCAP) files or Zeek logs. A [sensor](live-analysis.md#Hedgehog) (packet capture appliance) monitors network traffic mirrored to it over a SPAN port on a network switch or router, or using a network TAP device. [Zeek](https://www.zeek.org/index.html) logs and [Arkime](https://arkime.com/) sessions are generated containing important session metadata from the traffic observed, which are then securely forwarded to a Malcolm instance. Full PCAP files are optionally stored locally on the sensor device for examination later. -Malcolm parses the network session data and enriches it with additional lookups and mappings including GeoIP mapping, hardware manufacturer lookups from [organizationally unique identifiers (docs/OUI)](http://standards-oui.ieee.org/oui/oui.txt) in MAC addresses, assigning names to [network segments](host-and-subnet-mapping.md#SegmentNaming) and [hosts](host-and-subnet-mapping.md#HostNaming) based on user-defined IP address and MAC mappings, performing [TLS fingerprinting](https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967), and many others. +Malcolm parses the network session data and enriches it with additional lookups and mappings including GeoIP mapping, hardware manufacturer lookups from [organizationally unique identifiers (docs/OUI)](http://standards-oui.ieee.org/oui/oui.txt) in MAC addresses, assigning names to [network segments and hosts](asset-interaction-analysis.md#AssetInteractionAnalysis) based on a user-defined asset inventory, performing [TLS fingerprinting](https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967), and many others. The enriched data is stored in an [OpenSearch](https://opensearch.org/) document store in a format suitable for analysis through two intuitive interfaces: OpenSearch Dashboards, a flexible data visualization plugin with dozens of prebuilt dashboards providing an at-a-glance overview of network protocols; and Arkime, a powerful tool for finding and identifying the network sessions comprising suspected security incidents. These tools can be accessed through a web browser from analyst workstations or for display in a security operations center (SOC). Logs can also optionally be forwarded on to another instance of Malcolm. @@ -71,11 +71,6 @@ For smaller networks, use at home by network security enthusiasts, or in the fie * [Search Queries in Arkime and OpenSearch](queries-cheat-sheet.md#SearchCheatSheet) * Other Malcolm features - [Automatic file extraction and scanning](file-scanning.md#ZeekFileExtraction) - - [Automatic host and subnet name assignment](host-and-subnet-mapping.md#HostAndSubnetNaming) - + [Defining hostname and CIDR subnet names interface](host-and-subnet-mapping.md#NameMapUI) - + [Applying mapping changes](host-and-subnet-mapping.md#ApplyMapping) - + [IP/MAC address to hostname mapping](host-and-subnet-mapping.md#HostNaming) - + [CIDR subnet to network segment name mapping](host-and-subnet-mapping.md#SegmentNaming) - [OpenSearch index management](index-management.md#IndexManagement) - [Event severity scoring](severity.md#Severity) + [Customizing event severity scoring](severity.md#SeverityConfig) diff --git a/docs/arkime.md b/docs/arkime.md index a95464617..59906a0e7 100644 --- a/docs/arkime.md +++ b/docs/arkime.md @@ -120,7 +120,7 @@ While the default source and destination fields are *Src IP* and *Dst IP:Dst Por * *Src OUI* and *Dst OUI* (hardware manufacturers) * *Src IP* and *Protocols* -* *Originating Network Segment* and *Responding Network Segment* (see [CIDR subnet to network segment name mapping](host-and-subnet-mapping.md#SegmentNaming)) +* *Originating Network Segment* and *Responding Network Segment* (see [Asset Interaction Analysis](asset-interaction-analysis.md#AssetInteractionAnalysis)) * *Originating GeoIP City* and *Responding GeoIP City* or any other combination of these or other fields. diff --git a/docs/components.md b/docs/components.md index c407f60e7..8f5b52e80 100644 --- a/docs/components.md +++ b/docs/components.md @@ -15,7 +15,6 @@ Malcolm leverages the following excellent open source tools, among others. * [ClamAV](https://www.clamav.net/) - an antivirus engine for scanning files extracted by Zeek * [CyberChef](https://github.com/gchq/CyberChef) - a "swiss-army knife" data conversion tool * [jQuery File Upload](https://github.com/blueimp/jQuery-File-Upload) - for uploading PCAP files and Zeek logs for processing -* [List.js](https://github.com/javve/list.js) - for the [host and subnet name mapping](host-and-subnet-mapping.md#HostAndSubnetNaming) interface * [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) - for simple, reproducible deployment of the Malcolm appliance across environments and to coordinate communication between its various components * [NetBox](https://netbox.dev/) - a suite for modeling and documenting modern networks * [PostgreSQL](https://www.postgresql.org/) - a relational database for persisting NetBox's data diff --git a/docs/contributing-local-modifications.md b/docs/contributing-local-modifications.md index a3076a08d..89dfc4aaf 100644 --- a/docs/contributing-local-modifications.md +++ b/docs/contributing-local-modifications.md @@ -29,7 +29,6 @@ $ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml - ./logstash/certs/ca.crt:/certs/ca.crt:ro - ./logstash/certs/server.crt:/certs/server.crt:ro - ./logstash/certs/server.key:/certs/server.key:ro - - ./net-map.json:/usr/share/logstash/config/net-map.json:ro filebeat: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro @@ -89,9 +88,6 @@ $ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml - ./nginx/htpasswd:/var/www/htadmin/config/htpasswd:rw freq: - ./nginx/ca-trust:/var/local/ca-trust:ro - name-map-ui: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./net-map.json:/var/www/html/maps/net-map.json:rw api: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro diff --git a/docs/development.md b/docs/development.md index d4f111ca8..0a491e23c 100644 --- a/docs/development.md +++ b/docs/development.md @@ -22,7 +22,6 @@ Checking out the [Malcolm source code]({{ site.github.repository_url }}/tree/{{ * `dashboards` - code and configuration for the `dashboards` container for creating additional ad-hoc visualizations and dashboards beyond that which is provided by Arkime Viewer * `logstash` - code and configuration for the `logstash` container which parses Zeek logs and forwards them to the `opensearch` container * `malcolm-iso` - code and configuration for building an [installer ISO](malcolm-iso.md#ISO) for a minimal Debian-based Linux installation for running Malcolm -* `name-map-ui` - code and configuration for the `name-map-ui` container which provides the [host and subnet name mapping](host-and-subnet-mapping.md#HostAndSubnetNaming) interface * `netbox` - code and configuration for the `netbox`, `netbox-postgres`, `netbox-redis` and `netbox-redis-cache` containers which provide asset management capabilities * `nginx` - configuration for the `nginx` reverse proxy container * `pcap` - an initially empty directory for PCAP files to be uploaded, processed, and stored @@ -40,7 +39,6 @@ Checking out the [Malcolm source code]({{ site.github.repository_url }}/tree/{{ and the following files of special note: * `auth.env` - the script `./scripts/auth_setup` prompts the user for the administrator credentials used by the Malcolm appliance, and `auth.env` is the environment file where those values are stored -* `net-map.json` - a JSON-formatted file mapping devices and network segments to their names * `docker-compose.yml` - the configuration file used by `docker-compose` to build, start, and stop an instance of the Malcolm appliance * `docker-compose-standalone.yml` - similar to `docker-compose.yml`, only used for the ["packaged"](#Packager) installation of Malcolm @@ -64,7 +62,6 @@ Then, go take a walk or something since it will be a while. When you're done, yo * `ghcr.io/idaholab/malcolm/freq` (based on `debian:11-slim`) * `ghcr.io/idaholab/malcolm/htadmin` (based on `debian:11-slim`) * `ghcr.io/idaholab/malcolm/logstash-oss` (based on `opensearchproject/logstash-oss-with-opensearch-output-plugin`) -* `ghcr.io/idaholab/malcolm/name-map-ui` (based on `alpine:3.17`) * `ghcr.io/idaholab/malcolm/netbox` (based on `netboxcommunity/netbox:latest`) * `ghcr.io/idaholab/malcolm/nginx-proxy` (based on `alpine:3.17`) * `ghcr.io/idaholab/malcolm/opensearch` (based on `opensearchproject/opensearch`) @@ -131,7 +128,6 @@ A minute or so after starting Malcolm, the following services will be accessible - OpenSearch Dashboards: https://localhost/dashboards/ - PCAP upload (web): https://localhost/upload/ - PCAP upload (sftp): sftp://USERNAME@127.0.0.1:8022/files/ - - Host and subnet name mapping editor: https://localhost/name-map-ui/ - NetBox: https://localhost/netbox/ - Account management: https://localhost:488/ - Documentation: https://localhost/readme/ diff --git a/docs/host-and-subnet-mapping.md b/docs/host-and-subnet-mapping.md deleted file mode 100644 index 288e87f06..000000000 --- a/docs/host-and-subnet-mapping.md +++ /dev/null @@ -1,52 +0,0 @@ -# Automatic host and subnet name assignment - -* [Automatic host and subnet name assignment](host-and-subnet-mapping.md#HostAndSubnetNaming) - - [Defining hostname and CIDR subnet names interface](host-and-subnet-mapping.md#NameMapUI) - - [Applying mapping changes](host-and-subnet-mapping.md#ApplyMapping) - - [IP/MAC address to hostname mapping](host-and-subnet-mapping.md#HostNaming) - - [CIDR subnet to network segment name mapping](host-and-subnet-mapping.md#SegmentNaming) - -## Defining hostname and CIDR subnet names interface - -A **Host and Subnet Name Mapping** editor is available at [https://localhost/name-map-ui/](https://localhost/name-map-ui/) if you are connecting locally. Upon loading, the editor is populated from `net-map.json`. - -This editor provides the following controls: - -* 🔎 **Search mappings** - narrow the list of visible items using a search filter -* **Type**, **Address** and **Name** *(column headings)* - sort the list of items by clicking a column header -* 📝 *(per item)* - modify the selected item -* 🚫 *(per item)* - remove the selected item -* 🖳 **host** / 🖧 **segment**, **Address**, **Name** and 💾 - save the item with these values (either adding a new item or updating the item being modified) -* 📥 **Import** - clear the list and replace it with the contents of an uploaded `net-map.json` file -* 📤 **Export** - format and download the list as a `net-map.json` file -* 💾 **Save Mappings** - format and store `net-map.json` in the Malcolm directory (replacing the existing `net-map.json` file) -* 🔁 **Restart Logstash** - restart log ingestion, parsing and enrichment - -![Host and Subnet Name Mapping Editor](./images/screenshots/malcolm_name_map_ui.png) - -## Applying mapping changes - -When changes are made to `net-map.json`, Malcolm's Logstash container must be restarted. The easiest way to do this is to restart malcolm via `restart` (see [Stopping and restarting Malcolm](running.md#StopAndRestart)) or by clicking the 🔁 **Restart Logstash** button in the [name mapping interface](#NameMapUI) interface. - -Restarting Logstash may take several minutes, after which log ingestion will be resumed. - -## IP/MAC address to hostname mapping - -The editor described above can be used to define names for network devices based on IP and/or MAC addresses in Zeek logs. A device is identified by its address(es) and name. - -As Zeek logs are processed into Malcolm's OpenSearch instance, the log's source and destination IP and MAC address fields (`source.ip`, `destination.ip`, `source.mac`, and `destination.mac`, respectively) are compared against the address-to-name map. When a match is found, a new field is added to the log: `source.device` or `destination.device`, depending on whether the matching address belongs to the originating or responding host. - -`source.device` and `destination.device` may each contain multiple values. For example, if both a host's source IP address and source MAC address were matched by two different lines, `source.device` would contain the name from both matching lines. - -## CIDR subnet to network segment name mapping - -The editor described above can be also used to define names for network segments based on IP addresses in Zeek logs. A network segment is defined by its CIDR-formatted subnet IP range(s) and subnet name. - -As Zeek logs are processed into Malcolm's OpenSearch instance, the log's source and destination IP address fields (`source.ip` and `destination.ip`, respectively) are compared against the address-to-subnet map. When a match is found, a new field is added to the log: `source.segment` or `destination.segment`, depending on whether the matching address belongs to the originating or responding host. - -`source.segment` and `destination.segment` may each contain multiple values. For example, overlapping subnets are defined, `source.segment` would contain the subnet values for both if `source.ip` belonged to both subnets. - -If both `source.segment` and `destination.segment` are added to a log, and if they contain different values, the tag `cross_segment` will be added to the log's `tags` field for convenient identification of cross-segment traffic. This traffic could be easily visualized using Arkime's **Connections** graph, by setting the **Src:** value to **Originating Network Segment** and the **Dst:** value to **Responding Network Segment**: - -![Cross-segment traffic in Connections](./images/screenshots/arkime_connections_segments.png) - diff --git a/docs/images/screenshots/malcolm_name_map_ui.png b/docs/images/screenshots/malcolm_name_map_ui.png deleted file mode 100644 index 0026a4184bcfbff0da825fcea246b800155fad52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 254066 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Ⓢ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 - - - - - - \ No newline at end of file diff --git a/name-map-ui/site/mapping.css b/name-map-ui/site/mapping.css deleted file mode 100644 index a153aea42..000000000 --- a/name-map-ui/site/mapping.css +++ /dev/null @@ -1,229 +0,0 @@ -/* Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. */ - -html { - font-family: sans-serif; - line-height: 1.15; -} - -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - font-size: 0.9375rem; - font-weight: 400; - line-height: 1.5; - color: #cecece; - text-align: left; - background-color: #272B30; -} - -body, -div, -dl, -dt, -dd, -ul, -ol, -li, -h1, -h2, -h3, -h4, -h5, -h6, -pre, -form, -fieldset, -input, -textarea, -p, -blockquote, -th, -td { - margin-top: 0; - margin-bottom: 0.5rem -} - -table { - border-collapse: collapse; - border-spacing: 0; -} - -fieldset, -img { - border: 0; -} - -img.center { - display: block; - margin-left: auto; - margin-right: auto; - width: 50%; -} - -ol, -ul, -dl { - margin-top: 0; - margin-bottom: 0.5rem -} - -caption, -th { - text-align: left; -} - -header { - float: left; - margin-bottom: 20px; - width: 100%; -} - -address { - margin-bottom: 1rem; - line-height: inherit -} - -#container { - width: 800px; - margin-top: auto; - margin-bottom: auto; - margin-right: auto; - margin-left: auto; -} - -.c1 { - width: 800px; - float: left; -} - -.c2 { - width: 355px; - float: left; -} - -.c3 { - width: 235px; - float: left; -} - -p { - margin-top: 0; - margin-bottom: 1rem -} - -h1 { - letter-spacing: 1px; - text-align: center; - text-shadow: #262729 0 -1px 0; -} - -h2 { - color: #bbb; - text-shadow: #262729 0 -1px 0; - font-weight: 300; - text-align: center; -} - -input { - padding: 2px 10px; - border-radius: 10px; - border: solid 1px #555; - float: center; - margin-right: 10px; - margin-bottom: 15px; -} - -input:focus { - border: none; - outline: none; - color: #007bff; - font-weight: bold; - border-top: solid 1px #aaa; - border-right: solid 1px #e6e6e6; - border-bottom: solid 1px #e6e6e6; - border-left: solid 1px #aaa; -} - -table { - width: 100%; - margin-bottom: 10px; -} - -th { - background-color: rgba(23, 25, 28, .8); - border: solid 1px rgba(255, 255, 255, .1); - padding: 10px 10px; - text-align: center; - font-weight: bold; - color: #ddd; - width: auto; -} - -tr:hover { - background-color: #454c54; -} - -td { - padding: 10px 10px; - border: solid 1px rgba(255, 255, 255, .1); - width: auto; -} - -td.update, -td.remove, -td.type { - text-align: center; -} - -td.address { - font-family: monospace; -} - -button.add-btn, -button.update-btn, -button.cancel-btn, -button.edit-item-btn, -button.remove-item-btn { - font-size: 1.33rem; - border: 2px #ddd; - border-radius: 4px; -} - -button.save-btn, -button.export-btn, -button.import-btn, -button.restart-btn { - font-size: 0.9rem; -} - -#container.mapping-page { - margin-top: 10px; -} - -#mapping { - margin-bottom: 20px; -} - -#mapping td.update, -#mapping td.remove { - width: 130px; -} - -#mapping td.add { - width: 300px; - text-align: center; -} - -#mapping td.foot { - text-align: center; -} - -#mapping input { - width: 130px; - margin: 0; - border-radius: 5px; -} - -#mapping input.search { - width: 250px; -} \ No newline at end of file diff --git a/name-map-ui/site/restart-logstash.php b/name-map-ui/site/restart-logstash.php deleted file mode 100644 index ce04650f0..000000000 --- a/name-map-ui/site/restart-logstash.php +++ /dev/null @@ -1,7 +0,0 @@ -$output"; -} -?> diff --git a/name-map-ui/site/upload.html b/name-map-ui/site/upload.html deleted file mode 100644 index 01bdc734a..000000000 --- a/name-map-ui/site/upload.html +++ /dev/null @@ -1,8 +0,0 @@ - - -
- Choose a file to upload: - -
- - diff --git a/name-map-ui/site/upload.php b/name-map-ui/site/upload.php deleted file mode 100644 index 98f85246c..000000000 --- a/name-map-ui/site/upload.php +++ /dev/null @@ -1,57 +0,0 @@ - 67108864) { - throw new RuntimeException('Exceeded filesize limit'); - } - - // check upload MIME type - $finfo = new finfo(FILEINFO_MIME_TYPE); - $fmime = $finfo->file($_FILES['upfile']['tmp_name']); - if (false === $ext = array_search($fmime, - array('json' => 'application/json', - 'txt' => 'text/plain'), - true)) { - throw new RuntimeException(sprintf('Invalid file format: "%s"', $fmime)); - } - - // give file unique name based on sha - $ftmpname = $_FILES['upfile']['tmp_name']; - $fdstname = sprintf('./upload/%s.%s', - sha1_file($_FILES['upfile']['tmp_name']), - $ext); - if (!move_uploaded_file($ftmpname, $fdstname)) { - throw new RuntimeException(sprintf('Failed to move uploaded file ("%s" -> "%s")', $ftmpname, $fdstname)); - } - - echo 'Success'; - -} catch (RuntimeException $e) { - error_log ($e->getMessage()); - echo $e->getMessage(); -} - -?> \ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf index d73d9c868..1cf7bb19e 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -61,10 +61,6 @@ http { server logstash:9600; } - upstream name-map-ui { - server name-map-ui:8080; - } - upstream netbox { server netbox:8080; } @@ -190,14 +186,6 @@ http { proxy_set_header Host dashboards-helper.malcolm.local; } - # name-map-ui (UI for mapping names to network hosts and subnets) - location /name-map-ui { - proxy_pass http://name-map-ui/; - proxy_redirect off; - proxy_set_header Host name-map-ui.malcolm.local; - proxy_cache off; - } - location ~* ^/extracted-files\b(.*) { proxy_pass http://extracted-file-http-server$1; proxy_redirect off; diff --git a/scripts/build.sh b/scripts/build.sh index 610ac5d2b..f8ac8d269 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -104,8 +104,7 @@ FILES_IN_IMAGES=( "/opt/arkime/etc/ipv4-address-space.csv;arkime" "/opt/arkime/etc/oui.txt;arkime" "/opt/arkime/bin/capture;arkime" - "/var/www/html/list.min.js;name-map-ui" - "/var/www/html/jquery.min.js;name-map-ui" + "/opt/netbox-devicetype-library/schema/components.json;netbox" "/opt/zeek/bin/zeek;zeek" "/opt/zeek/bin/spicyz;zeek" "/usr/share/nginx/html/index.html;nginx-proxy" diff --git a/scripts/control.py b/scripts/control.py index 55c25fde4..235e26d75 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -643,7 +643,6 @@ def logs(): print(" - OpenSearch Dashboards: https://localhost/dashboards/") print(" - PCAP upload (web): https://localhost/upload/") print(" - PCAP upload (sftp): sftp://username@127.0.0.1:8022/files/") - print(" - Host and subnet name mapping editor: https://localhost/name-map-ui/") print(" - NetBox: https://localhost/netbox/\n") print(" - Account management: https://localhost:488/\n") print(" - Documentation: https://localhost/readme/\n") diff --git a/scripts/demo/reset_and_auto_populate.sh b/scripts/demo/reset_and_auto_populate.sh index dd1997303..f5871c173 100755 --- a/scripts/demo/reset_and_auto_populate.sh +++ b/scripts/demo/reset_and_auto_populate.sh @@ -370,7 +370,7 @@ if [[ -f "$MALCOLM_DOCKER_COMPOSE" ]] && \ sleep 5 docker-compose -f "$MALCOLM_FILE" exec -T dashboards-helper /data/opensearch_read_only.py -i _cluster sleep 5 - for CONTAINER in filebeat logstash upload pcap-monitor zeek name-map-ui pcap-capture freq; do + for CONTAINER in filebeat logstash upload pcap-monitor zeek pcap-capture freq; do docker-compose -f "$MALCOLM_FILE" pause "$CONTAINER" || true done sleep 5 diff --git a/scripts/install.py b/scripts/install.py index 89b0a1409..e125f2fb3 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -1086,14 +1086,6 @@ def tweak_malcolm_runtime( r'(LOGSTASH_OUI_LOOKUP\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(autoOui)}", line ) - elif 'LOGSTASH_NETWORK_MAP_ENRICHMENT' in line: - # enrich network traffic metadata directly from net-map.json - line = re.sub( - r'(LOGSTASH_NETWORK_MAP_ENRICHMENT\s*:(\s*&\S+)?\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(not netboxLogstashEnrich)}", - line, - ) - elif 'LOGSTASH_NETBOX_ENRICHMENT' in line: # enrich network traffic metadata via NetBox API calls line = re.sub( diff --git a/scripts/malcolm_appliance_packager.sh b/scripts/malcolm_appliance_packager.sh index 53db37ca0..d16414c41 100755 --- a/scripts/malcolm_appliance_packager.sh +++ b/scripts/malcolm_appliance_packager.sh @@ -153,7 +153,6 @@ if mkdir "$DESTDIR"; then echo " - OpenSearch Dashboards: https://localhost/dashboards/" | tee -a "$README" echo " - PCAP upload (web): https://localhost/upload/" | tee -a "$README" echo " - PCAP upload (sftp): sftp://USERNAME@127.0.0.1:8022/files/" | tee -a "$README" - echo " - Host and subnet name mapping editor: https://localhost/name-map-ui/" | tee -a "$README" echo " - NetBox: https://localhost/netbox/" | tee -a "$README" echo " - Account management: https://localhost:488/" | tee -a "$README" echo " - Documentation: https://localhost/readme/" | tee -a "$README" diff --git a/shared/bin/service_check_passthrough.sh b/shared/bin/service_check_passthrough.sh index 2aa75b2e4..769994c1b 100755 --- a/shared/bin/service_check_passthrough.sh +++ b/shared/bin/service_check_passthrough.sh @@ -113,8 +113,6 @@ if [[ -n "$SERVICE" ]]; then PORT=10004 elif [[ "$SERVICE" == "logstash" ]]; then PORT=9600 - elif [[ "$SERVICE" == "name-map-ui" ]]; then - PORT=8080 elif [[ "$SERVICE" == "netbox" ]]; then PORT=8080 elif [[ "$SERVICE" == "opensearch" ]]; then From 8408951d75391771b1c658ca2d562b75ad0f0df3 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 21 Mar 2023 12:38:41 -0600 Subject: [PATCH 024/239] allow API to be reached without speacifying trailing slash delimiter in nginx --- Dockerfiles/opensearch.Dockerfile | 2 +- api/project/__init__.py | 56 ++++++++++++++----- api/project/config.py | 5 +- .../malcolm_api_loopback_webhook.json | 2 +- docker-compose-standalone.yml | 2 +- docker-compose.yml | 2 +- .../includes.chroot/etc/bash.bash_functions | 4 +- nginx/nginx.conf | 6 +- nginx/nginx_readonly.conf | 2 +- scripts/demo/reset_and_auto_populate.sh | 2 +- suricata/scripts/eve-clean-logs.sh | 2 +- 11 files changed, 59 insertions(+), 26 deletions(-) diff --git a/Dockerfiles/opensearch.Dockerfile b/Dockerfiles/opensearch.Dockerfile index be897a523..228bd8a1c 100644 --- a/Dockerfiles/opensearch.Dockerfile +++ b/Dockerfiles/opensearch.Dockerfile @@ -26,7 +26,7 @@ ENV TINI_VERSION v0.19.0 ARG OPENSEARCH_LOCAL=true ENV OPENSEARCH_LOCAL $OPENSEARCH_LOCAL -ARG MALCOLM_API_URL="http://api:5000/event" +ARG MALCOLM_API_URL="http://api:5000/mapi/event" ENV MALCOLM_API_URL $MALCOLM_API_URL ARG DISABLE_INSTALL_DEMO_CONFIG=true diff --git a/api/project/__init__.py b/api/project/__init__.py index df906f6f6..417a1a82c 100644 --- a/api/project/__init__.py +++ b/api/project/__init__.py @@ -478,8 +478,15 @@ def bucketfield(fieldname, current_request, urls=None): ) -@app.route("/agg", defaults={'fieldname': 'event.provider'}, methods=['GET', 'POST']) -@app.route("/agg/", methods=['GET', 'POST']) +@app.route( + f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/agg", + defaults={'fieldname': 'event.provider'}, + methods=['GET', 'POST'], +) +@app.route( + f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/agg/", + methods=['GET', 'POST'], +) def aggregate(fieldname): """Returns the aggregated values and counts for a given field name, see bucketfield @@ -506,8 +513,15 @@ def aggregate(fieldname): ) -@app.route("/document", defaults={'index': app.config["ARKIME_INDEX_PATTERN"]}, methods=['GET', 'POST']) -@app.route("/document/", methods=['GET', 'POST']) +@app.route( + f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/document", + defaults={'index': app.config["ARKIME_INDEX_PATTERN"]}, + methods=['GET', 'POST'], +) +@app.route( + f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/document/", + methods=['GET', 'POST'], +) def document(index): """Returns the matching document(s) from the specified index @@ -541,9 +555,15 @@ def document(index): ) -@app.route("/index", methods=['GET']) -@app.route("/indexes", methods=['GET']) -@app.route("/indices", methods=['GET']) +@app.route( + f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/index", methods=['GET'] +) +@app.route( + f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/indexes", methods=['GET'] +) +@app.route( + f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/indices", methods=['GET'] +) def indices(): """Provide a list of indices in the OpenSearch data store @@ -564,7 +584,9 @@ def indices(): ) -@app.route("/fields", methods=['GET']) +@app.route( + f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/fields", methods=['GET'] +) def fields(): """Provide a list of fields Malcolm "knows about" merged from Arkime's field table, Malcolm's OpenSearch template for the sessions indices, and Kibana's field list @@ -684,8 +706,10 @@ def fields(): return jsonify(fields=fields, total=len(fields)) -@app.route("/", methods=['GET']) -@app.route("/version", methods=['GET']) +@app.route(f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/", methods=['GET']) +@app.route( + f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/version", methods=['GET'] +) def version(): """Provides version information about Malcolm and the underlying OpenSearch instance @@ -720,7 +744,9 @@ def version(): ) -@app.route("/ping", methods=['GET']) +@app.route( + f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/ping", methods=['GET'] +) def ping(): """Says 'pong' (for a simple health check) @@ -735,8 +761,12 @@ def ping(): return jsonify(ping="pong") -@app.route('/alert', methods=['POST']) -@app.route('/event', methods=['POST']) +@app.route( + f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/alert", methods=['POST'] +) +@app.route( + f"{('/' + app.config['MALCOLM_API_PREFIX']) if app.config['MALCOLM_API_PREFIX'] else ''}/event", methods=['POST'] +) def event(): """Webhook that accepts alert data (like that from the OpenSearch Alerting API) to be reindexed into OpenSearch as session records (e.g., arkime_sessions3-*) for viewing diff --git a/api/project/config.py b/api/project/config.py index 644495da6..01a8010f2 100644 --- a/api/project/config.py +++ b/api/project/config.py @@ -10,12 +10,15 @@ class Config(object): ARKIME_INDEX_TIME_FIELD = f"{os.getenv('ARKIME_INDEX_TIME_FIELD', 'firstPacket')}" BUILD_DATE = f"{os.getenv('BUILD_DATE', 'unknown')}" DASHBOARDS_URL = f"{os.getenv('DASHBOARDS_URL', 'http://dashboards:5601/dashboards')}" + MALCOLM_API_PREFIX = f"{os.getenv('MALCOLM_API_PREFIX', 'mapi')}" MALCOLM_API_DEBUG = f"{os.getenv('MALCOLM_API_DEBUG', 'false')}" MALCOLM_TEMPLATE = f"{os.getenv('MALCOLM_TEMPLATE', 'malcolm_template')}" MALCOLM_VERSION = f"{os.getenv('MALCOLM_VERSION', 'unknown')}" OPENSEARCH_URL = f"{os.getenv('OPENSEARCH_URL', 'http://opensearch:9200')}" OPENSEARCH_LOCAL = f"{os.getenv('OPENSEARCH_LOCAL', 'true')}" OPENSEARCH_SSL_CERTIFICATE_VERIFICATION = f"{os.getenv('OPENSEARCH_SSL_CERTIFICATE_VERIFICATION', 'false')}" - OPENSEARCH_CREDS_CONFIG_FILE = f"{os.getenv('OPENSEARCH_CREDS_CONFIG_FILE', '/var/local/opensearch.primary.curlrc')}" + OPENSEARCH_CREDS_CONFIG_FILE = ( + f"{os.getenv('OPENSEARCH_CREDS_CONFIG_FILE', '/var/local/opensearch.primary.curlrc')}" + ) RESULT_SET_LIMIT = int(f"{os.getenv('RESULT_SET_LIMIT', '500')}") VCS_REVISION = f"{os.getenv('VCS_REVISION', 'unknown')}" diff --git a/dashboards/notifications/channels/malcolm_api_loopback_webhook.json b/dashboards/notifications/channels/malcolm_api_loopback_webhook.json index 7046f94d9..d4ae6c3ba 100644 --- a/dashboards/notifications/channels/malcolm_api_loopback_webhook.json +++ b/dashboards/notifications/channels/malcolm_api_loopback_webhook.json @@ -6,7 +6,7 @@ "description": "Malcolm API webhook to index OpenSearch alerts as session documents", "config_type": "webhook", "webhook": { - "url": "http://api:5000/alert", + "url": "http://api:5000/mapi/alert", "method": "POST", "header_params": { "Content-Type": "application/json" diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 965434c8a..b9c4c8714 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -977,7 +977,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro healthcheck: - test: ["CMD", "curl", "--silent", "--fail", "http://localhost:5000/ping"] + test: ["CMD", "curl", "--silent", "--fail", "http://localhost:5000/mapi/ping"] interval: 30s timeout: 15s retries: 3 diff --git a/docker-compose.yml b/docker-compose.yml index 8e2d4344d..a7e26922d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1050,7 +1050,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro healthcheck: - test: ["CMD", "curl", "--silent", "--fail", "http://localhost:5000/ping"] + test: ["CMD", "curl", "--silent", "--fail", "http://localhost:5000/mapi/ping"] interval: 30s timeout: 15s retries: 3 diff --git a/malcolm-iso/config/includes.chroot/etc/bash.bash_functions b/malcolm-iso/config/includes.chroot/etc/bash.bash_functions index 0a84bb064..f28514ab8 100644 --- a/malcolm-iso/config/includes.chroot/etc/bash.bash_functions +++ b/malcolm-iso/config/includes.chroot/etc/bash.bash_functions @@ -509,9 +509,9 @@ function malcolmmonitor () { select-pane -t 5 \; \ send-keys 'while true; do clear; free -m | head -n 2; sleep 60; done' C-m \; \ select-pane -t 6 \; \ - send-keys "while true; do clear; pushd ~/Malcolm >/dev/null 2>&1; docker-compose exec -u $(id -u) api curl -sSL 'http://localhost:5000/agg/event.dataset?from=1970' | python3 -m json.tool | grep -P '\b(doc_count|key)\b' | tr -d '\", ' | cut -d: -f2 | paste - - -d'\t\t' | head -n $(( (MAX_HEIGHT / 2) - 1 )) ; popd >/dev/null 2>&1; sleep 60; done" C-m \; \ + send-keys "while true; do clear; pushd ~/Malcolm >/dev/null 2>&1; docker-compose exec -u $(id -u) api curl -sSL 'http://localhost:5000/mapi/agg/event.dataset?from=1970' | python3 -m json.tool | grep -P '\b(doc_count|key)\b' | tr -d '\", ' | cut -d: -f2 | paste - - -d'\t\t' | head -n $(( (MAX_HEIGHT / 2) - 1 )) ; popd >/dev/null 2>&1; sleep 60; done" C-m \; \ select-pane -t 7 \; \ - send-keys "while true; do clear; pushd ~/Malcolm >/dev/null 2>&1; docker-compose exec -u $(id -u) api curl -sSL 'http://localhost:5000/agg?from=1970' | python3 -m json.tool | grep -P '\b(doc_count|key)\b' | tr -d '\", ' | cut -d: -f2 | paste - - -d'\t\t' ; popd >/dev/null 2>&1; sleep 60; done" C-m \; \ + send-keys "while true; do clear; pushd ~/Malcolm >/dev/null 2>&1; docker-compose exec -u $(id -u) api curl -sSL 'http://localhost:5000/mapi/agg?from=1970' | python3 -m json.tool | grep -P '\b(doc_count|key)\b' | tr -d '\", ' | cut -d: -f2 | paste - - -d'\t\t' ; popd >/dev/null 2>&1; sleep 60; done" C-m \; \ split-window -v \; \ select-pane -t 8 \; \ send-keys "while true; do clear; find ~/Malcolm/zeek-logs/extract_files -type f | sed 's@.*/\(.*\)/.*@\1@' | sort | uniq -c | sort -nr; sleep 60; done" C-m \; \ diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 1cf7bb19e..68bd4ccde 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -99,7 +99,7 @@ http { location /upload { proxy_http_version 1.1; proxy_set_header Connection ""; - proxy_pass http://upload/; + proxy_pass http://upload; proxy_redirect off; proxy_set_header Host upload.malcolm.local; proxy_request_buffering off; @@ -109,7 +109,7 @@ http { location /server/php { proxy_http_version 1.1; proxy_set_header Connection ""; - proxy_pass http://upload/server/php/; + proxy_pass http://upload/server/php; proxy_redirect off; proxy_set_header Host upload.malcolm.local; proxy_request_buffering off; @@ -224,7 +224,7 @@ http { # Malcolm API location /mapi { - proxy_pass http://api/; + proxy_pass http://api; proxy_redirect off; proxy_set_header Host api.malcolm.local; } diff --git a/nginx/nginx_readonly.conf b/nginx/nginx_readonly.conf index 5b4a7cc92..4b6b3ef34 100644 --- a/nginx/nginx_readonly.conf +++ b/nginx/nginx_readonly.conf @@ -168,7 +168,7 @@ http { # Malcolm API location /mapi { - proxy_pass http://api/; + proxy_pass http://api; proxy_redirect off; proxy_set_header Host api.malcolm.local; } diff --git a/scripts/demo/reset_and_auto_populate.sh b/scripts/demo/reset_and_auto_populate.sh index f5871c173..732646f48 100755 --- a/scripts/demo/reset_and_auto_populate.sh +++ b/scripts/demo/reset_and_auto_populate.sh @@ -319,7 +319,7 @@ if [[ -f "$MALCOLM_DOCKER_COMPOSE" ]] && \ # get the total number of session records in the database NEW_LOG_COUNT=$(( docker-compose -f "$MALCOLM_FILE" exec -u $(id -u) -T api \ - curl -sSL "http://localhost:5000/agg/event.provider?from=1970" | \ + curl -sSL "http://localhost:5000/mapi/agg/event.provider?from=1970" | \ jq -r '.. | .buckets? // empty | .[] | objects | [.doc_count] | join ("")' | \ awk '{s+=$1} END {print s}') 2>/dev/null ) if [[ $NEW_LOG_COUNT =~ $NUMERIC_REGEX ]] ; then diff --git a/suricata/scripts/eve-clean-logs.sh b/suricata/scripts/eve-clean-logs.sh index dd11f570e..e38a0e00b 100755 --- a/suricata/scripts/eve-clean-logs.sh +++ b/suricata/scripts/eve-clean-logs.sh @@ -24,7 +24,7 @@ if [[ "${SURICATA_LIVE_CAPTURE:-false}" != "true" ]]; then DOCUMENT_FOUND=$( curl -sSL -XPOST \ -H 'Content-Type: application/json' \ - 'http://api:5000/document' \ + 'http://api:5000/mapi/document' \ -d "{\"limit\":1,\"filter\":{\"log.file.path\":\"$(basename $LOGFILE)\"}}" 2>/dev/null \ | jq '.results | length' 2>/dev/null || echo '0') From 95792ccffacb0d7ea2153c8467ea44dec24312d0 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 21 Mar 2023 14:09:45 -0600 Subject: [PATCH 025/239] getting upload to work --- nginx/nginx.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 68bd4ccde..3a747828d 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -99,6 +99,7 @@ http { location /upload { proxy_http_version 1.1; proxy_set_header Connection ""; + rewrite ^/upload(.*)/?$ /$1 break; proxy_pass http://upload; proxy_redirect off; proxy_set_header Host upload.malcolm.local; From ea2604990e9cf5414001c50f4f821799892e85a5 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 22 Mar 2023 08:30:58 -0600 Subject: [PATCH 026/239] upload wip --- kubernetes/04-upload.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index ed577ac95..582527a8b 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -45,8 +45,12 @@ spec: name: process-env - configMapRef: name: ssl-env + - configMapRef: + name: main-auth-env env: - name: UPLOAD_DISABLED - value: "true" + value: "false" + - name: SITE_NAME + value: "Malcolm K8s Test" - name: VIRTUAL_HOST value: "upload.malcolm.local" From 5e06916e9ecb982e41896d6c51b7bf6f1689551a Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 22 Mar 2023 11:51:49 -0600 Subject: [PATCH 027/239] example working claim --- kubernetes/01-volumes.yml | 40 +++++++++++++++++++++++++++++++++++++++ kubernetes/04-upload.yml | 8 ++++++++ 2 files changed, 48 insertions(+) create mode 100644 kubernetes/01-volumes.yml diff --git a/kubernetes/01-volumes.yml b/kubernetes/01-volumes.yml new file mode 100644 index 000000000..08d250d51 --- /dev/null +++ b/kubernetes/01-volumes.yml @@ -0,0 +1,40 @@ +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: pcap-volume + namespace: malcolm +spec: + capacity: + storage: 500Gi + volumeMode: Filesystem + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: nfs + mountOptions: + - nfsvers=4.1 + - soft + - noac + - timeo=600 + - retrans=2 + nfs: + path: /malcolm/pcap + server: 10.9.0.226 + readOnly: false + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: pcap-claim + namespace: malcolm +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + volumeMode: Filesystem + resources: + requests: + storage: 500Gi + volumeName: pcap-volume diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 582527a8b..4ded70a4c 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -54,3 +54,11 @@ spec: value: "Malcolm K8s Test" - name: VIRTUAL_HOST value: "upload.malcolm.local" + volumeMounts: + - mountPath: "/var/www/upload/server/php/chroot/files" + name: upload-pcap-volume + subPath: upload + volumes: + - name: upload-pcap-volume + persistentVolumeClaim: + claimName: pcap-claim From f36d08fb96b5d41d00c941387652b3da56473420 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 22 Mar 2023 12:43:50 -0600 Subject: [PATCH 028/239] remove trailing slashes --- kubernetes/00-ingress.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kubernetes/00-ingress.yml b/kubernetes/00-ingress.yml index 60d3925e3..5165d7748 100644 --- a/kubernetes/00-ingress.yml +++ b/kubernetes/00-ingress.yml @@ -7,8 +7,10 @@ metadata: annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/backend-protocol: "https" - nginx.ingress.kubernetes.io/preserve-trailing-slash: "true" + nginx.ingress.kubernetes.io/preserve-trailing-slash: "false" nginx.ingress.kubernetes.io/ssl-passthrough: "false" + nginx.ingress.kubernetes.io/configuration-snippet: | + rewrite ^(.+)/$ $1 permanent; spec: rules: - http: From 0837203ddf251a4b5138a8c622754b2f8b1a8f23 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 22 Mar 2023 13:57:21 -0600 Subject: [PATCH 029/239] work in progress --- kubernetes/00-ingress.yml | 8 +- kubernetes/01-volumes.yml | 128 +++++++++++++++++++++++++++++++- kubernetes/05-pcap-monitor.yml | 7 ++ kubernetes/services-dev-plan.md | 21 +++++- 4 files changed, 159 insertions(+), 5 deletions(-) diff --git a/kubernetes/00-ingress.yml b/kubernetes/00-ingress.yml index 5165d7748..4fca03a62 100644 --- a/kubernetes/00-ingress.yml +++ b/kubernetes/00-ingress.yml @@ -9,8 +9,12 @@ metadata: nginx.ingress.kubernetes.io/backend-protocol: "https" nginx.ingress.kubernetes.io/preserve-trailing-slash: "false" nginx.ingress.kubernetes.io/ssl-passthrough: "false" - nginx.ingress.kubernetes.io/configuration-snippet: | - rewrite ^(.+)/$ $1 permanent; + nginx.ingress.kubernetes.io/proxy-body-size: "0" + nginx.ingress.kubernetes.io/proxy-read-timeout: "600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "600" + nginx.ingress.kubernetes.io/proxy-buffering: "on" + nginx.ingress.kubernetes.io/proxy-buffer-size: "512k" + nginx.ingress.kubernetes.io/proxy-max-temp-file-size: "8192m" spec: rules: - http: diff --git a/kubernetes/01-volumes.yml b/kubernetes/01-volumes.yml index 08d250d51..1379cedf9 100644 --- a/kubernetes/01-volumes.yml +++ b/kubernetes/01-volumes.yml @@ -6,7 +6,7 @@ metadata: namespace: malcolm spec: capacity: - storage: 500Gi + storage: 400Gi volumeMode: Filesystem accessModes: - ReadWriteMany @@ -36,5 +36,129 @@ spec: volumeMode: Filesystem resources: requests: - storage: 500Gi + storage: 400Gi volumeName: pcap-volume + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: zeek-volume + namespace: malcolm +spec: + capacity: + storage: 100Gi + volumeMode: Filesystem + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: nfs + mountOptions: + - nfsvers=4.1 + - soft + - noac + - timeo=600 + - retrans=2 + nfs: + path: /malcolm/zeek-logs + server: 10.9.0.226 + readOnly: false + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: zeek-claim + namespace: malcolm +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + volumeMode: Filesystem + resources: + requests: + storage: 100Gi + volumeName: zeek-volume + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: suricata-volume + namespace: malcolm +spec: + capacity: + storage: 100Gi + volumeMode: Filesystem + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: nfs + mountOptions: + - nfsvers=4.1 + - soft + - noac + - timeo=600 + - retrans=2 + nfs: + path: /malcolm/suricata-logs + server: 10.9.0.226 + readOnly: false + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: suricata-claim + namespace: malcolm +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + volumeMode: Filesystem + resources: + requests: + storage: 100Gi + volumeName: suricata-volume + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: config-volume + namespace: malcolm +spec: + capacity: + storage: 50Gi + volumeMode: Filesystem + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: nfs + mountOptions: + - nfsvers=4.1 + - soft + - noac + - timeo=600 + - retrans=2 + nfs: + path: /malcolm/config + server: 10.9.0.226 + readOnly: false + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: config-claim + namespace: malcolm +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + volumeMode: Filesystem + resources: + requests: + storage: 50Gi + volumeName: config-volume + diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml index f896a2b46..a548f50a0 100644 --- a/kubernetes/05-pcap-monitor.yml +++ b/kubernetes/05-pcap-monitor.yml @@ -47,3 +47,10 @@ spec: env: - name: PCAPMON_DISABLED value: "true" + volumeMounts: + - mountPath: "/pcap" + name: pcapmon-pcap-volume + volumes: + - name: pcapmon-pcap-volume + persistentVolumeClaim: + claimName: pcap-claim diff --git a/kubernetes/services-dev-plan.md b/kubernetes/services-dev-plan.md index e469c91b3..f97923067 100644 --- a/kubernetes/services-dev-plan.md +++ b/kubernetes/services-dev-plan.md @@ -31,4 +31,23 @@ See **support Malcolm deployment with Kubernetes** [idaholab/Malcolm#149](https: * zeek-live - See note for `pcap-capture`. * suricata-live - - See note for `pcap-capture`. \ No newline at end of file + - See note for `pcap-capture`. + +## Groupings + +Note: this is all dependent on if we can get things in the same deployment to communicate via hostname. + +* dashboards + - dashboards + - dashboards-helper +* logs/enrichment + - logstash + - freq +* netbox + - netbox + - netbox-redis + - netbox-redis-cache + - netbox-postgres +* proxy/auth + - nginx-proxy + - htadmin From 87944850f5034307e43178a6145c0486ba3e1de1 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 22 Mar 2023 16:19:41 -0600 Subject: [PATCH 030/239] work in progress, added opensearch-curlrc and ca-trust volumes across containers --- kubernetes/01-volumes.yml | 85 +++++++++++++++++++++++++++- kubernetes/02-opensearch.yml | 22 +++++++ kubernetes/03-dashboards.yml | 12 ++++ kubernetes/04-upload.yml | 5 ++ kubernetes/05-pcap-monitor.yml | 10 ++++ kubernetes/06-arkime.yml | 17 ++++++ kubernetes/07-api.yml | 12 ++++ kubernetes/08-dashboards-helper.yml | 12 ++++ kubernetes/09-zeek.yml | 12 ++++ kubernetes/10-suricata.yml | 12 ++++ kubernetes/11-file-monitor.yml | 7 +++ kubernetes/12-filebeat.yml | 17 ++++++ kubernetes/13-logstash.yml | 22 +++++++ kubernetes/15-netbox-redis.yml | 9 +++ kubernetes/16-netbox-redis-cache.yml | 9 +++ kubernetes/17-netbox-postgres.yml | 9 +++ kubernetes/18-netbox.yml | 14 +++++ kubernetes/19-htadmin.yml | 7 +++ kubernetes/20-pcap-capture.yml | 13 +++++ kubernetes/21-zeek-live.yml | 7 +++ kubernetes/22-suricata-live.yml | 7 +++ kubernetes/23-freq.yml | 7 +++ kubernetes/99-nginx-proxy.yml | 5 ++ kubernetes/dashboards-helper.env | 8 ++- kubernetes/opensearch.env | 18 ++++-- 25 files changed, 351 insertions(+), 7 deletions(-) diff --git a/kubernetes/01-volumes.yml b/kubernetes/01-volumes.yml index 1379cedf9..07a077c19 100644 --- a/kubernetes/01-volumes.yml +++ b/kubernetes/01-volumes.yml @@ -6,7 +6,7 @@ metadata: namespace: malcolm spec: capacity: - storage: 400Gi + storage: 250Gi volumeMode: Filesystem accessModes: - ReadWriteMany @@ -36,7 +36,7 @@ spec: volumeMode: Filesystem resources: requests: - storage: 400Gi + storage: 250Gi volumeName: pcap-volume --- @@ -162,3 +162,84 @@ spec: storage: 50Gi volumeName: config-volume +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: opensearch-volume + namespace: malcolm +spec: + capacity: + storage: 250Gi + volumeMode: Filesystem + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: nfs + mountOptions: + - nfsvers=4.1 + - soft + - noac + - timeo=600 + - retrans=2 + nfs: + path: /malcolm/opensearch + server: 10.9.0.226 + readOnly: false + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: opensearch-claim + namespace: malcolm +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + volumeMode: Filesystem + resources: + requests: + storage: 250Gi + volumeName: opensearch-volume + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: opensearch-backup-volume + namespace: malcolm +spec: + capacity: + storage: 250Gi + volumeMode: Filesystem + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: nfs + mountOptions: + - nfsvers=4.1 + - soft + - noac + - timeo=600 + - retrans=2 + nfs: + path: /malcolm/opensearch-backup + server: 10.9.0.226 + readOnly: false + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: opensearch-backup-claim + namespace: malcolm +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + volumeMode: Filesystem + resources: + requests: + storage: 250Gi + volumeName: opensearch-backup-volume diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index ffba73248..fe3282e02 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -47,3 +47,25 @@ spec: value: "true" - name: VIRTUAL_HOST value: "os.malcolm.local" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: /var/local/curlrc/configmap + name: opensearch-curlrc-volume + - mountPath: "/usr/share/opensearch/data" + name: opensearch-data-volume + - mountPath: "/opt/opensearch/backup" + name: opensearch-backup-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: opensearch-curlrc-volume + configMap: + name: opensearch-curlrc + - name: opensearch-data-volume + persistentVolumeClaim: + claimName: opensearch-claim + - name: opensearch-backup-volume + persistentVolumeClaim: + claimName: opensearch-backup-claim diff --git a/kubernetes/03-dashboards.yml b/kubernetes/03-dashboards.yml index 917316003..4dfc1eb8a 100644 --- a/kubernetes/03-dashboards.yml +++ b/kubernetes/03-dashboards.yml @@ -47,3 +47,15 @@ spec: value: "true" - name: VIRTUAL_HOST value: "dashboards.malcolm.local" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: /var/local/curlrc/configmap + name: opensearch-curlrc-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: opensearch-curlrc-volume + configMap: + name: opensearch-curlrc diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 4ded70a4c..6ed1b7d33 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -55,10 +55,15 @@ spec: - name: VIRTUAL_HOST value: "upload.malcolm.local" volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume - mountPath: "/var/www/upload/server/php/chroot/files" name: upload-pcap-volume subPath: upload volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust - name: upload-pcap-volume persistentVolumeClaim: claimName: pcap-claim diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml index a548f50a0..ef5c605f6 100644 --- a/kubernetes/05-pcap-monitor.yml +++ b/kubernetes/05-pcap-monitor.yml @@ -48,9 +48,19 @@ spec: - name: PCAPMON_DISABLED value: "true" volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: /var/local/curlrc/configmap + name: opensearch-curlrc-volume - mountPath: "/pcap" name: pcapmon-pcap-volume volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: opensearch-curlrc-volume + configMap: + name: opensearch-curlrc - name: pcapmon-pcap-volume persistentVolumeClaim: claimName: pcap-claim diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index ef29252c6..670cd389e 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -51,3 +51,20 @@ spec: value: "true" - name: VIRTUAL_HOST value: "arkime.malcolm.local" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: /var/local/curlrc/configmap + name: opensearch-curlrc-volume + - mountPath: "/data/pcap" + name: arkime-pcap-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: opensearch-curlrc-volume + configMap: + name: opensearch-curlrc + - name: arkime-pcap-volume + persistentVolumeClaim: + claimName: pcap-claim diff --git a/kubernetes/07-api.yml b/kubernetes/07-api.yml index 324f7f181..2b594433f 100644 --- a/kubernetes/07-api.yml +++ b/kubernetes/07-api.yml @@ -47,3 +47,15 @@ spec: value: "true" - name: VIRTUAL_HOST value: "api.malcolm.local" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: /var/local/curlrc/configmap + name: opensearch-curlrc-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: opensearch-curlrc-volume + configMap: + name: opensearch-curlrc diff --git a/kubernetes/08-dashboards-helper.yml b/kubernetes/08-dashboards-helper.yml index a093117a3..d5760edc2 100644 --- a/kubernetes/08-dashboards-helper.yml +++ b/kubernetes/08-dashboards-helper.yml @@ -49,3 +49,15 @@ spec: value: "true" - name: VIRTUAL_HOST value: "dashboards-helper.malcolm.local" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: /var/local/curlrc/configmap + name: opensearch-curlrc-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: opensearch-curlrc-volume + configMap: + name: opensearch-curlrc diff --git a/kubernetes/09-zeek.yml b/kubernetes/09-zeek.yml index 57709445f..62aa6055d 100644 --- a/kubernetes/09-zeek.yml +++ b/kubernetes/09-zeek.yml @@ -34,3 +34,15 @@ spec: env: - name: ZEEK_DISABLED value: "true" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: "/pcap" + name: zeek-pcap-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: zeek-pcap-volume + persistentVolumeClaim: + claimName: pcap-claim diff --git a/kubernetes/10-suricata.yml b/kubernetes/10-suricata.yml index 86a66ca5c..9365b0033 100644 --- a/kubernetes/10-suricata.yml +++ b/kubernetes/10-suricata.yml @@ -34,3 +34,15 @@ spec: env: - name: SURICATA_DISABLED value: "true" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: "/data/pcap" + name: suricata-pcap-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: suricata-pcap-volume + persistentVolumeClaim: + claimName: pcap-claim diff --git a/kubernetes/11-file-monitor.yml b/kubernetes/11-file-monitor.yml index ccd21230b..51a446b4a 100644 --- a/kubernetes/11-file-monitor.yml +++ b/kubernetes/11-file-monitor.yml @@ -47,3 +47,10 @@ spec: value: "true" - name: VIRTUAL_HOST value: "file-monitor.malcolm.local" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml index 3835913fa..c60a82c5a 100644 --- a/kubernetes/12-filebeat.yml +++ b/kubernetes/12-filebeat.yml @@ -53,3 +53,20 @@ spec: env: - name: FILEBEAT_DISABLED value: "true" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: /var/local/curlrc/configmap + name: opensearch-curlrc-volume + - mountPath: /certs/configmap + name: filebeat-certs-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: opensearch-curlrc-volume + configMap: + name: opensearch-curlrc + - name: filebeat-certs-volume + configMap: + name: filebeat-certs diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index d151392a1..5c34c042c 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -58,3 +58,25 @@ spec: env: - name: LOGSTASH_DISABLED value: "true" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: /var/local/curlrc/configmap + name: opensearch-curlrc-volume + - mountPath: /certs/configmap + name: logstash-certs-volume + - mountPath: /etc/configmap + name: logstash-maps-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: opensearch-curlrc-volume + configMap: + name: opensearch-curlrc + - name: logstash-certs-volume + configMap: + name: logstash-certs + - name: logstash-maps-volume + configMap: + name: logstash-maps \ No newline at end of file diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index fd4a28ff3..211fc7bc1 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -42,6 +42,15 @@ spec: name: ssl-env - configMapRef: name: netbox-env + - configMapRef: + name: netbox-redis-env env: - name: VIRTUAL_HOST value: "netbox-redis.malcolm.local" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index d913be028..14a41737d 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -42,6 +42,15 @@ spec: name: ssl-env - configMapRef: name: netbox-env + - configMapRef: + name: netbox-redis-cache-env env: - name: VIRTUAL_HOST value: "netbox-redis-cache.malcolm.local" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index 3dfbc93bf..0203de45a 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -42,6 +42,15 @@ spec: name: ssl-env - configMapRef: name: netbox-env + - configMapRef: + name: netbox-postgres-env env: - name: VIRTUAL_HOST value: "netbox-postgres.malcolm.local" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index 2b86a98ca..14f37e104 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -51,6 +51,20 @@ spec: name: ssl-env - configMapRef: name: netbox-env + - configMapRef: + name: netbox-netbox-env env: - name: VIRTUAL_HOST value: "netbox.malcolm.local" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: /usr/local/share/configmap + name: netbox-netmap-json-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: netbox-netmap-json-volume + configMap: + name: netbox-netmap-json diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml index 0f3195707..8e52d318f 100644 --- a/kubernetes/19-htadmin.yml +++ b/kubernetes/19-htadmin.yml @@ -47,3 +47,10 @@ spec: value: "true" - name: VIRTUAL_HOST value: "htadmin.malcolm.local" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust diff --git a/kubernetes/20-pcap-capture.yml b/kubernetes/20-pcap-capture.yml index b7fee5855..de52b8788 100644 --- a/kubernetes/20-pcap-capture.yml +++ b/kubernetes/20-pcap-capture.yml @@ -30,3 +30,16 @@ spec: env: - name: PCAP_CAPTURE_DISABLED value: "true" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + - mountPath: "/pcap" + name: capture-pcap-volume + subPath: upload + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust + - name: capture-pcap-volume + persistentVolumeClaim: + claimName: pcap-claim diff --git a/kubernetes/21-zeek-live.yml b/kubernetes/21-zeek-live.yml index 6026a5529..3404e9aa9 100644 --- a/kubernetes/21-zeek-live.yml +++ b/kubernetes/21-zeek-live.yml @@ -36,3 +36,10 @@ spec: env: - name: ZEEK_DISABLED value: "true" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust diff --git a/kubernetes/22-suricata-live.yml b/kubernetes/22-suricata-live.yml index e68c16f3b..8e8d65901 100644 --- a/kubernetes/22-suricata-live.yml +++ b/kubernetes/22-suricata-live.yml @@ -36,3 +36,10 @@ spec: env: - name: SURICATA_DISABLED value: "true" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust diff --git a/kubernetes/23-freq.yml b/kubernetes/23-freq.yml index a72ca4485..2a2baa301 100644 --- a/kubernetes/23-freq.yml +++ b/kubernetes/23-freq.yml @@ -45,3 +45,10 @@ spec: env: - name: VIRTUAL_HOST value: "freq.malcolm.local" + volumeMounts: + - mountPath: /var/local/ca-trust/configmap + name: var-local-catrust-volume + volumes: + - name: var-local-catrust-volume + configMap: + name: var-local-catrust diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index 1d4eac8c1..fd89c619b 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -65,6 +65,8 @@ spec: mountPath: /etc/nginx/certs/configmap - name: etc-nginx-certs-pem-volume mountPath: /etc/nginx/dhparam/configmap + - name: opensearch-curlrc-volume + mountPath: /var/local/curlrc/configmap volumes: - name: etc-nginx-volume configMap: @@ -78,3 +80,6 @@ spec: - name: etc-nginx-certs-pem-volume configMap: name: etc-nginx-certs-pem + - name: opensearch-curlrc-volume + configMap: + name: opensearch-curlrc diff --git a/kubernetes/dashboards-helper.env b/kubernetes/dashboards-helper.env index 5a9474e8b..1454d30a3 100644 --- a/kubernetes/dashboards-helper.env +++ b/kubernetes/dashboards-helper.env @@ -9,4 +9,10 @@ OPENSEARCH_INDEX_SIZE_PRUNE_LIMIT=0 OPENSEARCH_INDEX_SIZE_PRUNE_NAME_SORT=false # Parameters for the OpenSearch repository used for index snapshots ISM_SNAPSHOT_COMPRESSED=false -ISM_SNAPSHOT_REPO=logs \ No newline at end of file +ISM_SNAPSHOT_REPO=logs + +DASHBOARDS_URL=http://dashboards:5601/dashboards +ARKIME_INDEX_PATTERN=arkime_sessions3-* +ARKIME_INDEX_PATTERN_ID=arkime_sessions3-* +ARKIME_INDEX_TIME_FIELD=firstPacket +CREATE_OS_ARKIME_SESSION_INDEX=true diff --git a/kubernetes/opensearch.env b/kubernetes/opensearch.env index c0ffeceb2..bddeed0db 100644 --- a/kubernetes/opensearch.env +++ b/kubernetes/opensearch.env @@ -15,8 +15,8 @@ OPENSEARCH_URL=http://opensearch:9200 # user (with a "user:password" value) and "insecure" (if the certificate verification # setting below is 'false'). See cURL config file format at # https://everything.curl.dev/cmdline/configfile. This file is bind mounted locally -# from .opensearch.primary.curlrc as /var/local/opensearch.primary.curlrc -OPENSEARCH_CREDS_CONFIG_FILE=/var/local/opensearch.primary.curlrc +# from .opensearch.primary.curlrc as /var/local/curlrc/opensearch.primary.curlrc +OPENSEARCH_CREDS_CONFIG_FILE=/var/local/curlrc/opensearch.primary.curlrc # Whether or not connections to the primary remote OpenSearch instance require full # TLS certificate validation for the connection (this may fail if using self-signed # certificates). @@ -30,9 +30,19 @@ OPENSEARCH_SECONDARY_URL= # Used when OPENSEARCH_SECONDARY is 'true', the cURL-formatted config file contains login # credentials for the secondary OpenSearch instance. The comments describing # OPENSEARCH_CREDS_CONFIG_FILE above also apply here. This file is bind mounted locally -# from .opensearch.secondary.curlrc as /var/local/opensearch.secondary.curlrc -OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE=/var/local/opensearch.secondary.curlrc +# from .opensearch.secondary.curlrc as /var/local/curlrc/opensearch.secondary.curlrc +OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE=/var/local/curlrc/opensearch.secondary.curlrc # Whether or not connections to the secondary remote OpenSearch instance require full # TLS certificate validation for the connection (this may fail if using self-signed # certificates). OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION=false + +logger.level=WARN +bootstrap.memory_lock=true +MAX_LOCKED_MEMORY=unlimited +OPENSEARCH_JAVA_OPTS=-server -Xms4g -Xmx4g -Xss256k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true +discovery.type=single-node +cluster.routing.allocation.disk.threshold_enabled=false +cluster.routing.allocation.node_initial_primaries_recoveries=8 +indices.query.bool.max_clause_count=4096 +path.repo=/opt/opensearch/backup From 25577d53ed801fcb2347fa9eadc4cdabcba0d043 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 23 Mar 2023 07:38:59 -0600 Subject: [PATCH 031/239] remove --mkpath from rsync command, as we're only creating one level anyway and it's not included in older rereleases --- shared/bin/docker-uid-gid-setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/bin/docker-uid-gid-setup.sh b/shared/bin/docker-uid-gid-setup.sh index be3cfe384..f94cbf320 100755 --- a/shared/bin/docker-uid-gid-setup.sh +++ b/shared/bin/docker-uid-gid-setup.sh @@ -25,7 +25,7 @@ if [[ -n ${CONFIG_MAP_DIR} ]] && command -v rsync >/dev/null 2>&1; then awk '{print gsub("/","/"), $0}' | sort -n | cut -d' ' -f2- | \ while read CMDIR; do - rsync --recursive --mkpath --copy-links \ + rsync --recursive --copy-links \ "--usermap=*:${PUID:-${DEFAULT_UID}}" \ "--groupmap=*:${PGID:-${DEFAULT_GID}}" \ --exclude='..*' --exclude="${CONFIG_MAP_DIR}"/ --exclude=.dockerignore --exclude=.gitignore \ From 6533e57def0442067c876749fa86b7c5244b663b Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 23 Mar 2023 07:57:56 -0600 Subject: [PATCH 032/239] tweaks to images and env. variables --- Dockerfiles/arkime.Dockerfile | 2 ++ Dockerfiles/file-upload.Dockerfile | 6 +++--- Dockerfiles/opensearch.Dockerfile | 14 +++++++++++--- docker-compose-standalone.yml | 21 --------------------- docker-compose.yml | 21 --------------------- kubernetes/04-upload.yml | 4 ++-- kubernetes/arkime.env | 2 ++ kubernetes/upload.env | 1 + 8 files changed, 21 insertions(+), 50 deletions(-) create mode 100644 kubernetes/upload.env diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index 7ca954217..ea7d70b0b 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -101,6 +101,7 @@ ARG ARKIME_ECS_PROVIDER=arkime ARG ARKIME_ECS_DATASET=session ARG ARKIME_INTERFACE=eth0 ARG ARKIME_ANALYZE_PCAP_THREADS=1 +ARG OPENSEARCH_MAX_SHARDS_PER_NODE=2500 ARG WISE=off ARG VIEWER=on #Whether or not Arkime is in charge of deleting old PCAP files to reclaim space @@ -123,6 +124,7 @@ ENV ARKIME_ECS_PROVIDER $ARKIME_ECS_PROVIDER ENV ARKIME_ECS_DATASET $ARKIME_ECS_DATASET ENV ARKIME_DIR "/opt/arkime" ENV ARKIME_ANALYZE_PCAP_THREADS $ARKIME_ANALYZE_PCAP_THREADS +ENV OPENSEARCH_MAX_SHARDS_PER_NODE $OPENSEARCH_MAX_SHARDS_PER_NODE ENV WISE $WISE ENV VIEWER $VIEWER ENV MANAGE_PCAP_FILES $MANAGE_PCAP_FILES diff --git a/Dockerfiles/file-upload.Dockerfile b/Dockerfiles/file-upload.Dockerfile index 20f8a3538..5d0a8ddef 100644 --- a/Dockerfiles/file-upload.Dockerfile +++ b/Dockerfiles/file-upload.Dockerfile @@ -4,9 +4,6 @@ FROM debian:11-slim AS build ENV DEBIAN_FRONTEND noninteractive -ARG SITE_NAME="Capture File and Log Archive Upload" - -ENV SITE_NAME $SITE_NAME ENV JQUERY_FILE_UPLOAD_VERSION v9.19.1 ENV JQUERY_FILE_UPLOAD_URL "https://github.com/blueimp/jQuery-File-Upload/archive/${JQUERY_FILE_UPLOAD_VERSION}.tar.gz" @@ -52,6 +49,9 @@ ENV TERM xterm ARG PHP_VERSION=7.4 ENV PHP_VERSION $PHP_VERSION +ARG SITE_NAME="Capture File and Log Archive Upload" +ENV SITE_NAME $SITE_NAME + COPY --from=build /jQuery-File-Upload/ /var/www/upload/ RUN apt-get -q update && \ diff --git a/Dockerfiles/opensearch.Dockerfile b/Dockerfiles/opensearch.Dockerfile index 228bd8a1c..d295ee392 100644 --- a/Dockerfiles/opensearch.Dockerfile +++ b/Dockerfiles/opensearch.Dockerfile @@ -48,17 +48,25 @@ RUN yum install -y openssl util-linux procps rsync && \ echo -e 'cluster.name: "docker-cluster"\nnetwork.host: 0.0.0.0\nbootstrap.memory_lock: true\nhttp.cors.enabled: true\nhttp.cors.allow-origin: "*"\nhttp.cors.allow-methods: OPTIONS, HEAD, GET, POST, PUT, DELETE\nhttp.cors.allow-headers: "kbn-version, Origin, X-Requested-With, Content-Type, Accept, Engaged-Auth-Token Authorization"' > /usr/share/opensearch/config/opensearch.yml && \ sed -i "s/#[[:space:]]*\([0-9]*-[0-9]*:-XX:-\(UseConcMarkSweepGC\|UseCMSInitiatingOccupancyOnly\)\)/\1/" /usr/share/opensearch/config/jvm.options && \ sed -i "s/^[0-9][0-9]*\(-:-XX:\(+UseG1GC\|G1ReservePercent\|InitiatingHeapOccupancyPercent\)\)/$($OPENSEARCH_JAVA_HOME/bin/java -version 2>&1 | grep version | awk '{print $3}' | tr -d '\"' | cut -d. -f1)\1/" /usr/share/opensearch/config/jvm.options && \ - mkdir -p /var/local/ca-trust && \ - chown -R $PUSER:$PGROUP /usr/share/opensearch/config/opensearch.yml /var/local/ca-trust && \ + mkdir -p /var/local/ca-trust /opt/opensearch/backup && \ + chown -R $PUSER:$PGROUP /usr/share/opensearch/config/opensearch.yml /var/local/ca-trust /opt/opensearch/backup && \ chmod +x /usr/bin/tini && \ sed -i '/^[[:space:]]*runOpensearch.*/i /usr/local/bin/jdk-cacerts-auto-import.sh || true' /usr/share/opensearch/opensearch-docker-entrypoint.sh - COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/jdk-cacerts-auto-import.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic +ENV bootstrap.memory_lock "true" +ENV cluster.routing.allocation.disk.threshold_enabled "false" +ENV cluster.routing.allocation.node_initial_primaries_recoveries 8 +ENV discovery.type "single-node" +ENV indices.query.bool.max_clause_count 4096 +ENV logger.level "WARN" +ENV MAX_LOCKED_MEMORY "unlimited" +ENV path.repo "/opt/opensearch/backup" + VOLUME ["/var/local/ca-trust"] ENTRYPOINT ["/usr/bin/tini", \ diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index b9c4c8714..67cc557ac 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -368,16 +368,8 @@ services: << : *process-variables << : *ssl-variables << : *opensearch-variables - logger.level : 'WARN' - bootstrap.memory_lock : 'true' - MAX_LOCKED_MEMORY : 'unlimited' OPENSEARCH_JAVA_OPTS : '-server -Xms4g -Xmx4g -Xss256k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true' VIRTUAL_HOST : 'os.malcolm.local' - discovery.type : 'single-node' - cluster.routing.allocation.disk.threshold_enabled : 'false' - cluster.routing.allocation.node_initial_primaries_recoveries : 8 - indices.query.bool.max_clause_count : 4096 - path.repo : '/opt/opensearch/backup' ulimits: memlock: soft: -1 @@ -410,12 +402,7 @@ services: << : *ssl-variables << : *opensearch-variables << : *dashboards-helper-variables - DASHBOARDS_URL : 'http://dashboards:5601/dashboards' VIRTUAL_HOST : 'dashboards-helper.malcolm.local' - ARKIME_INDEX_PATTERN : 'arkime_sessions3-*' - ARKIME_INDEX_PATTERN_ID : 'arkime_sessions3-*' - ARKIME_INDEX_TIME_FIELD : 'firstPacket' - CREATE_OS_ARKIME_SESSION_INDEX : 'true' depends_on: - opensearch volumes: @@ -511,10 +498,6 @@ services: << : *filebeat-variables << : *common-upload-variables << : *common-beats-variables - FILEBEAT_ZEEK_LOG_PATH : '/zeek/current' - FILEBEAT_ZEEK_LOG_LIVE_PATH : '/zeek/live' - FILEBEAT_SURICATA_LOG_PATH : '/suricata' - FILEBEAT_NGINX_LOG_PATH : '/nginx' depends_on: - logstash ports: @@ -551,9 +534,6 @@ services: << : *common-upload-variables << : *arkime-variables VIRTUAL_HOST : 'arkime.malcolm.local' - OPENSEARCH_MAX_SHARDS_PER_NODE : 2500 - VIEWER : 'on' - WISE : 'on' ulimits: memlock: soft: -1 @@ -792,7 +772,6 @@ services: environment: << : *process-variables << : *ssl-variables - SITE_NAME : 'Capture File and Log Archive Upload' VIRTUAL_HOST : 'upload.malcolm.local' depends_on: - arkime diff --git a/docker-compose.yml b/docker-compose.yml index a7e26922d..087408af2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -371,16 +371,8 @@ services: << : *process-variables << : *ssl-variables << : *opensearch-variables - logger.level : 'INFO' - bootstrap.memory_lock : 'true' - MAX_LOCKED_MEMORY : 'unlimited' OPENSEARCH_JAVA_OPTS : '-server -Xms4g -Xmx4g -Xss256k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true' VIRTUAL_HOST : 'os.malcolm.local' - discovery.type : 'single-node' - cluster.routing.allocation.disk.threshold_enabled : 'false' - cluster.routing.allocation.node_initial_primaries_recoveries : 8 - indices.query.bool.max_clause_count : 4096 - path.repo : '/opt/opensearch/backup' ulimits: memlock: soft: -1 @@ -416,12 +408,7 @@ services: << : *ssl-variables << : *opensearch-variables << : *dashboards-helper-variables - DASHBOARDS_URL : 'http://dashboards:5601/dashboards' VIRTUAL_HOST : 'dashboards-helper.malcolm.local' - ARKIME_INDEX_PATTERN : 'arkime_sessions3-*' - ARKIME_INDEX_PATTERN_ID : 'arkime_sessions3-*' - ARKIME_INDEX_TIME_FIELD : 'firstPacket' - CREATE_OS_ARKIME_SESSION_INDEX : 'true' depends_on: - opensearch volumes: @@ -530,10 +517,6 @@ services: << : *filebeat-variables << : *common-upload-variables << : *common-beats-variables - FILEBEAT_ZEEK_LOG_PATH : '/zeek/current' - FILEBEAT_ZEEK_LOG_LIVE_PATH : '/zeek/live' - FILEBEAT_SURICATA_LOG_PATH : '/suricata' - FILEBEAT_NGINX_LOG_PATH : '/nginx' depends_on: - logstash ports: @@ -573,9 +556,6 @@ services: << : *common-upload-variables << : *arkime-variables VIRTUAL_HOST : 'arkime.malcolm.local' - OPENSEARCH_MAX_SHARDS_PER_NODE : 2500 - VIEWER : 'on' - WISE : 'on' ulimits: memlock: soft: -1 @@ -843,7 +823,6 @@ services: environment: << : *process-variables << : *ssl-variables - SITE_NAME : 'Capture File and Log Archive Upload' VIRTUAL_HOST : 'upload.malcolm.local' depends_on: - arkime diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 6ed1b7d33..a97feeb0f 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -47,11 +47,11 @@ spec: name: ssl-env - configMapRef: name: main-auth-env + - configMapRef: + name: upload-env env: - name: UPLOAD_DISABLED value: "false" - - name: SITE_NAME - value: "Malcolm K8s Test" - name: VIRTUAL_HOST value: "upload.malcolm.local" volumeMounts: diff --git a/kubernetes/arkime.env b/kubernetes/arkime.env index 5eea566bb..6edb657a9 100644 --- a/kubernetes/arkime.env +++ b/kubernetes/arkime.env @@ -6,3 +6,5 @@ ARKIME_ANALYZE_PCAP_THREADS=1 # MaxMind GeoIP database update API key (see # https://support.maxmind.com/hc/en-us/articles/4407116112539-Using-License-Keys) MAXMIND_GEOIP_DB_LICENSE_KEY=0 + +OPENSEARCH_MAX_SHARDS_PER_NODE=2500 \ No newline at end of file diff --git a/kubernetes/upload.env b/kubernetes/upload.env new file mode 100644 index 000000000..ebc4e3d25 --- /dev/null +++ b/kubernetes/upload.env @@ -0,0 +1 @@ +SITE_NAME=Capture File and Log Archive Upload \ No newline at end of file From 20bc6b8b64e74b675f476d6a5bf6b27403d9bf11 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 23 Mar 2023 09:16:11 -0600 Subject: [PATCH 033/239] give volume names uniqueness across the board --- kubernetes/02-opensearch.yml | 8 ++++---- kubernetes/03-dashboards.yml | 8 ++++---- kubernetes/04-upload.yml | 4 ++-- kubernetes/05-pcap-monitor.yml | 8 ++++---- kubernetes/06-arkime.yml | 8 ++++---- kubernetes/07-api.yml | 8 ++++---- kubernetes/08-dashboards-helper.yml | 8 ++++---- kubernetes/09-zeek.yml | 4 ++-- kubernetes/10-suricata.yml | 4 ++-- kubernetes/11-file-monitor.yml | 4 ++-- kubernetes/12-filebeat.yml | 8 ++++---- kubernetes/13-logstash.yml | 8 ++++---- kubernetes/15-netbox-redis.yml | 4 ++-- kubernetes/16-netbox-redis-cache.yml | 4 ++-- kubernetes/17-netbox-postgres.yml | 4 ++-- kubernetes/18-netbox.yml | 4 ++-- kubernetes/19-htadmin.yml | 4 ++-- kubernetes/20-pcap-capture.yml | 4 ++-- kubernetes/21-zeek-live.yml | 4 ++-- kubernetes/22-suricata-live.yml | 4 ++-- kubernetes/23-freq.yml | 4 ++-- kubernetes/99-nginx-proxy.yml | 20 ++++++++++---------- 22 files changed, 68 insertions(+), 68 deletions(-) diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index fe3282e02..4515bedbb 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -49,18 +49,18 @@ spec: value: "os.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: opensearch-var-local-catrust-volume - mountPath: /var/local/curlrc/configmap - name: opensearch-curlrc-volume + name: opensearch-opensearch-curlrc-volume - mountPath: "/usr/share/opensearch/data" name: opensearch-data-volume - mountPath: "/opt/opensearch/backup" name: opensearch-backup-volume volumes: - - name: var-local-catrust-volume + - name: opensearch-var-local-catrust-volume configMap: name: var-local-catrust - - name: opensearch-curlrc-volume + - name: opensearch-opensearch-curlrc-volume configMap: name: opensearch-curlrc - name: opensearch-data-volume diff --git a/kubernetes/03-dashboards.yml b/kubernetes/03-dashboards.yml index 4dfc1eb8a..efca04a0e 100644 --- a/kubernetes/03-dashboards.yml +++ b/kubernetes/03-dashboards.yml @@ -49,13 +49,13 @@ spec: value: "dashboards.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: dashboards-var-local-catrust-volume - mountPath: /var/local/curlrc/configmap - name: opensearch-curlrc-volume + name: dashboards-opensearch-curlrc-volume volumes: - - name: var-local-catrust-volume + - name: dashboards-var-local-catrust-volume configMap: name: var-local-catrust - - name: opensearch-curlrc-volume + - name: dashboards-opensearch-curlrc-volume configMap: name: opensearch-curlrc diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index a97feeb0f..59bc8c9c8 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -56,12 +56,12 @@ spec: value: "upload.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: upload-var-local-catrust-volume - mountPath: "/var/www/upload/server/php/chroot/files" name: upload-pcap-volume subPath: upload volumes: - - name: var-local-catrust-volume + - name: upload-var-local-catrust-volume configMap: name: var-local-catrust - name: upload-pcap-volume diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml index ef5c605f6..a06ef446d 100644 --- a/kubernetes/05-pcap-monitor.yml +++ b/kubernetes/05-pcap-monitor.yml @@ -49,16 +49,16 @@ spec: value: "true" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: pcapmon-var-local-catrust-volume - mountPath: /var/local/curlrc/configmap - name: opensearch-curlrc-volume + name: pcapmon-opensearch-curlrc-volume - mountPath: "/pcap" name: pcapmon-pcap-volume volumes: - - name: var-local-catrust-volume + - name: pcapmon-var-local-catrust-volume configMap: name: var-local-catrust - - name: opensearch-curlrc-volume + - name: pcapmon-opensearch-curlrc-volume configMap: name: opensearch-curlrc - name: pcapmon-pcap-volume diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index 670cd389e..3de5a0706 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -53,16 +53,16 @@ spec: value: "arkime.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: arkime-var-local-catrust-volume - mountPath: /var/local/curlrc/configmap - name: opensearch-curlrc-volume + name: arkime-opensearch-curlrc-volume - mountPath: "/data/pcap" name: arkime-pcap-volume volumes: - - name: var-local-catrust-volume + - name: arkime-var-local-catrust-volume configMap: name: var-local-catrust - - name: opensearch-curlrc-volume + - name: arkime-opensearch-curlrc-volume configMap: name: opensearch-curlrc - name: arkime-pcap-volume diff --git a/kubernetes/07-api.yml b/kubernetes/07-api.yml index 2b594433f..4d40587c4 100644 --- a/kubernetes/07-api.yml +++ b/kubernetes/07-api.yml @@ -49,13 +49,13 @@ spec: value: "api.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: api-var-local-catrust-volume - mountPath: /var/local/curlrc/configmap - name: opensearch-curlrc-volume + name: api-opensearch-curlrc-volume volumes: - - name: var-local-catrust-volume + - name: api-var-local-catrust-volume configMap: name: var-local-catrust - - name: opensearch-curlrc-volume + - name: api-opensearch-curlrc-volume configMap: name: opensearch-curlrc diff --git a/kubernetes/08-dashboards-helper.yml b/kubernetes/08-dashboards-helper.yml index d5760edc2..043ece66c 100644 --- a/kubernetes/08-dashboards-helper.yml +++ b/kubernetes/08-dashboards-helper.yml @@ -51,13 +51,13 @@ spec: value: "dashboards-helper.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: dashboards-helper-var-local-catrust-volume - mountPath: /var/local/curlrc/configmap - name: opensearch-curlrc-volume + name: dashboards-helper-opensearch-curlrc-volume volumes: - - name: var-local-catrust-volume + - name: dashboards-helper-var-local-catrust-volume configMap: name: var-local-catrust - - name: opensearch-curlrc-volume + - name: dashboards-helper-opensearch-curlrc-volume configMap: name: opensearch-curlrc diff --git a/kubernetes/09-zeek.yml b/kubernetes/09-zeek.yml index 62aa6055d..8d0cac005 100644 --- a/kubernetes/09-zeek.yml +++ b/kubernetes/09-zeek.yml @@ -36,11 +36,11 @@ spec: value: "true" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: zeek-var-local-catrust-volume - mountPath: "/pcap" name: zeek-pcap-volume volumes: - - name: var-local-catrust-volume + - name: zeek-var-local-catrust-volume configMap: name: var-local-catrust - name: zeek-pcap-volume diff --git a/kubernetes/10-suricata.yml b/kubernetes/10-suricata.yml index 9365b0033..8ab654234 100644 --- a/kubernetes/10-suricata.yml +++ b/kubernetes/10-suricata.yml @@ -36,11 +36,11 @@ spec: value: "true" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: suricata-var-local-catrust-volume - mountPath: "/data/pcap" name: suricata-pcap-volume volumes: - - name: var-local-catrust-volume + - name: suricata-var-local-catrust-volume configMap: name: var-local-catrust - name: suricata-pcap-volume diff --git a/kubernetes/11-file-monitor.yml b/kubernetes/11-file-monitor.yml index 51a446b4a..6fb64f324 100644 --- a/kubernetes/11-file-monitor.yml +++ b/kubernetes/11-file-monitor.yml @@ -49,8 +49,8 @@ spec: value: "file-monitor.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: file-monitor-var-local-catrust-volume volumes: - - name: var-local-catrust-volume + - name: file-monitor-var-local-catrust-volume configMap: name: var-local-catrust diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml index c60a82c5a..32f0d2823 100644 --- a/kubernetes/12-filebeat.yml +++ b/kubernetes/12-filebeat.yml @@ -55,16 +55,16 @@ spec: value: "true" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: filebeat-var-local-catrust-volume - mountPath: /var/local/curlrc/configmap - name: opensearch-curlrc-volume + name: filebeat-opensearch-curlrc-volume - mountPath: /certs/configmap name: filebeat-certs-volume volumes: - - name: var-local-catrust-volume + - name: filebeat-var-local-catrust-volume configMap: name: var-local-catrust - - name: opensearch-curlrc-volume + - name: filebeat-opensearch-curlrc-volume configMap: name: opensearch-curlrc - name: filebeat-certs-volume diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index 5c34c042c..67d8ec1d7 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -60,18 +60,18 @@ spec: value: "true" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: logstash-var-local-catrust-volume - mountPath: /var/local/curlrc/configmap - name: opensearch-curlrc-volume + name: logstash-opensearch-curlrc-volume - mountPath: /certs/configmap name: logstash-certs-volume - mountPath: /etc/configmap name: logstash-maps-volume volumes: - - name: var-local-catrust-volume + - name: logstash-var-local-catrust-volume configMap: name: var-local-catrust - - name: opensearch-curlrc-volume + - name: logstash-opensearch-curlrc-volume configMap: name: opensearch-curlrc - name: logstash-certs-volume diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index 211fc7bc1..bc5fd2fa6 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -49,8 +49,8 @@ spec: value: "netbox-redis.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: netbox-redis-var-local-catrust-volume volumes: - - name: var-local-catrust-volume + - name: netbox-redis-var-local-catrust-volume configMap: name: var-local-catrust diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index 14a41737d..6f99059cb 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -49,8 +49,8 @@ spec: value: "netbox-redis-cache.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: netbox-redis-cache-var-local-catrust-volume volumes: - - name: var-local-catrust-volume + - name: netbox-redis-cache-var-local-catrust-volume configMap: name: var-local-catrust diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index 0203de45a..e2d985411 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -49,8 +49,8 @@ spec: value: "netbox-postgres.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: netbox-postgres-var-local-catrust-volume volumes: - - name: var-local-catrust-volume + - name: netbox-postgres-var-local-catrust-volume configMap: name: var-local-catrust diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index 14f37e104..a55d28ddf 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -58,11 +58,11 @@ spec: value: "netbox.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: netbox-var-local-catrust-volume - mountPath: /usr/local/share/configmap name: netbox-netmap-json-volume volumes: - - name: var-local-catrust-volume + - name: netbox-var-local-catrust-volume configMap: name: var-local-catrust - name: netbox-netmap-json-volume diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml index 8e52d318f..5561b20a7 100644 --- a/kubernetes/19-htadmin.yml +++ b/kubernetes/19-htadmin.yml @@ -49,8 +49,8 @@ spec: value: "htadmin.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: htadmin-var-local-catrust-volume volumes: - - name: var-local-catrust-volume + - name: htadmin-var-local-catrust-volume configMap: name: var-local-catrust diff --git a/kubernetes/20-pcap-capture.yml b/kubernetes/20-pcap-capture.yml index de52b8788..ca4bd8b46 100644 --- a/kubernetes/20-pcap-capture.yml +++ b/kubernetes/20-pcap-capture.yml @@ -32,12 +32,12 @@ spec: value: "true" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: capture-var-local-catrust-volume - mountPath: "/pcap" name: capture-pcap-volume subPath: upload volumes: - - name: var-local-catrust-volume + - name: capture-var-local-catrust-volume configMap: name: var-local-catrust - name: capture-pcap-volume diff --git a/kubernetes/21-zeek-live.yml b/kubernetes/21-zeek-live.yml index 3404e9aa9..f939b1a29 100644 --- a/kubernetes/21-zeek-live.yml +++ b/kubernetes/21-zeek-live.yml @@ -38,8 +38,8 @@ spec: value: "true" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: zeek-live-var-local-catrust-volume volumes: - - name: var-local-catrust-volume + - name: zeek-live-var-local-catrust-volume configMap: name: var-local-catrust diff --git a/kubernetes/22-suricata-live.yml b/kubernetes/22-suricata-live.yml index 8e8d65901..f49a49e7e 100644 --- a/kubernetes/22-suricata-live.yml +++ b/kubernetes/22-suricata-live.yml @@ -38,8 +38,8 @@ spec: value: "true" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: suricata-live-var-local-catrust-volume volumes: - - name: var-local-catrust-volume + - name: suricata-live-var-local-catrust-volume configMap: name: var-local-catrust diff --git a/kubernetes/23-freq.yml b/kubernetes/23-freq.yml index 2a2baa301..254285c13 100644 --- a/kubernetes/23-freq.yml +++ b/kubernetes/23-freq.yml @@ -47,8 +47,8 @@ spec: value: "freq.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: var-local-catrust-volume + name: freq-var-local-catrust-volume volumes: - - name: var-local-catrust-volume + - name: freq-var-local-catrust-volume configMap: name: var-local-catrust diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index fd89c619b..bd9505328 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -57,29 +57,29 @@ spec: successThreshold: 1 failureThreshold: 10 volumeMounts: - - name: etc-nginx-volume + - name: nginx-etc-nginx-volume mountPath: /etc/nginx/configmap - - name: var-local-catrust-volume + - name: nginx-var-local-catrust-volume mountPath: /var/local/ca-trust/configmap - - name: etc-nginx-certs-volume + - name: nginx-etc-nginx-certs-volume mountPath: /etc/nginx/certs/configmap - - name: etc-nginx-certs-pem-volume + - name: nginx-etc-nginx-certs-pem-volume mountPath: /etc/nginx/dhparam/configmap - - name: opensearch-curlrc-volume + - name: nginx-opensearch-curlrc-volume mountPath: /var/local/curlrc/configmap volumes: - - name: etc-nginx-volume + - name: nginx-etc-nginx-volume configMap: name: etc-nginx - - name: var-local-catrust-volume + - name: nginx-var-local-catrust-volume configMap: name: var-local-catrust - - name: etc-nginx-certs-volume + - name: nginx-etc-nginx-certs-volume configMap: name: etc-nginx-certs - - name: etc-nginx-certs-pem-volume + - name: nginx-etc-nginx-certs-pem-volume configMap: name: etc-nginx-certs-pem - - name: opensearch-curlrc-volume + - name: nginx-opensearch-curlrc-volume configMap: name: opensearch-curlrc From 33ab78e0eaed4328e72c65597101446f8ed96625 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 23 Mar 2023 09:25:13 -0600 Subject: [PATCH 034/239] added rsync to arkime --- Dockerfiles/arkime.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index ea7d70b0b..cff6f4618 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -45,7 +45,6 @@ RUN apt-get -q update && \ python3-pip \ python3-setuptools \ python3-wheel \ - rsync \ sudo \ swig \ wget \ @@ -162,6 +161,7 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l python3-setuptools \ python3-wheel \ rename \ + rsync \ sudo \ supervisor \ vim-tiny \ From 097af9983cfe8943eaab838a641133bff9043d61 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 23 Mar 2023 09:41:59 -0600 Subject: [PATCH 035/239] fix opensearch curlrc files --- docker-compose-standalone.yml | 30 +++++++++++++++--------------- docker-compose.yml | 30 +++++++++++++++--------------- kubernetes/opensearch.env | 8 ++++---- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 67cc557ac..cc744289a 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -61,8 +61,8 @@ x-opensearch-variables: &opensearch-variables # user (with a "user:password" value) and "insecure" (if the certificate verification # setting below is 'false'). See cURL config file format at # https://everything.curl.dev/cmdline/configfile. This file is bind mounted locally - # from .opensearch.primary.curlrc as /var/local/opensearch.primary.curlrc - OPENSEARCH_CREDS_CONFIG_FILE : '/var/local/opensearch.primary.curlrc' + # from .opensearch.primary.curlrc as /var/local/curlrc/.opensearch.primary.curlrc + OPENSEARCH_CREDS_CONFIG_FILE : '/var/local/curlrc/.opensearch.primary.curlrc' # Whether or not connections to the primary remote OpenSearch instance require full # TLS certificate validation for the connection (this may fail if using self-signed # certificates). @@ -76,8 +76,8 @@ x-opensearch-variables: &opensearch-variables # Used when OPENSEARCH_SECONDARY is 'true', the cURL-formatted config file contains login # credentials for the secondary OpenSearch instance. The comments describing # OPENSEARCH_CREDS_CONFIG_FILE above also apply here. This file is bind mounted locally - # from .opensearch.secondary.curlrc as /var/local/opensearch.secondary.curlrc - OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE : '/var/local/opensearch.secondary.curlrc' + # from .opensearch.secondary.curlrc as /var/local/curlrc/.opensearch.secondary.curlrc + OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE : '/var/local/curlrc/.opensearch.secondary.curlrc' # Whether or not connections to the secondary remote OpenSearch instance require full # TLS certificate validation for the connection (this may fail if using self-signed # certificates). @@ -378,8 +378,8 @@ services: - IPC_LOCK volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./.opensearch.secondary.curlrc:/var/local/opensearch.secondary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro - ./opensearch:/usr/share/opensearch/data:delegated - ./opensearch-backup:/opt/opensearch/backup:delegated - ./opensearch/opensearch.keystore:/usr/share/opensearch/config/opensearch.keystore:rw @@ -407,8 +407,8 @@ services: - opensearch volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./.opensearch.secondary.curlrc:/var/local/opensearch.secondary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro healthcheck: test: ["CMD", "supervisorctl", "status", "cron", "maps"] interval: 60s @@ -433,7 +433,7 @@ services: - dashboards-helper volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro healthcheck: test: ["CMD", "curl", "--silent", "--fail", "http://localhost:5601/dashboards/api/status"] interval: 30s @@ -470,8 +470,8 @@ services: - "127.0.0.1:5044:5044" volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./.opensearch.secondary.curlrc:/var/local/opensearch.secondary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro - ./logstash/maps/malcolm_severity.yaml:/etc/malcolm_severity.yaml:ro - ./logstash/certs/ca.crt:/certs/ca.crt:ro - ./logstash/certs/server.crt:/certs/server.crt:ro @@ -504,7 +504,7 @@ services: - "127.0.0.1:5045:5045" volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - nginx-log-path:/nginx:ro - ./zeek-logs:/zeek - ./suricata-logs:/suricata @@ -542,7 +542,7 @@ services: - opensearch volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./pcap:/data/pcap - ./arkime-logs:/opt/arkime/logs - ./arkime-raw:/opt/arkime/raw @@ -750,7 +750,7 @@ services: - opensearch volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./zeek-logs:/zeek - ./pcap:/pcap healthcheck: @@ -954,7 +954,7 @@ services: VIRTUAL_HOST : 'api.malcolm.local' volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro healthcheck: test: ["CMD", "curl", "--silent", "--fail", "http://localhost:5000/mapi/ping"] interval: 30s diff --git a/docker-compose.yml b/docker-compose.yml index 087408af2..22d77bd38 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -61,8 +61,8 @@ x-opensearch-variables: &opensearch-variables # user (with a "user:password" value) and "insecure" (if the certificate verification # setting below is 'false'). See cURL config file format at # https://everything.curl.dev/cmdline/configfile. This file is bind mounted locally - # from .opensearch.primary.curlrc as /var/local/opensearch.primary.curlrc - OPENSEARCH_CREDS_CONFIG_FILE : '/var/local/opensearch.primary.curlrc' + # from .opensearch.primary.curlrc as /var/local/curlrc/.opensearch.primary.curlrc + OPENSEARCH_CREDS_CONFIG_FILE : '/var/local/curlrc/.opensearch.primary.curlrc' # Whether or not connections to the primary remote OpenSearch instance require full # TLS certificate validation for the connection (this may fail if using self-signed # certificates). @@ -76,8 +76,8 @@ x-opensearch-variables: &opensearch-variables # Used when OPENSEARCH_SECONDARY is 'true', the cURL-formatted config file contains login # credentials for the secondary OpenSearch instance. The comments describing # OPENSEARCH_CREDS_CONFIG_FILE above also apply here. This file is bind mounted locally - # from .opensearch.secondary.curlrc as /var/local/opensearch.secondary.curlrc - OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE : '/var/local/opensearch.secondary.curlrc' + # from .opensearch.secondary.curlrc as /var/local/curlrc/.opensearch.secondary.curlrc + OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE : '/var/local/curlrc/.opensearch.secondary.curlrc' # Whether or not connections to the secondary remote OpenSearch instance require full # TLS certificate validation for the connection (this may fail if using self-signed # certificates). @@ -381,8 +381,8 @@ services: - IPC_LOCK volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./.opensearch.secondary.curlrc:/var/local/opensearch.secondary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro - ./opensearch:/usr/share/opensearch/data:delegated - ./opensearch-backup:/opt/opensearch/backup:delegated - ./opensearch/opensearch.keystore:/usr/share/opensearch/config/opensearch.keystore:rw @@ -413,8 +413,8 @@ services: - opensearch volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./.opensearch.secondary.curlrc:/var/local/opensearch.secondary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro healthcheck: test: ["CMD", "supervisorctl", "status", "cron", "maps"] interval: 60s @@ -442,7 +442,7 @@ services: - dashboards-helper volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro healthcheck: test: ["CMD", "curl", "--silent", "--fail", "http://localhost:5601/dashboards/api/status"] interval: 30s @@ -482,8 +482,8 @@ services: - "127.0.0.1:5044:5044" volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./.opensearch.secondary.curlrc:/var/local/opensearch.secondary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.orig.yml:ro - ./logstash/pipelines:/usr/share/logstash/malcolm-pipelines.available:ro - ./logstash/patterns:/usr/share/logstash/malcolm-patterns:ro @@ -523,7 +523,7 @@ services: - "127.0.0.1:5045:5045" volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - nginx-log-path:/nginx:ro - ./zeek-logs:/zeek - ./suricata-logs:/suricata @@ -564,7 +564,7 @@ services: - opensearch volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./pcap:/data/pcap - ./arkime-logs:/opt/arkime/logs - ./arkime-raw:/opt/arkime/raw @@ -798,7 +798,7 @@ services: - opensearch volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./zeek-logs:/zeek - ./pcap:/pcap healthcheck: @@ -1027,7 +1027,7 @@ services: VIRTUAL_HOST : 'api.malcolm.local' volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro healthcheck: test: ["CMD", "curl", "--silent", "--fail", "http://localhost:5000/mapi/ping"] interval: 30s diff --git a/kubernetes/opensearch.env b/kubernetes/opensearch.env index bddeed0db..3b8650b6b 100644 --- a/kubernetes/opensearch.env +++ b/kubernetes/opensearch.env @@ -15,8 +15,8 @@ OPENSEARCH_URL=http://opensearch:9200 # user (with a "user:password" value) and "insecure" (if the certificate verification # setting below is 'false'). See cURL config file format at # https://everything.curl.dev/cmdline/configfile. This file is bind mounted locally -# from .opensearch.primary.curlrc as /var/local/curlrc/opensearch.primary.curlrc -OPENSEARCH_CREDS_CONFIG_FILE=/var/local/curlrc/opensearch.primary.curlrc +# from .opensearch.primary.curlrc as /var/local/curlrc/.opensearch.primary.curlrc +OPENSEARCH_CREDS_CONFIG_FILE=/var/local/curlrc/.opensearch.primary.curlrc # Whether or not connections to the primary remote OpenSearch instance require full # TLS certificate validation for the connection (this may fail if using self-signed # certificates). @@ -30,8 +30,8 @@ OPENSEARCH_SECONDARY_URL= # Used when OPENSEARCH_SECONDARY is 'true', the cURL-formatted config file contains login # credentials for the secondary OpenSearch instance. The comments describing # OPENSEARCH_CREDS_CONFIG_FILE above also apply here. This file is bind mounted locally -# from .opensearch.secondary.curlrc as /var/local/curlrc/opensearch.secondary.curlrc -OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE=/var/local/curlrc/opensearch.secondary.curlrc +# from .opensearch.secondary.curlrc as /var/local/curlrc/.opensearch.secondary.curlrc +OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE=/var/local/curlrc/.opensearch.secondary.curlrc # Whether or not connections to the secondary remote OpenSearch instance require full # TLS certificate validation for the connection (this may fail if using self-signed # certificates). From 4ee679350b9eadbcb86ccbc02ae62756b71c49f9 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 23 Mar 2023 09:44:47 -0600 Subject: [PATCH 036/239] fix opensearch curlrc files --- api/project/config.py | 2 +- arkime/scripts/arkime-needs-upgrade.sh | 2 +- arkime/scripts/docker_entrypoint.sh | 2 +- arkime/scripts/initarkime.sh | 2 +- .../scripts/create-arkime-sessions-index.sh | 4 ++-- dashboards/scripts/docker_entrypoint.sh | 2 +- dashboards/scripts/index-refresh.py | 2 +- docs/contributing-local-modifications.md | 20 +++++++++---------- logstash/scripts/logstash-start.sh | 4 ++-- shared/bin/opensearch_index_size_prune.py | 2 +- shared/bin/opensearch_read_only.py | 2 +- shared/bin/opensearch_status.sh | 2 +- shared/bin/pcap_watcher.py | 2 +- 13 files changed, 24 insertions(+), 24 deletions(-) diff --git a/api/project/config.py b/api/project/config.py index 01a8010f2..5c79c0c9c 100644 --- a/api/project/config.py +++ b/api/project/config.py @@ -18,7 +18,7 @@ class Config(object): OPENSEARCH_LOCAL = f"{os.getenv('OPENSEARCH_LOCAL', 'true')}" OPENSEARCH_SSL_CERTIFICATE_VERIFICATION = f"{os.getenv('OPENSEARCH_SSL_CERTIFICATE_VERIFICATION', 'false')}" OPENSEARCH_CREDS_CONFIG_FILE = ( - f"{os.getenv('OPENSEARCH_CREDS_CONFIG_FILE', '/var/local/opensearch.primary.curlrc')}" + f"{os.getenv('OPENSEARCH_CREDS_CONFIG_FILE', '/var/local/curlrc/.opensearch.primary.curlrc')}" ) RESULT_SET_LIMIT = int(f"{os.getenv('RESULT_SET_LIMIT', '500')}") VCS_REVISION = f"{os.getenv('VCS_REVISION', 'unknown')}" diff --git a/arkime/scripts/arkime-needs-upgrade.sh b/arkime/scripts/arkime-needs-upgrade.sh index 446731ccd..29045af49 100755 --- a/arkime/scripts/arkime-needs-upgrade.sh +++ b/arkime/scripts/arkime-needs-upgrade.sh @@ -4,7 +4,7 @@ OPENSEARCH_URL=${OPENSEARCH_URL:-"http://opensearch:9200"} OPENSEARCH_LOCAL=${OPENSEARCH_LOCAL:-"true"} -OPENSEARCH_CREDS_CONFIG_FILE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/opensearch.primary.curlrc"} +OPENSEARCH_CREDS_CONFIG_FILE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/curlrc/.opensearch.primary.curlrc"} if [[ "$OPENSEARCH_LOCAL" == "false" ]] && [[ -r "$OPENSEARCH_CREDS_CONFIG_FILE" ]]; then CURL_CONFIG_PARAMS=( --config diff --git a/arkime/scripts/docker_entrypoint.sh b/arkime/scripts/docker_entrypoint.sh index a43e8f3c0..77e5a684e 100755 --- a/arkime/scripts/docker_entrypoint.sh +++ b/arkime/scripts/docker_entrypoint.sh @@ -12,7 +12,7 @@ ARKIME_DIR=${ARKIME_DIR:-"/opt/arkime"} OPENSEARCH_URL_FINAL=${OPENSEARCH_URL:-"http://opensearch:9200"} OPENSEARCH_LOCAL=${OPENSEARCH_LOCAL:-"true"} -OPENSEARCH_CREDS_CONFIG_FILE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/opensearch.primary.curlrc"} +OPENSEARCH_CREDS_CONFIG_FILE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/curlrc/.opensearch.primary.curlrc"} if [[ "$OPENSEARCH_LOCAL" == "false" ]] && [[ -r "$OPENSEARCH_CREDS_CONFIG_FILE" ]]; then # need to build the opensearch URL (including username/password) by combining # OPENSEARCH_URL and parameters from OPENSEARCH_CREDS_CONFIG_FILE diff --git a/arkime/scripts/initarkime.sh b/arkime/scripts/initarkime.sh index 29f6c232c..86089a386 100755 --- a/arkime/scripts/initarkime.sh +++ b/arkime/scripts/initarkime.sh @@ -5,7 +5,7 @@ OPENSEARCH_URL=${OPENSEARCH_URL:-"http://opensearch:9200"} OPENSEARCH_LOCAL=${OPENSEARCH_LOCAL:-"true"} OPENSEARCH_SSL_CERTIFICATE_VERIFICATION=${OPENSEARCH_SSL_CERTIFICATE_VERIFICATION:-"false"} -OPENSEARCH_CREDS_CONFIG_FILE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/opensearch.primary.curlrc"} +OPENSEARCH_CREDS_CONFIG_FILE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/curlrc/.opensearch.primary.curlrc"} if [[ "$OPENSEARCH_LOCAL" == "false" ]] && [[ -r "$OPENSEARCH_CREDS_CONFIG_FILE" ]]; then CURL_CONFIG_PARAMS=( --config diff --git a/dashboards/scripts/create-arkime-sessions-index.sh b/dashboards/scripts/create-arkime-sessions-index.sh index feb6b9497..985daa03a 100755 --- a/dashboards/scripts/create-arkime-sessions-index.sh +++ b/dashboards/scripts/create-arkime-sessions-index.sh @@ -38,7 +38,7 @@ if [[ "$CREATE_OS_ARKIME_SESSION_INDEX" = "true" ]] ; then if [[ "$LOOP" == "primary" ]]; then OPENSEARCH_URL_TO_USE=${OPENSEARCH_URL:-"http://opensearch:9200"} OPENSEARCH_LOCAL=${OPENSEARCH_LOCAL:-"true"} - OPENSEARCH_CREDS_CONFIG_FILE_TO_USE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/opensearch.primary.curlrc"} + OPENSEARCH_CREDS_CONFIG_FILE_TO_USE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/curlrc/.opensearch.primary.curlrc"} if [[ "$OPENSEARCH_LOCAL" == "false" ]] && [[ -r "$OPENSEARCH_CREDS_CONFIG_FILE_TO_USE" ]]; then CURL_CONFIG_PARAMS=( --config @@ -51,7 +51,7 @@ if [[ "$CREATE_OS_ARKIME_SESSION_INDEX" = "true" ]] ; then elif [[ "$LOOP" == "secondary" ]] && [[ "${OPENSEARCH_SECONDARY:-"false"}" == "true" ]] && [[ -n "${OPENSEARCH_SECONDARY_URL:-""}" ]]; then OPENSEARCH_URL_TO_USE=$OPENSEARCH_SECONDARY_URL OPENSEARCH_LOCAL=false - OPENSEARCH_CREDS_CONFIG_FILE_TO_USE=${OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE:-"/var/local/opensearch.secondary.curlrc"} + OPENSEARCH_CREDS_CONFIG_FILE_TO_USE=${OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE:-"/var/local/curlrc/.opensearch.secondary.curlrc"} if [[ -r "$OPENSEARCH_CREDS_CONFIG_FILE_TO_USE" ]]; then CURL_CONFIG_PARAMS=( --config diff --git a/dashboards/scripts/docker_entrypoint.sh b/dashboards/scripts/docker_entrypoint.sh index 7b41be316..ff9ee00f5 100755 --- a/dashboards/scripts/docker_entrypoint.sh +++ b/dashboards/scripts/docker_entrypoint.sh @@ -6,7 +6,7 @@ FINAL_YML=/usr/share/opensearch-dashboards/config/opensearch_dashboards.yml OPENSEARCH_SSL_CERTIFICATE_VERIFICATION=${OPENSEARCH_SSL_CERTIFICATE_VERIFICATION:-"false"} OPENSEARCH_LOCAL=${OPENSEARCH_LOCAL:-"true"} -OPENSEARCH_CREDS_CONFIG_FILE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/opensearch.primary.curlrc"} +OPENSEARCH_CREDS_CONFIG_FILE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/curlrc/.opensearch.primary.curlrc"} if [[ -f "$ORIG_YML" ]]; then cp "$ORIG_YML" "$FINAL_YML" diff --git a/dashboards/scripts/index-refresh.py b/dashboards/scripts/index-refresh.py index 3f4bb3518..7e5c84134 100755 --- a/dashboards/scripts/index-refresh.py +++ b/dashboards/scripts/index-refresh.py @@ -88,7 +88,7 @@ def main(): dest='opensearchCurlRcFile', metavar='', type=str, - default=os.getenv('OPENSEARCH_CREDS_CONFIG_FILE', '/var/local/opensearch.primary.curlrc'), + default=os.getenv('OPENSEARCH_CREDS_CONFIG_FILE', '/var/local/curlrc/.opensearch.primary.curlrc'), help='cURL.rc formatted file containing OpenSearch connection parameters', ) parser.add_argument( diff --git a/docs/contributing-local-modifications.md b/docs/contributing-local-modifications.md index 89dfc4aaf..4aa075d32 100644 --- a/docs/contributing-local-modifications.md +++ b/docs/contributing-local-modifications.md @@ -10,28 +10,28 @@ Some configuration changes can be put in place by modifying local copies of conf $ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml opensearch: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./.opensearch.secondary.curlrc:/var/local/opensearch.secondary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro - ./opensearch/opensearch.keystore:/usr/share/opensearch/config/opensearch.keystore:rw - ./opensearch:/usr/share/opensearch/data:delegated - ./opensearch-backup:/opt/opensearch/backup:delegated dashboards-helper: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro dashboards: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro logstash: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./.opensearch.secondary.curlrc:/var/local/opensearch.secondary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro - ./logstash/maps/malcolm_severity.yaml:/etc/malcolm_severity.yaml:ro - ./logstash/certs/ca.crt:/certs/ca.crt:ro - ./logstash/certs/server.crt:/certs/server.crt:ro - ./logstash/certs/server.key:/certs/server.key:ro filebeat: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./zeek-logs:/zeek - ./suricata-logs:/suricata - ./filebeat/certs/ca.crt:/certs/ca.crt:ro @@ -40,7 +40,7 @@ $ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml arkime: - ./auth.env - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./pcap:/data/pcap - ./arkime-logs:/opt/arkime/logs - ./arkime-raw:/opt/arkime/raw @@ -74,7 +74,7 @@ $ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml - ./pcap/upload:/pcap pcap-monitor: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./zeek-logs:/zeek - ./pcap:/pcap upload: @@ -90,7 +90,7 @@ $ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml - ./nginx/ca-trust:/var/local/ca-trust:ro api: - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro nginx-proxy: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./nginx/nginx_ldap.conf:/etc/nginx/nginx_ldap.conf:ro diff --git a/logstash/scripts/logstash-start.sh b/logstash/scripts/logstash-start.sh index 6d91c3240..94e38deb1 100755 --- a/logstash/scripts/logstash-start.sh +++ b/logstash/scripts/logstash-start.sh @@ -35,8 +35,8 @@ OPENSEARCH_SECONDARY=${OPENSEARCH_SECONDARY:-"false"} OPENSEARCH_SSL_CERTIFICATE_VERIFICATION=${OPENSEARCH_SSL_CERTIFICATE_VERIFICATION:-"false"} OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION=${OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION:-"false"} -OPENSEARCH_CREDS_CONFIG_FILE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/opensearch.primary.curlrc"} -OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE=${OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE:-"/var/local/opensearch.secondary.curlrc"} +OPENSEARCH_CREDS_CONFIG_FILE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/curlrc/.opensearch.primary.curlrc"} +OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE=${OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE:-"/var/local/curlrc/.opensearch.secondary.curlrc"} [[ "$OPENSEARCH_SECONDARY" != "true" ]] && OPENSEARCH_SECONDARY_URL= export OPENSEARCH_SECONDARY_URL diff --git a/shared/bin/opensearch_index_size_prune.py b/shared/bin/opensearch_index_size_prune.py index 01783c4a1..e2a7e5249 100755 --- a/shared/bin/opensearch_index_size_prune.py +++ b/shared/bin/opensearch_index_size_prune.py @@ -77,7 +77,7 @@ def main(): dest='opensearchCurlRcFile', metavar='', type=str, - default=os.getenv('OPENSEARCH_CREDS_CONFIG_FILE', '/var/local/opensearch.primary.curlrc'), + default=os.getenv('OPENSEARCH_CREDS_CONFIG_FILE', '/var/local/curlrc/.opensearch.primary.curlrc'), help='cURL.rc formatted file containing OpenSearch connection parameters', ) parser.add_argument( diff --git a/shared/bin/opensearch_read_only.py b/shared/bin/opensearch_read_only.py index 98278aac7..8fef64818 100755 --- a/shared/bin/opensearch_read_only.py +++ b/shared/bin/opensearch_read_only.py @@ -77,7 +77,7 @@ def main(): dest='opensearchCurlRcFile', metavar='', type=str, - default=os.getenv('OPENSEARCH_CREDS_CONFIG_FILE', '/var/local/opensearch.primary.curlrc'), + default=os.getenv('OPENSEARCH_CREDS_CONFIG_FILE', '/var/local/curlrc/.opensearch.primary.curlrc'), help='cURL.rc formatted file containing OpenSearch connection parameters', ) parser.add_argument( diff --git a/shared/bin/opensearch_status.sh b/shared/bin/opensearch_status.sh index f14988ede..04b048151 100755 --- a/shared/bin/opensearch_status.sh +++ b/shared/bin/opensearch_status.sh @@ -35,7 +35,7 @@ shift "$(($OPTIND -1))" OPENSEARCH_URL=${OPENSEARCH_URL:-"http://opensearch:9200"} OPENSEARCH_LOCAL=${OPENSEARCH_LOCAL:-"true"} OPENSEARCH_SSL_CERTIFICATE_VERIFICATION=${OPENSEARCH_SSL_CERTIFICATE_VERIFICATION:-"false"} -OPENSEARCH_CREDS_CONFIG_FILE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/opensearch.primary.curlrc"} +OPENSEARCH_CREDS_CONFIG_FILE=${OPENSEARCH_CREDS_CONFIG_FILE:-"/var/local/curlrc/.opensearch.primary.curlrc"} if [[ "$OPENSEARCH_LOCAL" == "false" ]] && [[ -r "$OPENSEARCH_CREDS_CONFIG_FILE" ]]; then CURL_CONFIG_PARAMS=( --config diff --git a/shared/bin/pcap_watcher.py b/shared/bin/pcap_watcher.py index 34846d2dd..2b785cdc2 100755 --- a/shared/bin/pcap_watcher.py +++ b/shared/bin/pcap_watcher.py @@ -336,7 +336,7 @@ def main(): dest='opensearchCurlRcFile', metavar='', type=str, - default=os.getenv('OPENSEARCH_CREDS_CONFIG_FILE', '/var/local/opensearch.primary.curlrc'), + default=os.getenv('OPENSEARCH_CREDS_CONFIG_FILE', '/var/local/curlrc/.opensearch.primary.curlrc'), help='cURL.rc formatted file containing OpenSearch connection parameters', ) parser.add_argument( From b9dc58e892ecd210748415555638f2f91450632c Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 23 Mar 2023 14:11:03 -0600 Subject: [PATCH 037/239] work in progress moving all environment variables into config instead of docker-compose yml file (install.py and documentation not done yet) --- .dockerignore | 4 +- .gitignore | 1 + _config.yml | 2 +- config/.gitignore | 1 + .../arkime.env => config/arkime.env.example | 0 .../auth-common.env.example | 0 config/auth.env.example | 3 + .../beats-common.env.example | 0 .../dashboards-helper.env.example | 6 - .../filebeat.env.example | 0 .../logstash.env.example | 4 +- .../lookup-common.env.example | 0 .../netbox-common.env.example | 0 config/netbox-postgres.env.example | 3 + config/netbox-redis-cache.env.example | 1 + config/netbox-redis.env.example | 1 + {netbox/env => config}/netbox.env.example | 0 .../nginx.env => config/nginx.env.example | 0 .../opensearch.env.example | 3 +- .../pcap-capture.env.example | 0 .../process.env => config/process.env.example | 0 kubernetes/ssl.env => config/ssl.env.example | 0 .../suricata-live.env.example | 4 +- .../suricata-offline.env.example | 4 +- .../suricata.env.example | 0 .../upload-common.env.example | 0 .../upload.env => config/upload.env.example | 0 .../zeek-live.env.example | 6 + .../zeek-offline.env.example | 5 +- .../zeek.env => config/zeek.env.example | 0 docker-compose-standalone.yml | 582 ++++-------------- docker-compose.yml | 582 ++++-------------- docs/contributing-local-modifications.md | 2 - docs/development.md | 2 +- docs/malcolm-upgrade.md | 2 +- kubernetes/04-upload.yml | 2 +- kubernetes/05-pcap-monitor.yml | 2 +- kubernetes/06-arkime.yml | 4 +- kubernetes/09-zeek.yml | 2 +- kubernetes/10-suricata.yml | 2 +- kubernetes/12-filebeat.yml | 4 +- kubernetes/13-logstash.yml | 6 +- kubernetes/15-netbox-redis.yml | 2 +- kubernetes/16-netbox-redis-cache.yml | 2 +- kubernetes/17-netbox-postgres.yml | 2 +- kubernetes/18-netbox.yml | 4 +- kubernetes/19-htadmin.yml | 2 +- kubernetes/21-zeek-live.yml | 2 +- kubernetes/22-suricata-live.yml | 2 +- kubernetes/23-freq.yml | 2 +- kubernetes/99-nginx-proxy.yml | 2 +- netbox/env/.gitignore | 4 - scripts/build.sh | 3 +- scripts/control.py | 28 +- .../demo/amazon_linux_2_malcolm_demo_setup.sh | 3 +- scripts/malcolm_appliance_packager.sh | 3 +- scripts/malcolm_common.py | 10 +- 57 files changed, 310 insertions(+), 1001 deletions(-) create mode 100644 config/.gitignore rename kubernetes/arkime.env => config/arkime.env.example (100%) rename kubernetes/auth.env => config/auth-common.env.example (100%) create mode 100644 config/auth.env.example rename kubernetes/common-beats.env => config/beats-common.env.example (100%) rename kubernetes/dashboards-helper.env => config/dashboards-helper.env.example (75%) rename kubernetes/filebeat.env => config/filebeat.env.example (100%) rename kubernetes/logstash.env => config/logstash.env.example (76%) rename kubernetes/common-lookup.env => config/lookup-common.env.example (100%) rename kubernetes/netbox.env => config/netbox-common.env.example (100%) create mode 100644 config/netbox-postgres.env.example create mode 100644 config/netbox-redis-cache.env.example create mode 100644 config/netbox-redis.env.example rename {netbox/env => config}/netbox.env.example (100%) rename kubernetes/nginx.env => config/nginx.env.example (100%) rename kubernetes/opensearch.env => config/opensearch.env.example (99%) rename kubernetes/pcap-capture.env => config/pcap-capture.env.example (100%) rename kubernetes/process.env => config/process.env.example (100%) rename kubernetes/ssl.env => config/ssl.env.example (100%) rename kubernetes/suricata-live.env => config/suricata-live.env.example (84%) rename kubernetes/suricata-offline.env => config/suricata-offline.env.example (89%) rename kubernetes/suricata.env => config/suricata.env.example (100%) rename kubernetes/common-upload.env => config/upload-common.env.example (100%) rename kubernetes/upload.env => config/upload.env.example (100%) rename kubernetes/zeek-live.env => config/zeek-live.env.example (50%) rename kubernetes/zeek-offline.env => config/zeek-offline.env.example (88%) rename kubernetes/zeek.env => config/zeek.env.example (100%) delete mode 100644 netbox/env/.gitignore diff --git a/.dockerignore b/.dockerignore index dfcb9cbf2..1daab5b10 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,11 +2,11 @@ **/*.crt **/*.iso **/*.key +**/*.env **/*.pem **/*.keystore **/.git* **/__pycache__ -**/auth.env **/.ldap_config_defaults **/htpasswd **/malcolm_*images.tar.gz @@ -35,4 +35,4 @@ zeek-logs suricata-logs netbox/netbox/media netbox/netbox/postgres -netbox/netbox/redis \ No newline at end of file +netbox/netbox/redis diff --git a/.gitignore b/.gitignore index 4ce70a661..8914aca58 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # auth-related /.ldap_config_defaults /auth.env +/config/*.env /.opensearch*.curlrc /logstash/certs /filebeat/certs diff --git a/_config.yml b/_config.yml index 3bb94c98f..0e14813af 100644 --- a/_config.yml +++ b/_config.yml @@ -55,7 +55,7 @@ exclude: - arkime - arkime-logs - arkime-raw - - auth.env + - config - dashboards - docker-compose-standalone.yml - docker-compose.yml diff --git a/config/.gitignore b/config/.gitignore new file mode 100644 index 000000000..03bd4129b --- /dev/null +++ b/config/.gitignore @@ -0,0 +1 @@ +*.env diff --git a/kubernetes/arkime.env b/config/arkime.env.example similarity index 100% rename from kubernetes/arkime.env rename to config/arkime.env.example diff --git a/kubernetes/auth.env b/config/auth-common.env.example similarity index 100% rename from kubernetes/auth.env rename to config/auth-common.env.example diff --git a/config/auth.env.example b/config/auth.env.example new file mode 100644 index 000000000..60a2bf032 --- /dev/null +++ b/config/auth.env.example @@ -0,0 +1,3 @@ +# Malcolm Administrator username and encrypted password for nginx reverse proxy (and upload server's SFTP access) +MALCOLM_USERNAME=admin +MALCOLM_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX== diff --git a/kubernetes/common-beats.env b/config/beats-common.env.example similarity index 100% rename from kubernetes/common-beats.env rename to config/beats-common.env.example diff --git a/kubernetes/dashboards-helper.env b/config/dashboards-helper.env.example similarity index 75% rename from kubernetes/dashboards-helper.env rename to config/dashboards-helper.env.example index 1454d30a3..75873108e 100644 --- a/kubernetes/dashboards-helper.env +++ b/config/dashboards-helper.env.example @@ -10,9 +10,3 @@ OPENSEARCH_INDEX_SIZE_PRUNE_NAME_SORT=false # Parameters for the OpenSearch repository used for index snapshots ISM_SNAPSHOT_COMPRESSED=false ISM_SNAPSHOT_REPO=logs - -DASHBOARDS_URL=http://dashboards:5601/dashboards -ARKIME_INDEX_PATTERN=arkime_sessions3-* -ARKIME_INDEX_PATTERN_ID=arkime_sessions3-* -ARKIME_INDEX_TIME_FIELD=firstPacket -CREATE_OS_ARKIME_SESSION_INDEX=true diff --git a/kubernetes/filebeat.env b/config/filebeat.env.example similarity index 100% rename from kubernetes/filebeat.env rename to config/filebeat.env.example diff --git a/kubernetes/logstash.env b/config/logstash.env.example similarity index 76% rename from kubernetes/logstash.env rename to config/logstash.env.example index daddf0249..10a566e11 100644 --- a/kubernetes/logstash.env +++ b/config/logstash.env.example @@ -10,4 +10,6 @@ LOGSTASH_SEVERITY_SCORING=true # Whether or not Logstash will perform a reverse DNS lookup for external IP addresses LOGSTASH_REVERSE_DNS=false # Whether or not Logstash will enrich network traffic metadata via NetBox API calls -LOGSTASH_NETBOX_ENRICHMENT=false \ No newline at end of file +LOGSTASH_NETBOX_ENRICHMENT=false + +LS_JAVA_OPTS : '-server -Xms2g -Xmx2g -Xss1536k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true' \ No newline at end of file diff --git a/kubernetes/common-lookup.env b/config/lookup-common.env.example similarity index 100% rename from kubernetes/common-lookup.env rename to config/lookup-common.env.example diff --git a/kubernetes/netbox.env b/config/netbox-common.env.example similarity index 100% rename from kubernetes/netbox.env rename to config/netbox-common.env.example diff --git a/config/netbox-postgres.env.example b/config/netbox-postgres.env.example new file mode 100644 index 000000000..8e4861b2e --- /dev/null +++ b/config/netbox-postgres.env.example @@ -0,0 +1,3 @@ +POSTGRES_DB=netbox +POSTGRES_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXX +POSTGRES_USER=netbox diff --git a/config/netbox-redis-cache.env.example b/config/netbox-redis-cache.env.example new file mode 100644 index 000000000..7883e1460 --- /dev/null +++ b/config/netbox-redis-cache.env.example @@ -0,0 +1 @@ +REDIS_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXX diff --git a/config/netbox-redis.env.example b/config/netbox-redis.env.example new file mode 100644 index 000000000..7883e1460 --- /dev/null +++ b/config/netbox-redis.env.example @@ -0,0 +1 @@ +REDIS_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXX diff --git a/netbox/env/netbox.env.example b/config/netbox.env.example similarity index 100% rename from netbox/env/netbox.env.example rename to config/netbox.env.example diff --git a/kubernetes/nginx.env b/config/nginx.env.example similarity index 100% rename from kubernetes/nginx.env rename to config/nginx.env.example diff --git a/kubernetes/opensearch.env b/config/opensearch.env.example similarity index 99% rename from kubernetes/opensearch.env rename to config/opensearch.env.example index 3b8650b6b..1141ad38e 100644 --- a/kubernetes/opensearch.env +++ b/config/opensearch.env.example @@ -37,10 +37,11 @@ OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE=/var/local/curlrc/.opensearch.secondary.c # certificates). OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION=false +OPENSEARCH_JAVA_OPTS=-server -Xms4g -Xmx4g -Xss256k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true + logger.level=WARN bootstrap.memory_lock=true MAX_LOCKED_MEMORY=unlimited -OPENSEARCH_JAVA_OPTS=-server -Xms4g -Xmx4g -Xss256k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true discovery.type=single-node cluster.routing.allocation.disk.threshold_enabled=false cluster.routing.allocation.node_initial_primaries_recoveries=8 diff --git a/kubernetes/pcap-capture.env b/config/pcap-capture.env.example similarity index 100% rename from kubernetes/pcap-capture.env rename to config/pcap-capture.env.example diff --git a/kubernetes/process.env b/config/process.env.example similarity index 100% rename from kubernetes/process.env rename to config/process.env.example diff --git a/kubernetes/ssl.env b/config/ssl.env.example similarity index 100% rename from kubernetes/ssl.env rename to config/ssl.env.example diff --git a/kubernetes/suricata-live.env b/config/suricata-live.env.example similarity index 84% rename from kubernetes/suricata-live.env rename to config/suricata-live.env.example index ce27ffc52..3fd9e045c 100644 --- a/kubernetes/suricata-live.env +++ b/config/suricata-live.env.example @@ -3,4 +3,6 @@ SURICATA_LIVE_CAPTURE=false # Specifies the Suricata runmode for live capture (see # https://suricata.readthedocs.io/en/latest/performance/runmodes.html) -SURICATA_RUNMODE=workers \ No newline at end of file +SURICATA_RUNMODE=workers + +SURICATA_PCAP_PROCESSOR=false \ No newline at end of file diff --git a/kubernetes/suricata-offline.env b/config/suricata-offline.env.example similarity index 89% rename from kubernetes/suricata-offline.env rename to config/suricata-offline.env.example index 318150c8f..68f7e69b1 100644 --- a/kubernetes/suricata-offline.env +++ b/config/suricata-offline.env.example @@ -7,4 +7,6 @@ SURICATA_AUTO_ANALYZE_PCAP_THREADS=1 # by netsniff-ng/tcpdump (see PCAP_ENABLE_NETSNIFF and PCAP_ENABLE_TCPDUMP # below). If SURICATA_LIVE_CAPTURE is true, this should be false: otherwise # Suricata will see duplicate traffic. -SURICATA_ROTATED_PCAP=true \ No newline at end of file +SURICATA_ROTATED_PCAP=true + +SURICATA_PCAP_PROCESSOR=true \ No newline at end of file diff --git a/kubernetes/suricata.env b/config/suricata.env.example similarity index 100% rename from kubernetes/suricata.env rename to config/suricata.env.example diff --git a/kubernetes/common-upload.env b/config/upload-common.env.example similarity index 100% rename from kubernetes/common-upload.env rename to config/upload-common.env.example diff --git a/kubernetes/upload.env b/config/upload.env.example similarity index 100% rename from kubernetes/upload.env rename to config/upload.env.example diff --git a/kubernetes/zeek-live.env b/config/zeek-live.env.example similarity index 50% rename from kubernetes/zeek-live.env rename to config/zeek-live.env.example index 04c81dd29..ab8f03680 100644 --- a/kubernetes/zeek-live.env +++ b/config/zeek-live.env.example @@ -1,3 +1,9 @@ # Whether or not Zeek should monitor live traffic on a local # interface (PCAP_IFACE variable below specifies capture interfaces) ZEEK_LIVE_CAPTURE=false + +ZEEK_PCAP_PROCESSOR=false +ZEEK_CRON=true +ZEEK_LOG_PATH=zeek/live +ZEEK_INTEL_PATH=opt/zeek/share/zeek/site/intel +EXTRACT_FILES_PATH=zeek/extract_files \ No newline at end of file diff --git a/kubernetes/zeek-offline.env b/config/zeek-offline.env.example similarity index 88% rename from kubernetes/zeek-offline.env rename to config/zeek-offline.env.example index 10e0ed363..f57f536b0 100644 --- a/kubernetes/zeek-offline.env +++ b/config/zeek-offline.env.example @@ -7,4 +7,7 @@ ZEEK_AUTO_ANALYZE_PCAP_THREADS=1 # by netsniff-ng/tcpdump (see PCAP_ENABLE_NETSNIFF and PCAP_ENABLE_TCPDUMP # below). If ZEEK_LIVE_CAPTURE is true, this should be false: otherwise # Zeek will see duplicate traffic. -ZEEK_ROTATED_PCAP=true \ No newline at end of file +ZEEK_ROTATED_PCAP=true + +ZEEK_PCAP_PROCESSOR=true +ZEEK_CRON=false \ No newline at end of file diff --git a/kubernetes/zeek.env b/config/zeek.env.example similarity index 100% rename from kubernetes/zeek.env rename to config/zeek.env.example diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index cc744289a..e28e20417 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -2,359 +2,6 @@ version: '3.7' -################################################################################ -# Commonly tweaked configuration options -#------------------------------------------------------------------------------- -x-process-variables: &process-variables - # docker containers will run processes as unprivileged user with UID:GID - PUID : 1000 - PGID : 1000 - # for debugging container init via tini (https://github.com/krallin/tini) - TINI_VERBOSITY : 1 - -x-auth-variables: &auth-variables - # authentication method: encrypted HTTP basic authentication ('true') vs LDAP ('false') - NGINX_BASIC_AUTH : 'true' - # NGINX LDAP (NGINX_BASIC_AUTH=false) can support LDAP, LDAPS, or LDAP+StartTLS. - # For StartTLS, set NGINX_LDAP_TLS_STUNNEL=true to issue the StartTLS command - # and use stunnel to tunnel the connection. - NGINX_LDAP_TLS_STUNNEL : 'false' - # stunnel will require and verify certificates for StartTLS when one or more - # trusted CA certificate files are placed in the ./nginx/ca-trust directory. - # For additional security, hostname or IP address checking of the associated - # CA certificate(s) can be enabled by providing these values. - NGINX_LDAP_TLS_STUNNEL_CHECK_HOST : '' - NGINX_LDAP_TLS_STUNNEL_CHECK_IP : '' - NGINX_LDAP_TLS_STUNNEL_VERIFY_LEVEL : 2 - -x-nginx-variables: &nginx-variables - # Whether or not nginx should use HTTPS. This is almost CERTAINLY what you want. - # The only case you may want to set this to false is if you're using another - # reverse proxy in front of Malcolm. Even if set to 'false', NGINX will still - # listen on port 443 (it just won't be encrypted). If you change this, you'll - # probably want to change "0.0.0.0:443:443" to something like - # "127.0.0.1:80:443" in the ports section for the nginx-proxy service. - NGINX_SSL : 'true' - # Whether or not to write nginx's access.log and error.log to OpenSearch - NGINX_LOG_ACCESS_AND_ERRORS : 'false' - -x-ssl-variables: &ssl-variables - # When possible, docker containers will automatically add trusted CA certificate files - # found in the ./nginx/ca-trust directory (which is bind mounted to /ca-trust). - PUSER_CA_TRUST : '/var/local/ca-trust' - -x-opensearch-variables: &opensearch-variables - # Used in various services to define the connection to the OpenSearch document store. - # Whether or not Malcolm will start and use its own local OpenSearch instance as its - # primary data store. Set to 'false' if you're connecting to another OpenSearch - # cluster, in which case the other environment variables in this section must also - # be set with the connection parameters. - OPENSEARCH_LOCAL : 'true' - # URL for connecting to OpenSearch instance. When using Malcolm's internal instance - # of OpenSearch (i.e., OPENSEARCH_LOCAL is 'true') this should be - # 'http://opensearch:9200', otherwise specify the primary remote instance URL - # in the format 'protocol://host:port'. - OPENSEARCH_URL : 'http://opensearch:9200' - # Used when OPENSEARCH_LOCAL is 'false', the cURL-formatted config file contains login - # credentials for the primary OpenSearch instance. It can be generated for you by the - # ./scripts/auth_setup script. The notable parameters expected from this file would be - # user (with a "user:password" value) and "insecure" (if the certificate verification - # setting below is 'false'). See cURL config file format at - # https://everything.curl.dev/cmdline/configfile. This file is bind mounted locally - # from .opensearch.primary.curlrc as /var/local/curlrc/.opensearch.primary.curlrc - OPENSEARCH_CREDS_CONFIG_FILE : '/var/local/curlrc/.opensearch.primary.curlrc' - # Whether or not connections to the primary remote OpenSearch instance require full - # TLS certificate validation for the connection (this may fail if using self-signed - # certificates). - OPENSEARCH_SSL_CERTIFICATE_VERIFICATION : 'false' - # Whether or not Malcolm's Logstash instance will forward logs to a secondary remote - # OpenSearch instance in addition to the (local or remote) primary instance. - OPENSEARCH_SECONDARY : 'false' - # URL for connecting to the secondary remote OpenSearch instance, specified - # in the format 'protocol://host:port'. - OPENSEARCH_SECONDARY_URL : '' - # Used when OPENSEARCH_SECONDARY is 'true', the cURL-formatted config file contains login - # credentials for the secondary OpenSearch instance. The comments describing - # OPENSEARCH_CREDS_CONFIG_FILE above also apply here. This file is bind mounted locally - # from .opensearch.secondary.curlrc as /var/local/curlrc/.opensearch.secondary.curlrc - OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE : '/var/local/curlrc/.opensearch.secondary.curlrc' - # Whether or not connections to the secondary remote OpenSearch instance require full - # TLS certificate validation for the connection (this may fail if using self-signed - # certificates). - OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION : 'false' - -x-arkime-variables: &arkime-variables - # Whether or not Arkime is allowed to delete uploaded/captured PCAP (see - # https://arkime.com/faq#pcap-deletion) - MANAGE_PCAP_FILES : 'false' - # The number of Arkime capture processes allowed to run concurrently - ARKIME_ANALYZE_PCAP_THREADS : 1 - # MaxMind GeoIP database update API key (see - # https://support.maxmind.com/hc/en-us/articles/4407116112539-Using-License-Keys) - MAXMIND_GEOIP_DB_LICENSE_KEY : '0' - -x-zeek-live-variables: &zeek-live-variables - # Whether or not Zeek should monitor live traffic on a local - # interface (PCAP_IFACE variable below specifies capture interfaces) - ZEEK_LIVE_CAPTURE : 'false' - -x-zeek-offline-variables: &zeek-offline-variables - # Whether or not Zeek should analyze uploaded PCAP files - ZEEK_AUTO_ANALYZE_PCAP_FILES : 'true' - # The number of Zeek processes for analyzing uploaded PCAP files allowed - # to run concurrently - ZEEK_AUTO_ANALYZE_PCAP_THREADS : 1 - # Whether or not Zeek should analyze captured PCAP files captured - # by netsniff-ng/tcpdump (see PCAP_ENABLE_NETSNIFF and PCAP_ENABLE_TCPDUMP - # below). If ZEEK_LIVE_CAPTURE is true, this should be false: otherwise - # Zeek will see duplicate traffic. - ZEEK_ROTATED_PCAP : 'true' - -x-zeek-variables: &zeek-variables - # Specifies the value for Zeek's Intel::item_expiration timeout (-1min to disable) - ZEEK_INTEL_ITEM_EXPIRATION : '-1min' - # When querying a TAXII or MISP feed, only process threat indicators that have - # been created or modified since the time represented by this value; - # it may be either a fixed date/time (01/01/2021) or relative interval (30 days ago) - ZEEK_INTEL_FEED_SINCE : '' - # Specifies a cron expression indicating the refresh interval for generating the - # Zeek Intelligence Framework files ('' disables automatic refresh) - ZEEK_INTEL_REFRESH_CRON_EXPRESSION : '' - # Determines the file extraction behavior for file transfers detected by Zeek - ZEEK_EXTRACTOR_MODE : 'none' - # Whether or not files extant in ./zeek-logs/extract_files/ will be ignored on startup - EXTRACTED_FILE_IGNORE_EXISTING : 'false' - # Determines the behavior for preservation of Zeek-extracted files - EXTRACTED_FILE_PRESERVATION : 'quarantined' - # The minimum size (in bytes) for files to be extracted by Zeek - EXTRACTED_FILE_MIN_BYTES : 64 - # The maximum size (in bytes) for files to be extracted by Zeek - EXTRACTED_FILE_MAX_BYTES : 134217728 - # A VirusTotal Public API v.20 used to submit hashes of Zeek-extracted files - VTOT_API2_KEY : '0' - # Rate limiting for VirusTotal, ClamAV, YARA and capa with Zeek-extracted files - VTOT_REQUESTS_PER_MINUTE : 4 - CLAMD_MAX_REQUESTS : 8 - YARA_MAX_REQUESTS : 8 - CAPA_MAX_REQUESTS : 4 - # Whether or not YARA will scan Zeek-extracted files - EXTRACTED_FILE_ENABLE_YARA : 'false' - # Whether or not the default YARA ruleset will be ignored and only custom rules used - EXTRACTED_FILE_YARA_CUSTOM_ONLY : 'false' - # Whether or not capa will scan Zeek-extracted executables - EXTRACTED_FILE_ENABLE_CAPA : 'false' - # Whether or not capa will be extra verbose - EXTRACTED_FILE_CAPA_VERBOSE : 'false' - # Whether or not ClamAV will scan Zeek-extracted executables - EXTRACTED_FILE_ENABLE_CLAMAV : 'false' - # Whether or not to regularly update rule definitions for file scanning engines - EXTRACTED_FILE_UPDATE_RULES : 'false' - # Whether or not to enable debug output for Zeek-extracted file scanning - EXTRACTED_FILE_PIPELINE_DEBUG : 'false' - # Whether or not to enable very verbose debug output for Zeek-extracted file scanning - EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA : 'false' - # Whether or not to serve the directory containing Zeek-extracted over HTTP at ./extracted-files/ - EXTRACTED_FILE_HTTP_SERVER_ENABLE : 'false' - # Whether or not Zeek-extracted files served over HTTP will be AES-256-CBC-encrypted - EXTRACTED_FILE_HTTP_SERVER_ENCRYPT : 'true' - # Specifies the AES-256-CBC decryption password for encrypted Zeek-extracted files served over HTTP - EXTRACTED_FILE_HTTP_SERVER_KEY : 'quarantined' - # Environment variables for tweaking Zeek at runtime (see local.zeek) - # Set to any non-blank value to disable the corresponding feature - ZEEK_DISABLE_HASH_ALL_FILES : '' - ZEEK_DISABLE_LOG_PASSWORDS : '' - ZEEK_DISABLE_SSL_VALIDATE_CERTS : '' - ZEEK_DISABLE_TRACK_ALL_ASSETS : '' - ZEEK_DISABLE_BEST_GUESS_ICS : 'true' - ZEEK_DISABLE_SPICY_DHCP : 'true' - ZEEK_DISABLE_SPICY_DNS : 'true' - ZEEK_DISABLE_SPICY_HTTP : 'true' - ZEEK_DISABLE_SPICY_IPSEC : '' - ZEEK_DISABLE_SPICY_LDAP : '' - ZEEK_DISABLE_SPICY_OPENVPN : '' - ZEEK_DISABLE_SPICY_STUN : '' - ZEEK_DISABLE_SPICY_TAILSCALE : '' - ZEEK_DISABLE_SPICY_TFTP : '' - ZEEK_DISABLE_SPICY_WIREGUARD : '' - -x-suricata-live-variables: &suricata-live-variables - # Whether or not Suricata should monitor live traffic on a local - # interface (PCAP_IFACE variable below specifies capture interfaces) - SURICATA_LIVE_CAPTURE : 'false' - # Specifies the Suricata runmode for live capture (see - # https://suricata.readthedocs.io/en/latest/performance/runmodes.html) - SURICATA_RUNMODE : 'workers' - -x-suricata-offline-variables: &suricata-offline-variables - # Whether or not Suricata should analyze uploaded PCAP files - SURICATA_AUTO_ANALYZE_PCAP_FILES: 'true' - # The number of Suricata processes for analyzing uploaded PCAP files allowed - # to run concurrently - SURICATA_AUTO_ANALYZE_PCAP_THREADS : 1 - # Whether or not Suricata should analyze captured PCAP files captured - # by netsniff-ng/tcpdump (see PCAP_ENABLE_NETSNIFF and PCAP_ENABLE_TCPDUMP - # below). If SURICATA_LIVE_CAPTURE is true, this should be false: otherwise - # Suricata will see duplicate traffic. - SURICATA_ROTATED_PCAP : 'true' - -x-suricata-variables: &suricata-variables - # Whether or not the default Suricata ruleset will be ignored and only custom rules used - SURICATA_CUSTOM_RULES_ONLY : 'false' - SURICATA_UPDATE_RULES: 'false' - SURICATA_UPDATE_DEBUG: 'false' - SURICATA_UPDATE_ETOPEN: 'true' - # suricata_config_populate.py can use MANY more environment variables to tweak - # suricata.yaml (see https://github.com/OISF/suricata/blob/master/suricata.yaml.in and - # https://suricata.readthedocs.io/en/latest/configuration/suricata-yaml.html). - # DEFAULT_VARS in that script defines those variables (albeit without the - # required `SURICATA_` prefixing each) - -x-dashboards-helper-variables: &dashboards-helper-variables - # Whether or not to set OpenSearch Dashboards to dark mode - DASHBOARDS_DARKMODE : 'true' - # The maximum cumulative size of OpenSearch indices containing network traffic metadata - # (arkime_sessions3-*) before which the oldest indices will be deleted ('' to disable - # storage-based index pruning). - OPENSEARCH_INDEX_SIZE_PRUNE_LIMIT : '0' - # Whether to determine the "oldest" indices for storage-based index pruning by creation - # date/time ('true') or index name ('false') - OPENSEARCH_INDEX_SIZE_PRUNE_NAME_SORT : 'false' - # Parameters for the OpenSearch repository used for index snapshots - ISM_SNAPSHOT_COMPRESSED : 'false' - ISM_SNAPSHOT_REPO : 'logs' - -x-logstash-variables: &logstash-variables - # Parameters for tuning Logstash pipelines (see - # https://www.elastic.co/guide/en/logstash/current/logstash-settings-file.html) - pipeline.workers : 3 - pipeline.batch.size : 75 - pipeline.batch.delay : 50 - # Whether or not Logstash will map MAC addresses to vendors for MAC addresses - LOGSTASH_OUI_LOOKUP : 'true' - # Whether or not Logstash will perform severity scoring on network traffic metadata - LOGSTASH_SEVERITY_SCORING : 'true' - # Whether or not Logstash will perform a reverse DNS lookup for external IP addresses - LOGSTASH_REVERSE_DNS : 'false' - # Whether or not Logstash will enrich network traffic metadata via NetBox API calls - LOGSTASH_NETBOX_ENRICHMENT : 'false' - -x-filebeat-variables: &filebeat-variables - # filebeat parameters used for monitoring log files containing network traffic metadata - # (see https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-log.html) - FILEBEAT_SCAN_FREQUENCY : '10s' - FILEBEAT_CLEAN_INACTIVE : '180m' - FILEBEAT_IGNORE_OLDER : '120m' - FILEBEAT_CLOSE_INACTIVE : '120s' - FILEBEAT_CLOSE_INACTIVE_LIVE : '90m' - FILEBEAT_CLOSE_RENAMED : 'true' - FILEBEAT_CLOSE_REMOVED : 'true' - FILEBEAT_CLOSE_EOF : 'true' - FILEBEAT_CLEAN_REMOVED : 'true' - # Whether or not to expose a filebeat TCP input listener (see - # https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-tcp.html) - FILEBEAT_TCP_LISTEN : 'false' - # Log format expected for events sent to the filebeat TCP input listener ('json' or 'raw') - FILEBEAT_TCP_LOG_FORMAT : 'raw' - # Source field name to parse (when FILEBEAT_TCP_LOG_FORMAT is 'json') for events sent to the - # filebeat TCP input listener - FILEBEAT_TCP_PARSE_SOURCE_FIELD : 'message' - # Target field name to store decoded JSON fields (when FILEBEAT_TCP_LOG_FORMAT is 'json') for - # events sent to the filebeat TCP input listener - FILEBEAT_TCP_PARSE_TARGET_FIELD : '' - # Name of field to drop (if it exists) in events sent to the filebeat TCP input listener - FILEBEAT_TCP_PARSE_DROP_FIELD : '' - # Tag to append to events sent to the filebeat TCP input listener - FILEBEAT_TCP_TAG : '_malcolm_beats' - -x-netbox-variables: &netbox-variables - # Parameters related to NetBox (and supporting tools). Note that other more specific parameters - # can also be configured in the env_file files for netbox* services - # The name of the default "site" to be created upon NetBox initialization, and to be queried - # for enrichment (see LOGSTASH_NETBOX_ENRICHMENT) - NETBOX_DEFAULT_SITE : 'Malcolm' - # Whether to disable Malcolm's NetBox instance ('true') or not ('false') - NETBOX_DISABLED : &netboxdisabled 'true' - NETBOX_POSTGRES_DISABLED : *netboxdisabled - NETBOX_REDIS_DISABLED : *netboxdisabled - NETBOX_REDIS_CACHE_DISABLED : *netboxdisabled - # Whether or not to periodically query network traffic metadata and use it to populate NetBox - NETBOX_CRON : 'false' - # If using the NetBox interface to create API tokens, set this - # (see https://docs.djangoproject.com/en/4.1/ref/settings/#csrf-trusted-origins) - # CSRF_TRUSTED_ORIGINS : 'https://malcolm.example.org' - -x-common-upload-variables: &common-upload-variables - # Whether or not to automatically apply tags based (on the PCAP filename) to network traffic metadata - # parsed from uploaded PCAP files - AUTO_TAG : 'true' - # The node name (e.g., the hostname of this machine running Malcolm) to associate with - # network traffic metadata - PCAP_NODE_NAME : 'malcolm' - # Whether or not to enable debug output for processing uploaded/captured PCAP files - PCAP_PIPELINE_DEBUG : 'false' - # Whether or not to enable very verbose debug output for processing uploaded/captured PCAP files - PCAP_PIPELINE_DEBUG_EXTRA : 'false' - # Whether or not PCAP files extant in ./pcap/ will be ignored on startup - PCAP_PIPELINE_IGNORE_PREEXISTING : 'false' - # 'pcap-monitor' to match the name of the container providing the uploaded/captured PCAP file - # monitoring service - PCAP_MONITOR_HOST : 'pcap-monitor' - # The age (in minutes) at which already-processed log files containing network traffic metadata should - # be pruned from the filesystem - LOG_CLEANUP_MINUTES : 360 - # The age (in minutes) at which the compressed archives containing already-processed log files should - # be pruned from the filesystem - ZIP_CLEANUP_MINUTES : 720 - -x-common-lookup-variables: &common-lookup-variables - # Whether or not domain names (from DNS queries and SSL server names) will be assigned entropy scores - # as calculated by freq - FREQ_LOOKUP : 'true' - # When severity scoring is enabled, this variable indicates the entropy threshold for - # assigning severity to events with entropy scores calculated by freq; - # a lower value will only assign severity scores to fewer domain names with higher entropy - FREQ_SEVERITY_THRESHOLD : '2.0' - # When severity scoring is enabled, this variable indicates the size threshold (in megabytes) - # for assigning severity to large connections or file transfers - TOTAL_MEGABYTES_SEVERITY_THRESHOLD : 1000 - # When severity scoring is enabled, this variable indicates the duration threshold (in seconds) - # for assigning severity to long connections - CONNECTION_SECONDS_SEVERITY_THRESHOLD : 3600 - # When severity scoring is enabled, this variable defines a comma-separated list of - # sensitive countries (using ISO 3166-1 alpha-2 codes) - SENSITIVE_COUNTRY_CODES : 'AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ' - -x-common-beats-variables: &common-beats-variables - # Whether or not Logstash will use require encrypted communications for any external - # Beats-based forwarders from which it will accept logs - BEATS_SSL : 'true' - -x-pcap-capture-variables: &pcap-capture-variables - # Whether or not netsniff-ng should create PCAP files from live traffic on a local - # interface for analysis by Arkime capture (should be 'false' if PCAP_ENABLE_TCPDUMP - # is 'true') - PCAP_ENABLE_NETSNIFF : 'false' - # Whether or not tcpdump should create PCAP files from live traffic on a local - # interface for analysis by Arkime capture (should be 'false' if PCAP_ENABLE_NETSNIFF - # is 'true') - PCAP_ENABLE_TCPDUMP : 'false' - # Specifies local network interface(s) for local packet capture if PCAP_ENABLE_NETSNIFF, - # PCAP_ENABLE_TCPDUMP, ZEEK_LIVE_CAPTURE or SURICATA_LIVE_CAPTURE are 'true' - PCAP_IFACE : 'lo' - # Whether or not ethtool will disable NIC hardware offloading features and adjust - # ring buffer sizes for capture interface(s) (should be 'true' if the interface(s) are - # being used for capture only, 'false' if they are being used for management/communication) - PCAP_IFACE_TWEAK : 'false' - # Specifies how large a locally-captured PCAP file can become (in megabytes) before - # it is closed for processing and a new PCAP file created - PCAP_ROTATE_MEGABYTES : 4096 - # Specifies a time interval (in minutes) after which a locally-captured PCAP file - # will be closed for processing and a new PCAP file created - PCAP_ROTATE_MINUTES : 10 - # Specifies a tcpdump-style filter expression for local packet capture ('' to capture all traffic) - PCAP_FILTER : '' -################################################################################ - services: opensearch: image: ghcr.io/idaholab/malcolm/opensearch:kubernetes @@ -364,11 +11,11 @@ services: hostname: opensearch networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables - OPENSEARCH_JAVA_OPTS : '-server -Xms4g -Xmx4g -Xss256k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true' VIRTUAL_HOST : 'os.malcolm.local' ulimits: memlock: @@ -397,11 +44,12 @@ services: hostname: dashboards-helper networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/dashboards-helper.env environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables - << : *dashboards-helper-variables VIRTUAL_HOST : 'dashboards-helper.malcolm.local' depends_on: - opensearch @@ -423,10 +71,11 @@ services: hostname: dashboards networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables VIRTUAL_HOST : 'dashboards.malcolm.local' depends_on: - opensearch @@ -454,16 +103,14 @@ services: hard: -1 cap_add: - IPC_LOCK - env_file: ./netbox/env/netbox.env - environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables - << : *netbox-variables - << : *logstash-variables - << : *common-beats-variables - << : *common-lookup-variables - LS_JAVA_OPTS : '-server -Xms2g -Xmx2g -Xss1536k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true' + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/netbox-common.env + - ./config/beats-common.env + - ./config/lookup-common.env + - ./config/logstash.env depends_on: - opensearch ports: @@ -490,14 +137,14 @@ services: hostname: filebeat networks: - default - environment: - << : *process-variables - << : *ssl-variables - << : *nginx-variables - << : *opensearch-variables - << : *filebeat-variables - << : *common-upload-variables - << : *common-beats-variables + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/upload-common.env + - ./config/nginx.env + - ./config/beats-common.env + - ./config/filebeat.env depends_on: - logstash ports: @@ -526,13 +173,13 @@ services: networks: - default env_file: - - ./auth.env + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/upload-common.env + - ./config/auth.env + - ./config/arkime.env environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables - << : *common-upload-variables - << : *arkime-variables VIRTUAL_HOST : 'arkime.malcolm.local' ulimits: memlock: @@ -569,14 +216,12 @@ services: - NET_ADMIN - NET_RAW - SYS_ADMIN - environment: - << : *process-variables - << : *ssl-variables - << : *common-upload-variables - << : *zeek-variables - << : *zeek-offline-variables - ZEEK_PCAP_PROCESSOR : 'true' - ZEEK_CRON : 'false' + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/upload-common.env + - ./config/zeek.env + - ./config/zeek-offline.env depends_on: - opensearch volumes: @@ -606,18 +251,13 @@ services: - NET_ADMIN - NET_RAW - SYS_ADMIN - environment: - << : *process-variables - << : *ssl-variables - << : *common-upload-variables - << : *zeek-variables - << : *zeek-live-variables - << : *pcap-capture-variables - ZEEK_PCAP_PROCESSOR : 'false' - ZEEK_CRON : 'true' - ZEEK_LOG_PATH : '/zeek/live' - ZEEK_INTEL_PATH : '/opt/zeek/share/zeek/site/intel' - EXTRACT_FILES_PATH : '/zeek/extract_files' + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/upload-common.env + - ./config/pcap-capture.env + - ./config/zeek.env + - ./config/zeek-live.env volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./zeek-logs/live:/zeek/live @@ -640,13 +280,12 @@ services: - NET_ADMIN - NET_RAW - SYS_ADMIN - environment: - << : *process-variables - << : *ssl-variables - << : *common-upload-variables - << : *suricata-variables - << : *suricata-offline-variables - SURICATA_PCAP_PROCESSOR : 'true' + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/upload-common.env + - ./config/suricata.env + - ./config/suricata-offline.env depends_on: - logstash volumes: @@ -675,14 +314,13 @@ services: - NET_ADMIN - NET_RAW - SYS_ADMIN - environment: - << : *process-variables - << : *ssl-variables - << : *common-upload-variables - << : *suricata-variables - << : *suricata-live-variables - << : *pcap-capture-variables - SURICATA_PCAP_PROCESSOR : 'false' + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/upload-common.env + - ./config/pcap-capture.env + - ./config/suricata.env + - ./config/suricata-live.env volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./suricata-logs:/var/log/suricata @@ -695,10 +333,11 @@ services: hostname: file-monitor networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/zeek.env environment: - << : *process-variables - << : *ssl-variables - << : *zeek-variables VIRTUAL_HOST : 'file-monitor.malcolm.local' volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro @@ -726,10 +365,10 @@ services: - NET_ADMIN - NET_RAW - SYS_ADMIN - environment: - << : *process-variables - << : *ssl-variables - << : *pcap-capture-variables + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/pcap-capture.env volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/pcap @@ -741,11 +380,11 @@ services: hostname: pcapmon networks: - default - environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables - << : *common-upload-variables + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/upload-common.env depends_on: - opensearch volumes: @@ -768,10 +407,10 @@ services: networks: - default env_file: - - ./auth.env + - ./config/process.env + - ./config/ssl.env + - ./config/auth.env environment: - << : *process-variables - << : *ssl-variables VIRTUAL_HOST : 'upload.malcolm.local' depends_on: - arkime @@ -794,10 +433,11 @@ services: hostname: htadmin networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/auth-common.env environment: - << : *process-variables - << : *ssl-variables - << : *auth-variables VIRTUAL_HOST : 'htadmin.malcolm.local' volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro @@ -818,10 +458,11 @@ services: hostname: freq networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/lookup-common.env environment: - << : *process-variables - << : *ssl-variables - << : *common-lookup-variables VIRTUAL_HOST : 'freq.malcolm.local' volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro @@ -839,11 +480,12 @@ services: hostname: netbox networks: - default - env_file: ./netbox/env/netbox.env + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/netbox-common.env + - ./config/netbox.env environment: - << : *process-variables - << : *ssl-variables - << : *netbox-variables VIRTUAL_HOST : 'netbox.malcolm.local' depends_on: - netbox-postgres @@ -870,11 +512,12 @@ services: hostname: netbox-postgres networks: - default - env_file: ./netbox/env/postgres.env + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/netbox-common.env + - ./config/netbox-postgres.env environment: - << : *process-variables - << : *ssl-variables - << : *netbox-variables VIRTUAL_HOST : 'netbox-postgres.malcolm.local' volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro @@ -893,11 +536,12 @@ services: hostname: netbox-redis networks: - default - env_file: ./netbox/env/redis.env + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/netbox-common.env + - ./config/netbox-redis.env environment: - << : *process-variables - << : *ssl-variables - << : *netbox-variables VIRTUAL_HOST : 'netbox-redis.malcolm.local' command: - sh @@ -920,11 +564,12 @@ services: hostname: netbox-redis-cache networks: - default - env_file: ./netbox/env/redis-cache.env + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/netbox-common.env + - ./config/netbox-redis-cache.env environment: - << : *process-variables - << : *ssl-variables - << : *netbox-variables VIRTUAL_HOST : 'netbox-redis-cache.malcolm.local' command: - sh @@ -947,10 +592,11 @@ services: hostname: api networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables VIRTUAL_HOST : 'api.malcolm.local' volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro @@ -969,11 +615,11 @@ services: hostname: nginx-proxy networks: - default - environment: - << : *process-variables - << : *ssl-variables - << : *auth-variables - << : *nginx-variables + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/auth-common.env + - ./config/nginx.env depends_on: - api - arkime diff --git a/docker-compose.yml b/docker-compose.yml index 22d77bd38..1fefac40f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,359 +2,6 @@ version: '3.7' -################################################################################ -# Commonly tweaked configuration options -#------------------------------------------------------------------------------- -x-process-variables: &process-variables - # docker containers will run processes as unprivileged user with UID:GID - PUID : 1000 - PGID : 1000 - # for debugging container init via tini (https://github.com/krallin/tini) - TINI_VERBOSITY : 1 - -x-auth-variables: &auth-variables - # authentication method: encrypted HTTP basic authentication ('true') vs LDAP ('false') - NGINX_BASIC_AUTH : 'true' - # NGINX LDAP (NGINX_BASIC_AUTH=false) can support LDAP, LDAPS, or LDAP+StartTLS. - # For StartTLS, set NGINX_LDAP_TLS_STUNNEL=true to issue the StartTLS command - # and use stunnel to tunnel the connection. - NGINX_LDAP_TLS_STUNNEL : 'false' - # stunnel will require and verify certificates for StartTLS when one or more - # trusted CA certificate files are placed in the ./nginx/ca-trust directory. - # For additional security, hostname or IP address checking of the associated - # CA certificate(s) can be enabled by providing these values. - NGINX_LDAP_TLS_STUNNEL_CHECK_HOST : '' - NGINX_LDAP_TLS_STUNNEL_CHECK_IP : '' - NGINX_LDAP_TLS_STUNNEL_VERIFY_LEVEL : 2 - -x-nginx-variables: &nginx-variables - # Whether or not nginx should use HTTPS. This is almost CERTAINLY what you want. - # The only case you may want to set this to false is if you're using another - # reverse proxy in front of Malcolm. Even if set to 'false', NGINX will still - # listen on port 443 (it just won't be encrypted). If you change this, you'll - # probably want to change "0.0.0.0:443:443" to something like - # "127.0.0.1:80:443" in the ports section for the nginx-proxy service. - NGINX_SSL : 'true' - # Whether or not to write nginx's access.log and error.log to OpenSearch - NGINX_LOG_ACCESS_AND_ERRORS : 'false' - -x-ssl-variables: &ssl-variables - # When possible, docker containers will automatically add trusted CA certificate files - # found in the ./nginx/ca-trust directory (which is bind mounted to /ca-trust). - PUSER_CA_TRUST : '/var/local/ca-trust' - -x-opensearch-variables: &opensearch-variables - # Used in various services to define the connection to the OpenSearch document store. - # Whether or not Malcolm will start and use its own local OpenSearch instance as its - # primary data store. Set to 'false' if you're connecting to another OpenSearch - # cluster, in which case the other environment variables in this section must also - # be set with the connection parameters. - OPENSEARCH_LOCAL : 'true' - # URL for connecting to OpenSearch instance. When using Malcolm's internal instance - # of OpenSearch (i.e., OPENSEARCH_LOCAL is 'true') this should be - # 'http://opensearch:9200', otherwise specify the primary remote instance URL - # in the format 'protocol://host:port'. - OPENSEARCH_URL : 'http://opensearch:9200' - # Used when OPENSEARCH_LOCAL is 'false', the cURL-formatted config file contains login - # credentials for the primary OpenSearch instance. It can be generated for you by the - # ./scripts/auth_setup script. The notable parameters expected from this file would be - # user (with a "user:password" value) and "insecure" (if the certificate verification - # setting below is 'false'). See cURL config file format at - # https://everything.curl.dev/cmdline/configfile. This file is bind mounted locally - # from .opensearch.primary.curlrc as /var/local/curlrc/.opensearch.primary.curlrc - OPENSEARCH_CREDS_CONFIG_FILE : '/var/local/curlrc/.opensearch.primary.curlrc' - # Whether or not connections to the primary remote OpenSearch instance require full - # TLS certificate validation for the connection (this may fail if using self-signed - # certificates). - OPENSEARCH_SSL_CERTIFICATE_VERIFICATION : 'false' - # Whether or not Malcolm's Logstash instance will forward logs to a secondary remote - # OpenSearch instance in addition to the (local or remote) primary instance. - OPENSEARCH_SECONDARY : 'false' - # URL for connecting to the secondary remote OpenSearch instance, specified - # in the format 'protocol://host:port'. - OPENSEARCH_SECONDARY_URL : '' - # Used when OPENSEARCH_SECONDARY is 'true', the cURL-formatted config file contains login - # credentials for the secondary OpenSearch instance. The comments describing - # OPENSEARCH_CREDS_CONFIG_FILE above also apply here. This file is bind mounted locally - # from .opensearch.secondary.curlrc as /var/local/curlrc/.opensearch.secondary.curlrc - OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE : '/var/local/curlrc/.opensearch.secondary.curlrc' - # Whether or not connections to the secondary remote OpenSearch instance require full - # TLS certificate validation for the connection (this may fail if using self-signed - # certificates). - OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION : 'false' - -x-arkime-variables: &arkime-variables - # Whether or not Arkime is allowed to delete uploaded/captured PCAP (see - # https://arkime.com/faq#pcap-deletion) - MANAGE_PCAP_FILES : 'false' - # The number of Arkime capture processes allowed to run concurrently - ARKIME_ANALYZE_PCAP_THREADS : 1 - # MaxMind GeoIP database update API key (see - # https://support.maxmind.com/hc/en-us/articles/4407116112539-Using-License-Keys) - MAXMIND_GEOIP_DB_LICENSE_KEY : '0' - -x-zeek-live-variables: &zeek-live-variables - # Whether or not Zeek should monitor live traffic on a local - # interface (PCAP_IFACE variable below specifies capture interfaces) - ZEEK_LIVE_CAPTURE : 'false' - -x-zeek-offline-variables: &zeek-offline-variables - # Whether or not Zeek should analyze uploaded PCAP files - ZEEK_AUTO_ANALYZE_PCAP_FILES : 'true' - # The number of Zeek processes for analyzing uploaded PCAP files allowed - # to run concurrently - ZEEK_AUTO_ANALYZE_PCAP_THREADS : 1 - # Whether or not Zeek should analyze captured PCAP files captured - # by netsniff-ng/tcpdump (see PCAP_ENABLE_NETSNIFF and PCAP_ENABLE_TCPDUMP - # below). If ZEEK_LIVE_CAPTURE is true, this should be false: otherwise - # Zeek will see duplicate traffic. - ZEEK_ROTATED_PCAP : 'true' - -x-zeek-variables: &zeek-variables - # Specifies the value for Zeek's Intel::item_expiration timeout (-1min to disable) - ZEEK_INTEL_ITEM_EXPIRATION : '-1min' - # When querying a TAXII or MISP feed, only process threat indicators that have - # been created or modified since the time represented by this value; - # it may be either a fixed date/time (01/01/2021) or relative interval (30 days ago) - ZEEK_INTEL_FEED_SINCE : '' - # Specifies a cron expression indicating the refresh interval for generating the - # Zeek Intelligence Framework files ('' disables automatic refresh) - ZEEK_INTEL_REFRESH_CRON_EXPRESSION : '' - # Determines the file extraction behavior for file transfers detected by Zeek - ZEEK_EXTRACTOR_MODE : 'none' - # Whether or not files extant in ./zeek-logs/extract_files/ will be ignored on startup - EXTRACTED_FILE_IGNORE_EXISTING : 'false' - # Determines the behavior for preservation of Zeek-extracted files - EXTRACTED_FILE_PRESERVATION : 'quarantined' - # The minimum size (in bytes) for files to be extracted by Zeek - EXTRACTED_FILE_MIN_BYTES : 64 - # The maximum size (in bytes) for files to be extracted by Zeek - EXTRACTED_FILE_MAX_BYTES : 134217728 - # A VirusTotal Public API v.20 used to submit hashes of Zeek-extracted files - VTOT_API2_KEY : '0' - # Rate limiting for VirusTotal, ClamAV, YARA and capa with Zeek-extracted files - VTOT_REQUESTS_PER_MINUTE : 4 - CLAMD_MAX_REQUESTS : 8 - YARA_MAX_REQUESTS : 8 - CAPA_MAX_REQUESTS : 4 - # Whether or not YARA will scan Zeek-extracted files - EXTRACTED_FILE_ENABLE_YARA : 'false' - # Whether or not the default YARA ruleset will be ignored and only custom rules used - EXTRACTED_FILE_YARA_CUSTOM_ONLY : 'false' - # Whether or not capa will scan Zeek-extracted executables - EXTRACTED_FILE_ENABLE_CAPA : 'false' - # Whether or not capa will be extra verbose - EXTRACTED_FILE_CAPA_VERBOSE : 'false' - # Whether or not ClamAV will scan Zeek-extracted executables - EXTRACTED_FILE_ENABLE_CLAMAV : 'false' - # Whether or not to regularly update rule definitions for file scanning engines - EXTRACTED_FILE_UPDATE_RULES : 'false' - # Whether or not to enable debug output for Zeek-extracted file scanning - EXTRACTED_FILE_PIPELINE_DEBUG : 'false' - # Whether or not to enable very verbose debug output for Zeek-extracted file scanning - EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA : 'false' - # Whether or not to serve the directory containing Zeek-extracted over HTTP at ./extracted-files/ - EXTRACTED_FILE_HTTP_SERVER_ENABLE : 'false' - # Whether or not Zeek-extracted files served over HTTP will be AES-256-CBC-encrypted - EXTRACTED_FILE_HTTP_SERVER_ENCRYPT : 'true' - # Specifies the AES-256-CBC decryption password for encrypted Zeek-extracted files served over HTTP - EXTRACTED_FILE_HTTP_SERVER_KEY : 'quarantined' - # Environment variables for tweaking Zeek at runtime (see local.zeek) - # Set to any non-blank value to disable the corresponding feature - ZEEK_DISABLE_HASH_ALL_FILES : '' - ZEEK_DISABLE_LOG_PASSWORDS : '' - ZEEK_DISABLE_SSL_VALIDATE_CERTS : '' - ZEEK_DISABLE_TRACK_ALL_ASSETS : '' - ZEEK_DISABLE_BEST_GUESS_ICS : 'true' - ZEEK_DISABLE_SPICY_DHCP : 'true' - ZEEK_DISABLE_SPICY_DNS : 'true' - ZEEK_DISABLE_SPICY_HTTP : 'true' - ZEEK_DISABLE_SPICY_IPSEC : '' - ZEEK_DISABLE_SPICY_LDAP : '' - ZEEK_DISABLE_SPICY_OPENVPN : '' - ZEEK_DISABLE_SPICY_STUN : '' - ZEEK_DISABLE_SPICY_TAILSCALE : '' - ZEEK_DISABLE_SPICY_TFTP : '' - ZEEK_DISABLE_SPICY_WIREGUARD : '' - -x-suricata-live-variables: &suricata-live-variables - # Whether or not Suricata should monitor live traffic on a local - # interface (PCAP_IFACE variable below specifies capture interfaces) - SURICATA_LIVE_CAPTURE : 'false' - # Specifies the Suricata runmode for live capture (see - # https://suricata.readthedocs.io/en/latest/performance/runmodes.html) - SURICATA_RUNMODE : 'workers' - -x-suricata-offline-variables: &suricata-offline-variables - # Whether or not Suricata should analyze uploaded PCAP files - SURICATA_AUTO_ANALYZE_PCAP_FILES: 'true' - # The number of Suricata processes for analyzing uploaded PCAP files allowed - # to run concurrently - SURICATA_AUTO_ANALYZE_PCAP_THREADS : 1 - # Whether or not Suricata should analyze captured PCAP files captured - # by netsniff-ng/tcpdump (see PCAP_ENABLE_NETSNIFF and PCAP_ENABLE_TCPDUMP - # below). If SURICATA_LIVE_CAPTURE is true, this should be false: otherwise - # Suricata will see duplicate traffic. - SURICATA_ROTATED_PCAP : 'true' - -x-suricata-variables: &suricata-variables - # Whether or not the default Suricata ruleset will be ignored and only custom rules used - SURICATA_CUSTOM_RULES_ONLY : 'false' - SURICATA_UPDATE_RULES: 'false' - SURICATA_UPDATE_DEBUG: 'false' - SURICATA_UPDATE_ETOPEN: 'true' - # suricata_config_populate.py can use MANY more environment variables to tweak - # suricata.yaml (see https://github.com/OISF/suricata/blob/master/suricata.yaml.in and - # https://suricata.readthedocs.io/en/latest/configuration/suricata-yaml.html). - # DEFAULT_VARS in that script defines those variables (albeit without the - # required `SURICATA_` prefixing each) - -x-dashboards-helper-variables: &dashboards-helper-variables - # Whether or not to set OpenSearch Dashboards to dark mode - DASHBOARDS_DARKMODE : 'true' - # The maximum cumulative size of OpenSearch indices containing network traffic metadata - # (arkime_sessions3-*) before which the oldest indices will be deleted ('' to disable - # storage-based index pruning). - OPENSEARCH_INDEX_SIZE_PRUNE_LIMIT : '0' - # Whether to determine the "oldest" indices for storage-based index pruning by creation - # date/time ('true') or index name ('false') - OPENSEARCH_INDEX_SIZE_PRUNE_NAME_SORT : 'false' - # Parameters for the OpenSearch repository used for index snapshots - ISM_SNAPSHOT_COMPRESSED : 'false' - ISM_SNAPSHOT_REPO : 'logs' - -x-logstash-variables: &logstash-variables - # Parameters for tuning Logstash pipelines (see - # https://www.elastic.co/guide/en/logstash/current/logstash-settings-file.html) - pipeline.workers : 3 - pipeline.batch.size : 75 - pipeline.batch.delay : 50 - # Whether or not Logstash will map MAC addresses to vendors for MAC addresses - LOGSTASH_OUI_LOOKUP : 'true' - # Whether or not Logstash will perform severity scoring on network traffic metadata - LOGSTASH_SEVERITY_SCORING : 'true' - # Whether or not Logstash will perform a reverse DNS lookup for external IP addresses - LOGSTASH_REVERSE_DNS : 'false' - # Whether or not Logstash will enrich network traffic metadata via NetBox API calls - LOGSTASH_NETBOX_ENRICHMENT : 'false' - -x-filebeat-variables: &filebeat-variables - # filebeat parameters used for monitoring log files containing network traffic metadata - # (see https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-log.html) - FILEBEAT_SCAN_FREQUENCY : '10s' - FILEBEAT_CLEAN_INACTIVE : '180m' - FILEBEAT_IGNORE_OLDER : '120m' - FILEBEAT_CLOSE_INACTIVE : '120s' - FILEBEAT_CLOSE_INACTIVE_LIVE : '90m' - FILEBEAT_CLOSE_RENAMED : 'true' - FILEBEAT_CLOSE_REMOVED : 'true' - FILEBEAT_CLOSE_EOF : 'true' - FILEBEAT_CLEAN_REMOVED : 'true' - # Whether or not to expose a filebeat TCP input listener (see - # https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-tcp.html) - FILEBEAT_TCP_LISTEN : 'false' - # Log format expected for events sent to the filebeat TCP input listener ('json' or 'raw') - FILEBEAT_TCP_LOG_FORMAT : 'raw' - # Source field name to parse (when FILEBEAT_TCP_LOG_FORMAT is 'json') for events sent to the - # filebeat TCP input listener - FILEBEAT_TCP_PARSE_SOURCE_FIELD : 'message' - # Target field name to store decoded JSON fields (when FILEBEAT_TCP_LOG_FORMAT is 'json') for - # events sent to the filebeat TCP input listener - FILEBEAT_TCP_PARSE_TARGET_FIELD : '' - # Name of field to drop (if it exists) in events sent to the filebeat TCP input listener - FILEBEAT_TCP_PARSE_DROP_FIELD : '' - # Tag to append to events sent to the filebeat TCP input listener - FILEBEAT_TCP_TAG : '_malcolm_beats' - -x-netbox-variables: &netbox-variables - # Parameters related to NetBox (and supporting tools). Note that other more specific parameters - # can also be configured in the env_file files for netbox* services - # The name of the default "site" to be created upon NetBox initialization, and to be queried - # for enrichment (see LOGSTASH_NETBOX_ENRICHMENT) - NETBOX_DEFAULT_SITE : 'Malcolm' - # Whether to disable Malcolm's NetBox instance ('true') or not ('false') - NETBOX_DISABLED : &netboxdisabled 'true' - NETBOX_POSTGRES_DISABLED : *netboxdisabled - NETBOX_REDIS_DISABLED : *netboxdisabled - NETBOX_REDIS_CACHE_DISABLED : *netboxdisabled - # Whether or not to periodically query network traffic metadata and use it to populate NetBox - NETBOX_CRON : 'false' - # If using the NetBox interface to create API tokens, set this - # (see https://docs.djangoproject.com/en/4.1/ref/settings/#csrf-trusted-origins) - # CSRF_TRUSTED_ORIGINS : 'https://malcolm.example.org' - -x-common-upload-variables: &common-upload-variables - # Whether or not to automatically apply tags based (on the PCAP filename) to network traffic metadata - # parsed from uploaded PCAP files - AUTO_TAG : 'true' - # The node name (e.g., the hostname of this machine running Malcolm) to associate with - # network traffic metadata - PCAP_NODE_NAME : 'malcolm' - # Whether or not to enable debug output for processing uploaded/captured PCAP files - PCAP_PIPELINE_DEBUG : 'false' - # Whether or not to enable very verbose debug output for processing uploaded/captured PCAP files - PCAP_PIPELINE_DEBUG_EXTRA : 'false' - # Whether or not PCAP files extant in ./pcap/ will be ignored on startup - PCAP_PIPELINE_IGNORE_PREEXISTING : 'false' - # 'pcap-monitor' to match the name of the container providing the uploaded/captured PCAP file - # monitoring service - PCAP_MONITOR_HOST : 'pcap-monitor' - # The age (in minutes) at which already-processed log files containing network traffic metadata should - # be pruned from the filesystem - LOG_CLEANUP_MINUTES : 360 - # The age (in minutes) at which the compressed archives containing already-processed log files should - # be pruned from the filesystem - ZIP_CLEANUP_MINUTES : 720 - -x-common-lookup-variables: &common-lookup-variables - # Whether or not domain names (from DNS queries and SSL server names) will be assigned entropy scores - # as calculated by freq - FREQ_LOOKUP : 'true' - # When severity scoring is enabled, this variable indicates the entropy threshold for - # assigning severity to events with entropy scores calculated by freq; - # a lower value will only assign severity scores to fewer domain names with higher entropy - FREQ_SEVERITY_THRESHOLD : '2.0' - # When severity scoring is enabled, this variable indicates the size threshold (in megabytes) - # for assigning severity to large connections or file transfers - TOTAL_MEGABYTES_SEVERITY_THRESHOLD : 1000 - # When severity scoring is enabled, this variable indicates the duration threshold (in seconds) - # for assigning severity to long connections - CONNECTION_SECONDS_SEVERITY_THRESHOLD : 3600 - # When severity scoring is enabled, this variable defines a comma-separated list of - # sensitive countries (using ISO 3166-1 alpha-2 codes) - SENSITIVE_COUNTRY_CODES : 'AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ' - -x-common-beats-variables: &common-beats-variables - # Whether or not Logstash will use require encrypted communications for any external - # Beats-based forwarders from which it will accept logs - BEATS_SSL : 'true' - -x-pcap-capture-variables: &pcap-capture-variables - # Whether or not netsniff-ng should create PCAP files from live traffic on a local - # interface for analysis by Arkime capture (should be 'false' if PCAP_ENABLE_TCPDUMP - # is 'true') - PCAP_ENABLE_NETSNIFF : 'false' - # Whether or not tcpdump should create PCAP files from live traffic on a local - # interface for analysis by Arkime capture (should be 'false' if PCAP_ENABLE_NETSNIFF - # is 'true') - PCAP_ENABLE_TCPDUMP : 'false' - # Specifies local network interface(s) for local packet capture if PCAP_ENABLE_NETSNIFF, - # PCAP_ENABLE_TCPDUMP, ZEEK_LIVE_CAPTURE or SURICATA_LIVE_CAPTURE are 'true' - PCAP_IFACE : 'lo' - # Whether or not ethtool will disable NIC hardware offloading features and adjust - # ring buffer sizes for capture interface(s) (should be 'true' if the interface(s) are - # being used for capture only, 'false' if they are being used for management/communication) - PCAP_IFACE_TWEAK : 'false' - # Specifies how large a locally-captured PCAP file can become (in megabytes) before - # it is closed for processing and a new PCAP file created - PCAP_ROTATE_MEGABYTES : 4096 - # Specifies a time interval (in minutes) after which a locally-captured PCAP file - # will be closed for processing and a new PCAP file created - PCAP_ROTATE_MINUTES : 10 - # Specifies a tcpdump-style filter expression for local packet capture ('' to capture all traffic) - PCAP_FILTER : '' -################################################################################ - services: opensearch: build: @@ -367,11 +14,11 @@ services: hostname: opensearch networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables - OPENSEARCH_JAVA_OPTS : '-server -Xms4g -Xmx4g -Xss256k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true' VIRTUAL_HOST : 'os.malcolm.local' ulimits: memlock: @@ -403,11 +50,12 @@ services: hostname: dashboards-helper networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/dashboards-helper.env environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables - << : *dashboards-helper-variables VIRTUAL_HOST : 'dashboards-helper.malcolm.local' depends_on: - opensearch @@ -432,10 +80,11 @@ services: hostname: dashboards networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables VIRTUAL_HOST : 'dashboards.malcolm.local' depends_on: - opensearch @@ -466,16 +115,14 @@ services: hard: -1 cap_add: - IPC_LOCK - env_file: ./netbox/env/netbox.env - environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables - << : *netbox-variables - << : *logstash-variables - << : *common-beats-variables - << : *common-lookup-variables - LS_JAVA_OPTS : '-server -Xms2g -Xmx2g -Xss1536k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true' + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/netbox-common.env + - ./config/beats-common.env + - ./config/lookup-common.env + - ./config/logstash.env depends_on: - opensearch ports: @@ -509,14 +156,14 @@ services: hostname: filebeat networks: - default - environment: - << : *process-variables - << : *ssl-variables - << : *nginx-variables - << : *opensearch-variables - << : *filebeat-variables - << : *common-upload-variables - << : *common-beats-variables + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/upload-common.env + - ./config/nginx.env + - ./config/beats-common.env + - ./config/filebeat.env depends_on: - logstash ports: @@ -548,13 +195,13 @@ services: networks: - default env_file: - - ./auth.env + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/upload-common.env + - ./config/auth.env + - ./config/arkime.env environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables - << : *common-upload-variables - << : *arkime-variables VIRTUAL_HOST : 'arkime.malcolm.local' ulimits: memlock: @@ -597,14 +244,12 @@ services: - NET_ADMIN - NET_RAW - SYS_ADMIN - environment: - << : *process-variables - << : *ssl-variables - << : *common-upload-variables - << : *zeek-variables - << : *zeek-offline-variables - ZEEK_PCAP_PROCESSOR : 'true' - ZEEK_CRON : 'false' + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/upload-common.env + - ./config/zeek.env + - ./config/zeek-offline.env depends_on: - opensearch volumes: @@ -638,18 +283,13 @@ services: - NET_ADMIN - NET_RAW - SYS_ADMIN - environment: - << : *process-variables - << : *ssl-variables - << : *common-upload-variables - << : *zeek-variables - << : *zeek-live-variables - << : *pcap-capture-variables - ZEEK_PCAP_PROCESSOR : 'false' - ZEEK_CRON : 'true' - ZEEK_LOG_PATH : '/zeek/live' - ZEEK_INTEL_PATH : '/opt/zeek/share/zeek/site/intel' - EXTRACT_FILES_PATH : '/zeek/extract_files' + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/upload-common.env + - ./config/pcap-capture.env + - ./config/zeek.env + - ./config/zeek-live.env volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./zeek-logs/live:/zeek/live @@ -676,13 +316,12 @@ services: - NET_ADMIN - NET_RAW - SYS_ADMIN - environment: - << : *process-variables - << : *ssl-variables - << : *common-upload-variables - << : *suricata-variables - << : *suricata-offline-variables - SURICATA_PCAP_PROCESSOR : 'true' + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/upload-common.env + - ./config/suricata.env + - ./config/suricata-offline.env depends_on: - logstash volumes: @@ -714,14 +353,13 @@ services: - NET_ADMIN - NET_RAW - SYS_ADMIN - environment: - << : *process-variables - << : *ssl-variables - << : *common-upload-variables - << : *suricata-variables - << : *suricata-live-variables - << : *pcap-capture-variables - SURICATA_PCAP_PROCESSOR : 'false' + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/upload-common.env + - ./config/pcap-capture.env + - ./config/suricata.env + - ./config/suricata-live.env volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./suricata-logs:/var/log/suricata @@ -737,10 +375,11 @@ services: hostname: file-monitor networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/zeek.env environment: - << : *process-variables - << : *ssl-variables - << : *zeek-variables VIRTUAL_HOST : 'file-monitor.malcolm.local' volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro @@ -771,10 +410,10 @@ services: - NET_ADMIN - NET_RAW - SYS_ADMIN - environment: - << : *process-variables - << : *ssl-variables - << : *pcap-capture-variables + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/pcap-capture.env volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/pcap @@ -789,11 +428,11 @@ services: hostname: pcapmon networks: - default - environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables - << : *common-upload-variables + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/upload-common.env depends_on: - opensearch volumes: @@ -819,10 +458,10 @@ services: networks: - default env_file: - - ./auth.env + - ./config/process.env + - ./config/ssl.env + - ./config/auth.env environment: - << : *process-variables - << : *ssl-variables VIRTUAL_HOST : 'upload.malcolm.local' depends_on: - arkime @@ -848,10 +487,11 @@ services: hostname: htadmin networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/auth-common.env environment: - << : *process-variables - << : *ssl-variables - << : *auth-variables VIRTUAL_HOST : 'htadmin.malcolm.local' volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro @@ -875,10 +515,11 @@ services: hostname: freq networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/lookup-common.env environment: - << : *process-variables - << : *ssl-variables - << : *common-lookup-variables VIRTUAL_HOST : 'freq.malcolm.local' volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro @@ -899,11 +540,12 @@ services: hostname: netbox networks: - default - env_file: ./netbox/env/netbox.env + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/netbox-common.env + - ./config/netbox.env environment: - << : *process-variables - << : *ssl-variables - << : *netbox-variables VIRTUAL_HOST : 'netbox.malcolm.local' depends_on: - netbox-postgres @@ -934,11 +576,12 @@ services: hostname: netbox-postgres networks: - default - env_file: ./netbox/env/postgres.env + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/netbox-common.env + - ./config/netbox-postgres.env environment: - << : *process-variables - << : *ssl-variables - << : *netbox-variables VIRTUAL_HOST : 'netbox-postgres.malcolm.local' volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro @@ -960,11 +603,12 @@ services: hostname: netbox-redis networks: - default - env_file: ./netbox/env/redis.env + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/netbox-common.env + - ./config/netbox-redis.env environment: - << : *process-variables - << : *ssl-variables - << : *netbox-variables VIRTUAL_HOST : 'netbox-redis.malcolm.local' command: - sh @@ -990,11 +634,12 @@ services: hostname: netbox-redis-cache networks: - default - env_file: ./netbox/env/redis-cache.env + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/netbox-common.env + - ./config/netbox-redis-cache.env environment: - << : *process-variables - << : *ssl-variables - << : *netbox-variables VIRTUAL_HOST : 'netbox-redis-cache.malcolm.local' command: - sh @@ -1020,10 +665,11 @@ services: hostname: api networks: - default + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env environment: - << : *process-variables - << : *ssl-variables - << : *opensearch-variables VIRTUAL_HOST : 'api.malcolm.local' volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro @@ -1045,11 +691,11 @@ services: hostname: nginx-proxy networks: - default - environment: - << : *process-variables - << : *ssl-variables - << : *auth-variables - << : *nginx-variables + env_file: + - ./config/process.env + - ./config/ssl.env + - ./config/auth-common.env + - ./config/nginx.env depends_on: - api - arkime diff --git a/docs/contributing-local-modifications.md b/docs/contributing-local-modifications.md index 4aa075d32..80bd32668 100644 --- a/docs/contributing-local-modifications.md +++ b/docs/contributing-local-modifications.md @@ -38,7 +38,6 @@ $ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml - ./filebeat/certs/client.crt:/certs/client.crt:ro - ./filebeat/certs/client.key:/certs/client.key:ro arkime: - - ./auth.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./pcap:/data/pcap @@ -78,7 +77,6 @@ $ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml - ./zeek-logs:/zeek - ./pcap:/pcap upload: - - ./auth.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/var/www/upload/server/php/chroot/files htadmin: diff --git a/docs/development.md b/docs/development.md index 0a491e23c..aa25c9f16 100644 --- a/docs/development.md +++ b/docs/development.md @@ -11,6 +11,7 @@ Checking out the [Malcolm source code]({{ site.github.repository_url }}/tree/{{ * `arkime-logs` - an initially empty directory to which the `arkime` container will write some debug log files * `arkime-raw` - an initially empty directory to which the `arkime` container will write captured PCAP files; as Arkime as employed by Malcolm is currently used for processing previously-captured PCAP files, this directory is currently unused * `Dockerfiles` - a directory containing build instructions for Malcolm's docker images +* `config` - a directory containing the environment variable files that define Malcolm's configuration * `docs` - a directory containing instructions and documentation * `opensearch` - an initially empty directory where the OpenSearch database instance will reside * `opensearch-backup` - an initially empty directory for storing OpenSearch [index snapshots](index-management.md#IndexManagement) @@ -38,7 +39,6 @@ Checking out the [Malcolm source code]({{ site.github.repository_url }}/tree/{{ and the following files of special note: -* `auth.env` - the script `./scripts/auth_setup` prompts the user for the administrator credentials used by the Malcolm appliance, and `auth.env` is the environment file where those values are stored * `docker-compose.yml` - the configuration file used by `docker-compose` to build, start, and stop an instance of the Malcolm appliance * `docker-compose-standalone.yml` - similar to `docker-compose.yml`, only used for the ["packaged"](#Packager) installation of Malcolm diff --git a/docs/malcolm-upgrade.md b/docs/malcolm-upgrade.md index 991c07316..75e898ce1 100644 --- a/docs/malcolm-upgrade.md +++ b/docs/malcolm-upgrade.md @@ -39,7 +39,7 @@ If you installed Malcolm from [pre-packaged installation files]({{ site.github.r * `tar xf malcolm_YYYYMMDD_HHNNSS_xxxxxxx.tar.gz` 3. backup current Malcolm scripts, configuration files and certificates * `mkdir -p ./upgrade_backup_$(date +%Y-%m-%d)` - * `cp -r filebeat/ htadmin/ logstash/ nginx/ auth.env docker-compose.yml ./scripts ./README.md ./upgrade_backup_$(date +%Y-%m-%d)/` + * `cp -r filebeat/ htadmin/ logstash/ nginx/ config/ docker-compose.yml ./scripts ./README.md ./upgrade_backup_$(date +%Y-%m-%d)/` 3. replace scripts and local documentation in your existing installation with the new ones * `rm -rf ./scripts ./README.md` * `cp -r ./malcolm_YYYYMMDD_HHNNSS_xxxxxxx/scripts ./malcolm_YYYYMMDD_HHNNSS_xxxxxxx/README.md ./` diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 59bc8c9c8..89e038e1c 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -46,7 +46,7 @@ spec: - configMapRef: name: ssl-env - configMapRef: - name: main-auth-env + name: auth-env - configMapRef: name: upload-env env: diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml index a06ef446d..88d407112 100644 --- a/kubernetes/05-pcap-monitor.yml +++ b/kubernetes/05-pcap-monitor.yml @@ -43,7 +43,7 @@ spec: - configMapRef: name: opensearch-env - configMapRef: - name: common-upload-env + name: upload-common-env env: - name: PCAPMON_DISABLED value: "true" diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index 3de5a0706..23c1db584 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -43,7 +43,9 @@ spec: - configMapRef: name: opensearch-env - configMapRef: - name: common-upload-env + name: auth-env + - configMapRef: + name: upload-common-env - configMapRef: name: arkime-env env: diff --git a/kubernetes/09-zeek.yml b/kubernetes/09-zeek.yml index 8d0cac005..bd10573f1 100644 --- a/kubernetes/09-zeek.yml +++ b/kubernetes/09-zeek.yml @@ -26,7 +26,7 @@ spec: - configMapRef: name: ssl-env - configMapRef: - name: common-upload-env + name: upload-common-env - configMapRef: name: zeek-env - configMapRef: diff --git a/kubernetes/10-suricata.yml b/kubernetes/10-suricata.yml index 8ab654234..c289ab9e0 100644 --- a/kubernetes/10-suricata.yml +++ b/kubernetes/10-suricata.yml @@ -26,7 +26,7 @@ spec: - configMapRef: name: ssl-env - configMapRef: - name: common-upload-env + name: upload-common-env - configMapRef: name: suricata-env - configMapRef: diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml index 32f0d2823..6f6f4ef06 100644 --- a/kubernetes/12-filebeat.yml +++ b/kubernetes/12-filebeat.yml @@ -47,9 +47,9 @@ spec: - configMapRef: name: filebeat-env - configMapRef: - name: common-upload-env + name: upload-common-env - configMapRef: - name: common-beats-env + name: beats-common-env env: - name: FILEBEAT_DISABLED value: "true" diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index 67d8ec1d7..1659d8067 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -48,13 +48,13 @@ spec: - configMapRef: name: opensearch-env - configMapRef: - name: netbox-env + name: netbox-common-env - configMapRef: name: logstash-env - configMapRef: - name: common-beats-env + name: beats-common-env - configMapRef: - name: common-lookup-env + name: lookup-common-env env: - name: LOGSTASH_DISABLED value: "true" diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index bc5fd2fa6..20deda4ea 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -41,7 +41,7 @@ spec: - configMapRef: name: ssl-env - configMapRef: - name: netbox-env + name: netbox-common-env - configMapRef: name: netbox-redis-env env: diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index 6f99059cb..fa165fd3f 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -41,7 +41,7 @@ spec: - configMapRef: name: ssl-env - configMapRef: - name: netbox-env + name: netbox-common-env - configMapRef: name: netbox-redis-cache-env env: diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index e2d985411..4bb1121c2 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -41,7 +41,7 @@ spec: - configMapRef: name: ssl-env - configMapRef: - name: netbox-env + name: netbox-common-env - configMapRef: name: netbox-postgres-env env: diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index a55d28ddf..090242d20 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -50,9 +50,9 @@ spec: - configMapRef: name: ssl-env - configMapRef: - name: netbox-env + name: netbox-common-env - configMapRef: - name: netbox-netbox-env + name: netbox-env env: - name: VIRTUAL_HOST value: "netbox.malcolm.local" diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml index 5561b20a7..992cbf91f 100644 --- a/kubernetes/19-htadmin.yml +++ b/kubernetes/19-htadmin.yml @@ -41,7 +41,7 @@ spec: - configMapRef: name: ssl-env - configMapRef: - name: auth-env + name: auth-common-env env: - name: HTADMIN_DISABLED value: "true" diff --git a/kubernetes/21-zeek-live.yml b/kubernetes/21-zeek-live.yml index f939b1a29..d241443df 100644 --- a/kubernetes/21-zeek-live.yml +++ b/kubernetes/21-zeek-live.yml @@ -26,7 +26,7 @@ spec: - configMapRef: name: ssl-env - configMapRef: - name: common-upload-env + name: upload-common-env - configMapRef: name: zeek-env - configMapRef: diff --git a/kubernetes/22-suricata-live.yml b/kubernetes/22-suricata-live.yml index f49a49e7e..045c8b405 100644 --- a/kubernetes/22-suricata-live.yml +++ b/kubernetes/22-suricata-live.yml @@ -26,7 +26,7 @@ spec: - configMapRef: name: ssl-env - configMapRef: - name: common-upload-env + name: upload-common-env - configMapRef: name: suricata-env - configMapRef: diff --git a/kubernetes/23-freq.yml b/kubernetes/23-freq.yml index 254285c13..4f33850a7 100644 --- a/kubernetes/23-freq.yml +++ b/kubernetes/23-freq.yml @@ -41,7 +41,7 @@ spec: - configMapRef: name: ssl-env - configMapRef: - name: common-lookup-env + name: lookup-common-env env: - name: VIRTUAL_HOST value: "freq.malcolm.local" diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index bd9505328..eeee321ff 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -41,7 +41,7 @@ spec: - configMapRef: name: ssl-env - configMapRef: - name: auth-env + name: auth-common-env - configMapRef: name: nginx-env livenessProbe: diff --git a/netbox/env/.gitignore b/netbox/env/.gitignore deleted file mode 100644 index 981ab9771..000000000 --- a/netbox/env/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -postgres.env -redis-cache.env -redis.env -netbox.env \ No newline at end of file diff --git a/scripts/build.sh b/scripts/build.sh index f8ac8d269..c31a36dd1 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -72,7 +72,8 @@ MALCOLM_VERSION="$($GREP -P "^\s+image:\s*malcolm" "$CONFIG_FILE" | awk '{print VCS_REVISION="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)" GITHUB_API_TOKEN="${GITHUB_TOKEN:-}" -[[ ! -f ./auth.env ]] && touch ./auth.env +mkdir -p ./config +[[ ! -f ./config/auth.env ]] && touch ./config/auth.env # MaxMind now requires a (free) license key to download the free versions of their GeoIP databases. if [ ${#MAXMIND_GEOIP_DB_LICENSE_KEY} -gt 1 ]; then diff --git a/scripts/control.py b/scripts/control.py index 235e26d75..b6a8d174a 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -778,13 +778,13 @@ def start(): os.path.join(MalcolmPath, os.path.join('nginx', 'nginx_ldap.conf')), os.path.join(MalcolmPath, '.opensearch.primary.curlrc'), os.path.join(MalcolmPath, '.opensearch.secondary.curlrc'), - os.path.join(MalcolmPath, os.path.join('netbox', os.path.join('env', 'netbox.env'))), - os.path.join(MalcolmPath, os.path.join('netbox', os.path.join('env', 'postgres.env'))), - os.path.join(MalcolmPath, os.path.join('netbox', os.path.join('env', 'redis-cache.env'))), - os.path.join(MalcolmPath, os.path.join('netbox', os.path.join('env', 'redis.env'))), ]: # chmod 600 authFile os.chmod(authFile, stat.S_IRUSR | stat.S_IWUSR) + with pushd(os.path.join(MalcolmPath, 'config')): + for envFile in glob.glob("*.env"): + # chmod 600 envFile + os.chmod(envFile, stat.S_IRUSR | stat.S_IWUSR) # make sure some directories exist before we start boundPathsToCreate = ( @@ -883,7 +883,7 @@ def authSetup(wipe=False): eprint("Passwords do not match") # get previous admin username to remove from htpasswd file if it's changed - authEnvFile = os.path.join(MalcolmPath, 'auth.env') + authEnvFile = os.path.join(MalcolmPath, os.path.join('config', 'auth.env')) if os.path.isfile(authEnvFile): prevAuthInfo = defaultdict(str) with open(authEnvFile, 'r') as f: @@ -1325,11 +1325,9 @@ def authSetup(wipe=False): if YesOrNo( '(Re)generate internal passwords for NetBox', - default=not os.path.isfile( - os.path.join(MalcolmPath, os.path.join('netbox', os.path.join('env', 'netbox.env'))) - ), + default=not os.path.isfile(os.path.join(MalcolmPath, os.path.join('config', 'netbox.env'))), ): - with pushd(os.path.join(MalcolmPath, os.path.join('netbox', 'env'))): + with pushd(os.path.join(MalcolmPath, 'config')): netboxPwAlphabet = string.ascii_letters + string.digits + '_' netboxKeyAlphabet = string.ascii_letters + string.digits + '%@<=>?~^_-' netboxPostGresPassword = ''.join(secrets.choice(netboxPwAlphabet) for i in range(24)) @@ -1339,19 +1337,19 @@ def authSetup(wipe=False): netboxSuToken = ''.join(secrets.choice(netboxPwAlphabet) for i in range(40)) netboxSecretKey = ''.join(secrets.choice(netboxKeyAlphabet) for i in range(50)) - with open('postgres.env', 'w') as f: + with open('netbox-postgres.env', 'w') as f: f.write('POSTGRES_DB=netbox\n') f.write(f'POSTGRES_PASSWORD={netboxPostGresPassword}\n') f.write('POSTGRES_USER=netbox\n') - os.chmod('postgres.env', stat.S_IRUSR | stat.S_IWUSR) + os.chmod('netbox-postgres.env', stat.S_IRUSR | stat.S_IWUSR) - with open('redis-cache.env', 'w') as f: + with open('netbox-redis-cache.env', 'w') as f: f.write(f'REDIS_PASSWORD={netboxRedisCachePassword}\n') - os.chmod('redis-cache.env', stat.S_IRUSR | stat.S_IWUSR) + os.chmod('netbox-redis-cache.env', stat.S_IRUSR | stat.S_IWUSR) - with open('redis.env', 'w') as f: + with open('netbox-redis.env', 'w') as f: f.write(f'REDIS_PASSWORD={netboxRedisPassword}\n') - os.chmod('redis.env', stat.S_IRUSR | stat.S_IWUSR) + os.chmod('netbox-redis.env', stat.S_IRUSR | stat.S_IWUSR) if (not os.path.isfile('netbox.env')) and (os.path.isfile('netbox.env.example')): shutil.copy2('netbox.env.example', 'netbox.env') diff --git a/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh b/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh index b6d534f75..da6beadbf 100755 --- a/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh +++ b/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh @@ -572,7 +572,8 @@ function InstallMalcolm { sed -i "s/\(^[[:space:]]*$KEY[[:space:]]*:[[:space:]]*\).*/\1$VALUE/g" "$CONFIG" done done - touch auth.env + mkdir -p ./config + touch ./config/auth.env grep image: docker-compose-standalone.yml | awk '{print $2}' | sort -u | xargs -l -r $SUDO_CMD docker pull echo "Please run $MALCOLM_PATH/scripts/auth_setup to complete configuration" >&2 popd >/dev/null 2>&1 diff --git a/scripts/malcolm_appliance_packager.sh b/scripts/malcolm_appliance_packager.sh index d16414c41..e46cb68b3 100755 --- a/scripts/malcolm_appliance_packager.sh +++ b/scripts/malcolm_appliance_packager.sh @@ -77,6 +77,7 @@ if mkdir "$DESTDIR"; then mkdir $VERBOSE -p "$DESTDIR/opensearch/nodes/" mkdir $VERBOSE -p "$DESTDIR/pcap/processed/" mkdir $VERBOSE -p "$DESTDIR/pcap/upload/" + mkdir $VERBOSE -p "$DESTDIR/config/" mkdir $VERBOSE -p "$DESTDIR/scripts/" mkdir $VERBOSE -p "$DESTDIR/suricata-logs/live" mkdir $VERBOSE -p "$DESTDIR/suricata/rules/" @@ -90,7 +91,7 @@ if mkdir "$DESTDIR"; then mkdir $VERBOSE -p "$DESTDIR/zeek/intel/MISP" mkdir $VERBOSE -p "$DESTDIR/zeek/intel/STIX" cp $VERBOSE ./docker-compose-standalone.yml "$DESTDIR/docker-compose.yml" - touch "$DESTDIR/"auth.env + touch "$DESTDIR/"config/auth.env cp $VERBOSE ./net-map.json "$DESTDIR/" cp $VERBOSE ./scripts/install.py "$DESTDIR/scripts/" cp $VERBOSE ./scripts/control.py "$DESTDIR/scripts/" diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index 2f5a18b4f..a6f468607 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -793,11 +793,11 @@ def MalcolmAuthFilesExist(): and os.path.isfile(os.path.join(MalcolmPath, os.path.join('nginx', os.path.join('certs', 'cert.pem')))) and os.path.isfile(os.path.join(MalcolmPath, os.path.join('nginx', os.path.join('certs', 'key.pem')))) and os.path.isfile(os.path.join(MalcolmPath, os.path.join('htadmin', 'config.ini'))) - and os.path.isfile(os.path.join(MalcolmPath, os.path.join('netbox', os.path.join('env', 'netbox.env')))) - and os.path.isfile(os.path.join(MalcolmPath, os.path.join('netbox', os.path.join('env', 'postgres.env')))) - and os.path.isfile(os.path.join(MalcolmPath, os.path.join('netbox', os.path.join('env', 'redis-cache.env')))) - and os.path.isfile(os.path.join(MalcolmPath, os.path.join('netbox', os.path.join('env', 'redis.env')))) - and os.path.isfile(os.path.join(MalcolmPath, 'auth.env')) + and os.path.isfile(os.path.join(MalcolmPath, os.path.join('config', 'netbox.env'))) + and os.path.isfile(os.path.join(MalcolmPath, os.path.join('config', 'netbox-postgres.env'))) + and os.path.isfile(os.path.join(MalcolmPath, os.path.join('config', 'netbox-redis-cache.env'))) + and os.path.isfile(os.path.join(MalcolmPath, os.path.join('config', 'netbox-redis.env'))) + and os.path.isfile(os.path.join(MalcolmPath, os.path.join('config', 'auth.env'))) and os.path.isfile(os.path.join(MalcolmPath, '.opensearch.primary.curlrc')) ) From ad2322ce1e84c316ae4bfeb05e4a39df8335a495 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 23 Mar 2023 14:33:30 -0600 Subject: [PATCH 038/239] fix LS_JAVA_OPTS env var --- config/logstash.env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/logstash.env.example b/config/logstash.env.example index 10a566e11..b2b0854a4 100644 --- a/config/logstash.env.example +++ b/config/logstash.env.example @@ -12,4 +12,4 @@ LOGSTASH_REVERSE_DNS=false # Whether or not Logstash will enrich network traffic metadata via NetBox API calls LOGSTASH_NETBOX_ENRICHMENT=false -LS_JAVA_OPTS : '-server -Xms2g -Xmx2g -Xss1536k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true' \ No newline at end of file +LS_JAVA_OPTS=-server -Xms2g -Xmx2g -Xss1536k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true \ No newline at end of file From 07389fb00e5699dbce2833b77eb559b816716443 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 07:03:01 -0600 Subject: [PATCH 039/239] rewording documentation --- README.md | 2 +- docs/dashboards.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4e25b7983..860cc7e2a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Malcolm is a powerful network traffic analysis tool suite designed with the foll * **Permissive license** – Malcolm is comprised of several widely used open source tools, making it an attractive alternative to security solutions requiring paid licenses. * **Expanding control systems visibility** – While Malcolm is great for general-purpose network traffic analysis, its creators see a particular need in the community for tools providing insight into protocols used in industrial control systems (ICS) environments. Ongoing Malcolm development will aim to provide additional parsers for common ICS protocols. -Although all of the open source tools which make up Malcolm are already available and in general use, Malcolm provides a framework of interconnectivity which makes it greater than the sum of its parts. And while there are many other network traffic analysis solutions out there, ranging from complete Linux distributions like Security Onion to licensed products like Splunk Enterprise Security, the creators of Malcolm feel its easy deployment and robust combination of tools fill a void in the network security space that will make network traffic analysis accessible to many in both the public and private sectors as well as individual enthusiasts. +Although all of the open source tools which make up Malcolm are already available and in general use, Malcolm provides a framework of interconnectivity which makes it greater than the sum of its parts. In short, Malcolm provides an easily deployable network analysis tool suite for full packet capture artifacts (PCAP files) and Zeek logs. While Internet access is required to build it, it is not required at runtime. diff --git a/docs/dashboards.md b/docs/dashboards.md index 7a5f5fc00..4d47745d5 100644 --- a/docs/dashboards.md +++ b/docs/dashboards.md @@ -40,7 +40,7 @@ The **Discover** view enables you to view events on a record-by-record basis (si Malcolm comes with dozens of prebuilt visualizations and dashboards for the network traffic represented by each of the Zeek log types. Click **Dashboard** to see a list of these dashboards. As is the case with all OpenSearch Dashboards visualizations, all of the charts, graphs, maps, and tables are interactive and can be clicked on to narrow or expand the scope of the data you are investigating. Similarly, click **Visualize** to explore the prebuilt visualizations used to build the dashboards. -Many of Malcolm's prebuilt visualizations for Zeek logs were originally inspired by the excellent [Kibana Dashboards](https://github.com/Security-Onion-Solutions/securityonion-elastic/tree/master/kibana/dashboards) that are part of [Security Onion](https://securityonion.net/). +Inspiration for many of Malcolm's prebuilt visualizations for Zeek logs was originally drawn from [Security Onion](https://github.com/Security-Onion-Solutions/securityonion)'s excellent Kibana dashboards. ####
Screenshots From 592182f951df94b37481340b698486f91e0b4362 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 09:27:39 -0600 Subject: [PATCH 040/239] work in progress on using environment variables for storing config vs. the docker-compose.yml files --- .dockerignore | 1 + .gitignore | 1 + config/logstash.env.example | 2 +- config/opensearch.env.example | 2 +- .../config/package-lists/python.list.chroot | 1 + scripts/install.py | 771 ++++++++++-------- scripts/malcolm_common.py | 4 + 7 files changed, 420 insertions(+), 362 deletions(-) diff --git a/.dockerignore b/.dockerignore index 1daab5b10..4e33ae8cd 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,6 +17,7 @@ .configured .trigger_workflow_build .tmp +config.* docker-compose*yml Dockerfiles Gemfile.lock diff --git a/.gitignore b/.gitignore index 8914aca58..991827d72 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ # runtime .tmp .configured +config.*/ # development .envrc diff --git a/config/logstash.env.example b/config/logstash.env.example index b2b0854a4..f8aa43202 100644 --- a/config/logstash.env.example +++ b/config/logstash.env.example @@ -11,5 +11,5 @@ LOGSTASH_SEVERITY_SCORING=true LOGSTASH_REVERSE_DNS=false # Whether or not Logstash will enrich network traffic metadata via NetBox API calls LOGSTASH_NETBOX_ENRICHMENT=false - +# Logstash memory allowance and other Java options LS_JAVA_OPTS=-server -Xms2g -Xmx2g -Xss1536k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true \ No newline at end of file diff --git a/config/opensearch.env.example b/config/opensearch.env.example index 1141ad38e..258e23233 100644 --- a/config/opensearch.env.example +++ b/config/opensearch.env.example @@ -36,7 +36,7 @@ OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE=/var/local/curlrc/.opensearch.secondary.c # TLS certificate validation for the connection (this may fail if using self-signed # certificates). OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION=false - +# OpenSearch memory allowance and other Java options OPENSEARCH_JAVA_OPTS=-server -Xms4g -Xmx4g -Xss256k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true logger.level=WARN diff --git a/malcolm-iso/config/package-lists/python.list.chroot b/malcolm-iso/config/package-lists/python.list.chroot index 2cf6d89cf..253fdc03f 100644 --- a/malcolm-iso/config/package-lists/python.list.chroot +++ b/malcolm-iso/config/package-lists/python.list.chroot @@ -2,6 +2,7 @@ python3 python3-pip python3-bs4 python3-colorama +python3-dotenv python3-netifaces python3-psutil python3-pycryptodome diff --git a/scripts/install.py b/scripts/install.py index e125f2fb3..378df1e1d 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -37,6 +37,9 @@ MAC_BREW_DOCKER_PACKAGE = 'docker-edge' MAC_BREW_DOCKER_SETTINGS = '/Users/{}/Library/Group Containers/group.com.docker/settings.json' +LOGSTASH_JAVA_OPTS_DEFAULT = '-server -Xms2g -Xmx2g -Xss1536k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true' +OPENSEARCH_JAVA_OPTS_DEFAULT = '-server -Xms4g -Xmx4g -Xss256k -XX:-HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j.formatMsgNoLookups=true' + ################################################################################################### ScriptName = os.path.basename(__file__) origPath = os.getcwd() @@ -46,6 +49,11 @@ args = None requests_imported = None yaml_imported = None +dotenv_imported = None + +################################################################################################### +TrueOrFalseQuote = lambda x: "'true'" if x else "'false'" +TrueOrFalseNoQuote = lambda x: 'true' if x else 'false' ################################################################################################### @@ -156,10 +164,6 @@ def InstallerDisplayMessage( ) -def TrueOrFalseQuote(expression): - return "'{}'".format('true' if expression else 'false') - - ################################################################################################### class Installer(object): # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -330,7 +334,20 @@ def tweak_malcolm_runtime( restart_mode_default=False, ): global args + global dotenv_imported + + envFilesDir = None + composeFiles = [] + # determine directory containing .env files + if (not args.configDir) or (not os.path.isdir(args.configDir)): + # use default config directory + envFilesDir = os.path.join(malcolm_install_path, 'config') + else: + # configuration directory explicitly specified + envFilesDir = args.configDir + + # determine docker-compose file(s) if not args.configFile: # get a list of all of the docker-compose files composeFiles = glob.glob(os.path.join(malcolm_install_path, 'docker-compose*.yml')) @@ -340,6 +357,9 @@ def tweak_malcolm_runtime( composeFiles = [os.path.realpath(args.configFile)] malcolm_install_path = os.path.dirname(composeFiles[0]) + if (not envFilesDir) or (not os.path.isdir(envFilesDir)): + raise Exception("Could not determine configuration directory containing Malcolm's .env files") + # figure out what UID/GID to run non-rood processes under docker as puid = '1000' pgid = '1000' @@ -368,7 +388,9 @@ def tweak_malcolm_runtime( # guestimate how much memory we should use based on total system memory if self.debug: - eprint(f"{malcolm_install_path} contains {composeFiles}, system memory is {self.totalMemoryGigs} GiB") + eprint( + f'{malcolm_install_path} with "{composeFiles}" and "{envFilesDir}", system memory is {self.totalMemoryGigs} GiB' + ) if self.totalMemoryGigs >= 63.0: osMemory = '30g' @@ -847,7 +869,357 @@ def tweak_malcolm_runtime( dashboardsDarkMode = InstallerYesOrNo('Enable dark mode for OpenSearch Dashboards?', default=True) - # modify specified values in-place in docker-compose files + # modify values in .env files in envFilesDir + + # first, look at the ".env.example" files and if the corresponding .env file doesn't exist, create it + envExampleFiles = glob.glob(os.path.join(envFilesDir, '*.env.example')) + for envExampleFile in envExampleFiles: + envFile = envExampleFile[: -len('.example')] + if not os.path.isfile(envFile): + shutil.copyfile(envExampleFile, envFile) + + EnvValue = namedtuple("EnvValue", ["envFile", "key", "value"], rename=False) + + EnvValues = [ + # Whether or not Arkime is allowed to delete uploaded/captured PCAP + EnvValue( + os.path.join(envFilesDir, 'arkime.env'), + 'MANAGE_PCAP_FILES', + TrueOrFalseNoQuote(arkimeManagePCAP), + ), + # basic (useBasicAuth=True) vs ldap (useBasicAuth=False) + EnvValue( + os.path.join(envFilesDir, 'auth-common.env'), + 'NGINX_BASIC_AUTH', + TrueOrFalseNoQuote(useBasicAuth), + ), + # StartTLS vs. ldap:// or ldaps:// + EnvValue( + os.path.join(envFilesDir, 'auth-common.env'), + 'NGINX_LDAP_TLS_STUNNEL', + TrueOrFalseNoQuote(((not useBasicAuth) and ldapStartTLS)), + ), + # turn on dark mode, or not + EnvValue( + os.path.join(envFilesDir, 'dashboards-helper.env'), + 'DASHBOARDS_DARKMODE', + TrueOrFalseNoQuote(dashboardsDarkMode), + ), + # OpenSearch index state management snapshot compression + EnvValue( + os.path.join(envFilesDir, 'dashboards-helper.env'), + 'ISM_SNAPSHOT_COMPRESSED', + TrueOrFalseNoQuote(indexSnapshotCompressed), + ), + # delete based on index pattern size + EnvValue( + os.path.join(envFilesDir, 'dashboards-helper.env'), + 'OPENSEARCH_INDEX_SIZE_PRUNE_LIMIT', + indexPruneSizeLimit, + ), + # delete based on index pattern size (sorted by name vs. creation time) + EnvValue( + os.path.join(envFilesDir, 'dashboards-helper.env'), + 'OPENSEARCH_INDEX_SIZE_PRUNE_NAME_SORT', + TrueOrFalseNoQuote(indexPruneNameSort), + ), + # expose a filebeat TCP input listener + EnvValue( + os.path.join(envFilesDir, 'filebeat.env'), + 'FILEBEAT_TCP_LISTEN', + TrueOrFalseNoQuote(filebeatTcpOpen), + ), + # log format expected for events sent to the filebeat TCP input listener + EnvValue( + os.path.join(envFilesDir, 'filebeat.env'), + 'FILEBEAT_TCP_LOG_FORMAT', + filebeatTcpFormat, + ), + # source field name to parse for events sent to the filebeat TCP input listener + EnvValue( + os.path.join(envFilesDir, 'filebeat.env'), + 'FILEBEAT_TCP_PARSE_SOURCE_FIELD', + filebeatTcpSourceField, + ), + # target field name to store decoded JSON fields for events sent to the filebeat TCP input listener + EnvValue( + os.path.join(envFilesDir, 'filebeat.env'), + 'FILEBEAT_TCP_PARSE_TARGET_FIELD', + filebeatTcpTargetField, + ), + # field to drop in events sent to the filebeat TCP input listener + EnvValue( + os.path.join(envFilesDir, 'filebeat.env'), + 'FILEBEAT_TCP_PARSE_DROP_FIELD', + filebeatTcpDropField, + ), + # tag to append to events sent to the filebeat TCP input listener + EnvValue( + os.path.join(envFilesDir, 'filebeat.env'), + 'FILEBEAT_TCP_TAG', + filebeatTcpTag, + ), + # logstash memory allowance + EnvValue( + os.path.join(envFilesDir, 'logstash.env'), + 'LS_JAVA_OPTS', + re.sub(r'(-Xm[sx])(\w+)', fr'\g<1>{lsMemory}', LOGSTASH_JAVA_OPTS_DEFAULT), + ), + # automatic local reverse dns lookup + EnvValue( + os.path.join(envFilesDir, 'logstash.env'), + 'LOGSTASH_REVERSE_DNS', + TrueOrFalseNoQuote(reverseDns), + ), + # automatic MAC OUI lookup + EnvValue( + os.path.join(envFilesDir, 'logstash.env'), + 'LOGSTASH_OUI_LOOKUP', + TrueOrFalseNoQuote(autoOui), + ), + # enrich network traffic metadata via NetBox API calls + EnvValue( + os.path.join(envFilesDir, 'logstash.env'), + 'LOGSTASH_NETBOX_ENRICHMENT', + TrueOrFalseNoQuote(netboxLogstashEnrich), + ), + # logstash pipeline workers + EnvValue( + os.path.join(envFilesDir, 'logstash.env'), + 'pipeline.workers', + lsWorkers, + ), + # freq.py string randomness calculations + EnvValue( + os.path.join(envFilesDir, 'lookup-common.env'), + 'FREQ_LOOKUP', + TrueOrFalseNoQuote(autoFreq), + ), + # enable/disable netbox + EnvValue( + os.path.join(envFilesDir, 'netbox-common.env'), + 'NETBOX_DISABLED', + TrueOrFalseNoQuote(not netboxEnabled), + ), + # HTTPS (nginxSSL=True) vs unencrypted HTTP (nginxSSL=False) + EnvValue( + os.path.join(envFilesDir, 'nginx.env'), + 'NGINX_SSL', + TrueOrFalseNoQuote(nginxSSL), + ), + # OpenSearch primary instance is local vs. remote + EnvValue( + os.path.join(envFilesDir, 'opensearch.env'), + 'OPENSEARCH_LOCAL', + TrueOrFalseNoQuote(not opensearchPrimaryRemote), + ), + # OpenSearch primary instance URL + EnvValue( + os.path.join(envFilesDir, 'opensearch.env'), + 'OPENSEARCH_URL', + opensearchPrimaryUrl, + ), + # OpenSearch primary instance needs SSL verification + EnvValue( + os.path.join(envFilesDir, 'opensearch.env'), + 'OPENSEARCH_SSL_CERTIFICATE_VERIFICATION', + TrueOrFalseNoQuote(opensearchPrimarySslVerify), + ), + # OpenSearch secondary instance URL + EnvValue( + os.path.join(envFilesDir, 'opensearch.env'), + 'OPENSEARCH_SECONDARY_URL', + opensearchSecondaryUrl, + ), + # OpenSearch secondary instance needs SSL verification + EnvValue( + os.path.join(envFilesDir, 'opensearch.env'), + 'OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION', + TrueOrFalseNoQuote(opensearchSecondarySslVerify), + ), + # OpenSearch secondary remote instance is enabled + EnvValue( + os.path.join(envFilesDir, 'opensearch.env'), + 'OPENSEARCH_SECONDARY', + TrueOrFalseNoQuote(opensearchSecondaryRemote), + ), + # OpenSearch memory allowance + EnvValue( + os.path.join(envFilesDir, 'opensearch.env'), + 'OPENSEARCH_JAVA_OPTS', + re.sub(r'(-Xm[sx])(\w+)', fr'\g<1>{osMemory}', OPENSEARCH_JAVA_OPTS_DEFAULT), + ), + # capture pcaps via netsniff-ng + EnvValue( + os.path.join(envFilesDir, 'pcap-capture.env'), + 'PCAP_ENABLE_NETSNIFF', + TrueOrFalseNoQuote(pcapNetSniff), + ), + # capture pcaps via tcpdump + EnvValue( + os.path.join(envFilesDir, 'pcap-capture.env'), + 'PCAP_ENABLE_TCPDUMP', + TrueOrFalseNoQuote(pcapTcpDump and (not pcapNetSniff)), + ), + # disable NIC hardware offloading features and adjust ring buffers + EnvValue( + os.path.join(envFilesDir, 'pcap-capture.env'), + 'PCAP_IFACE_TWEAK', + TrueOrFalseNoQuote(tweakIface), + ), + # capture interface(s) + EnvValue( + os.path.join(envFilesDir, 'pcap-capture.env'), + 'PCAP_IFACE', + pcapIface, + ), + # capture filter + EnvValue( + os.path.join(envFilesDir, 'pcap-capture.env'), + 'PCAP_FILTER', + pcapFilter, + ), + # process UID + EnvValue( + os.path.join(envFilesDir, 'process.env'), + 'PUID', + puid, + ), + # process GID + EnvValue( + os.path.join(envFilesDir, 'process.env'), + 'PGID', + pgid, + ), + # Suricata signature updates (via suricata-update) + EnvValue( + os.path.join(envFilesDir, 'suricata.env'), + 'SURICATA_UPDATE_RULES', + TrueOrFalseNoQuote(suricataRuleUpdate), + ), + # live traffic analysis with Suricata + EnvValue( + os.path.join(envFilesDir, 'suricata-live.env'), + 'SURICATA_LIVE_CAPTURE', + TrueOrFalseNoQuote(liveSuricata), + ), + # rotated captured PCAP analysis with Suricata (not live capture) + EnvValue( + os.path.join(envFilesDir, 'suricata-offline.env'), + 'SURICATA_ROTATED_PCAP', + TrueOrFalseNoQuote(autoSuricata and (not liveSuricata)), + ), + # automatic uploaded pcap analysis with suricata + EnvValue( + os.path.join(envFilesDir, 'suricata-offline.env'), + 'SURICATA_AUTO_ANALYZE_PCAP_FILES', + TrueOrFalseNoQuote(autoSuricata), + ), + # capture source "node name" for locally processed PCAP files + EnvValue( + os.path.join(envFilesDir, 'upload-common.env'), + 'PCAP_NODE_NAME', + HostName, + ), + # zeek file extraction mode + EnvValue( + os.path.join(envFilesDir, 'zeek.env'), + 'ZEEK_EXTRACTOR_MODE', + fileCarveMode, + ), + # zeek file preservation mode + EnvValue( + os.path.join(envFilesDir, 'zeek.env'), + 'EXTRACTED_FILE_PRESERVATION', + filePreserveMode, + ), + # HTTP server for extracted files + EnvValue( + os.path.join(envFilesDir, 'zeek.env'), + 'EXTRACTED_FILE_HTTP_SERVER_ENABLE', + TrueOrFalseNoQuote(fileCarveHttpServer), + ), + # encrypt HTTP server for extracted files + EnvValue( + os.path.join(envFilesDir, 'zeek.env'), + 'EXTRACTED_FILE_HTTP_SERVER_ENCRYPT', + TrueOrFalseNoQuote(fileCarveHttpServer and (len(fileCarveHttpServeEncryptKey) > 0)), + ), + # key for encrypted HTTP-served extracted files (' -> '' for escaping in YAML) + EnvValue( + os.path.join(envFilesDir, 'zeek.env'), + 'EXTRACTED_FILE_HTTP_SERVER_KEY', + fileCarveHttpServeEncryptKey, + ), + # virustotal API key + EnvValue( + os.path.join(envFilesDir, 'zeek.env'), + 'VTOT_API2_KEY', + vtotApiKey, + ), + # file scanning via yara + EnvValue( + os.path.join(envFilesDir, 'zeek.env'), + 'EXTRACTED_FILE_ENABLE_YARA', + TrueOrFalseNoQuote(yaraScan), + ), + # PE file scanning via capa + EnvValue( + os.path.join(envFilesDir, 'zeek.env'), + 'EXTRACTED_FILE_ENABLE_CAPA', + TrueOrFalseNoQuote(capaScan), + ), + # file scanning via clamav + EnvValue( + os.path.join(envFilesDir, 'zeek.env'), + 'EXTRACTED_FILE_ENABLE_CLAMAV', + TrueOrFalseNoQuote(clamAvScan), + ), + # rule updates (yara/capa via git, clamav via freshclam) + EnvValue( + os.path.join(envFilesDir, 'zeek.env'), + 'EXTRACTED_FILE_UPDATE_RULES', + TrueOrFalseNoQuote(fileScanRuleUpdate), + ), + # live traffic analysis with Zeek + EnvValue( + os.path.join(envFilesDir, 'zeek-live.env'), + 'ZEEK_LIVE_CAPTURE', + TrueOrFalseNoQuote(liveZeek), + ), + # rotated captured PCAP analysis with Zeek (not live capture) + EnvValue( + os.path.join(envFilesDir, 'zeek-offline.env'), + 'ZEEK_ROTATED_PCAP', + TrueOrFalseNoQuote(autoZeek and (not liveZeek)), + ), + # automatic uploaded pcap analysis with Zeek + EnvValue( + os.path.join(envFilesDir, 'zeek-offline.env'), + 'ZEEK_AUTO_ANALYZE_PCAP_FILES', + TrueOrFalseNoQuote(autoZeek), + ), + ] + + # now, go through and modify the values in the .env files + for val in EnvValues: + try: + Touch(val.envFile) + except Exception as e: + pass + + try: + dotenv_imported.set_key( + val.envFile, + val.key, + val.value, + quote_mode='never', + encoding='utf-8', + ) + except Exception as e: + eprint(f"Setting value for {val.key} in {val.envFile} module failed: {e}") + + # modify docker-compose specific values (port mappings, volume bind mounts, etc.) in-place in docker-compose files for composeFile in composeFiles: # save off owner of original files composeFileStat = os.stat(composeFile) @@ -890,341 +1262,7 @@ def tweak_malcolm_runtime( currentService = serviceMatch.group(1).lower() serviceStartLine = True - if currentSection is None: - # variables defined in the sections at the top of the compose file - - if 'PUID' in line: - # process UID - line = re.sub(r'(PUID\s*:\s*)(\S+)', fr"\g<1>{puid}", line) - - elif 'PGID' in line: - # process GID - line = re.sub(r'(PGID\s*:\s*)(\S+)', fr"\g<1>{pgid}", line) - - elif 'PCAP_NODE_NAME' in line: - # capture source "node name" for locally processed PCAP files - line = re.sub(r'(PCAP_NODE_NAME\s*:\s*)(\S+)', fr"\g<1>'{HostName}'", line) - - elif 'NGINX_SSL' in line: - # HTTPS (nginxSSL=True) vs unencrypted HTTP (nginxSSL=False) - line = re.sub(r'(NGINX_SSL\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(nginxSSL)}", line) - - elif 'NGINX_BASIC_AUTH' in line: - # basic (useBasicAuth=True) vs ldap (useBasicAuth=False) - line = re.sub( - r'(NGINX_BASIC_AUTH\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(useBasicAuth)}", line - ) - - elif 'NGINX_LDAP_TLS_STUNNEL' in line: - # StartTLS vs. ldap:// or ldaps:// - line = re.sub( - r'(NGINX_LDAP_TLS_STUNNEL\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(((not useBasicAuth) and ldapStartTLS))}", - line, - ) - - elif 'ZEEK_EXTRACTOR_MODE' in line: - # zeek file extraction mode - line = re.sub(r'(ZEEK_EXTRACTOR_MODE\s*:\s*)(\S+)', fr"\g<1>'{fileCarveMode}'", line) - - elif 'EXTRACTED_FILE_PRESERVATION' in line: - # zeek file preservation mode - line = re.sub( - r'(EXTRACTED_FILE_PRESERVATION\s*:\s*)(\S+)', fr"\g<1>'{filePreserveMode}'", line - ) - - elif 'EXTRACTED_FILE_HTTP_SERVER_ENABLE' in line: - # HTTP server for extracted files - line = re.sub( - r'(EXTRACTED_FILE_HTTP_SERVER_ENABLE\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(fileCarveHttpServer)}", - line, - ) - - elif 'EXTRACTED_FILE_HTTP_SERVER_ENCRYPT' in line: - # encrypt HTTP server for extracted files - line = re.sub( - r'(EXTRACTED_FILE_HTTP_SERVER_ENCRYPT\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(fileCarveHttpServer and (len(fileCarveHttpServeEncryptKey) > 0))}", - line, - ) - - elif 'EXTRACTED_FILE_HTTP_SERVER_KEY' in line: - # key for encrypted HTTP-served extracted files (' -> '' for escaping in YAML) - fileCarveHttpServeEncryptKeyEscaped = fileCarveHttpServeEncryptKey.replace("'", "''") - line = re.sub( - r'(EXTRACTED_FILE_HTTP_SERVER_KEY\s*:\s*)(\S+)', - fr"\g<1>'{fileCarveHttpServeEncryptKeyEscaped}'", - line, - ) - - elif 'VTOT_API2_KEY' in line: - # virustotal API key - line = re.sub(r'(VTOT_API2_KEY\s*:\s*)(\S+)', fr"\g<1>'{vtotApiKey}'", line) - - elif 'EXTRACTED_FILE_ENABLE_YARA' in line: - # file scanning via yara - line = re.sub( - r'(EXTRACTED_FILE_ENABLE_YARA\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(yaraScan)}", line - ) - - elif 'EXTRACTED_FILE_ENABLE_CAPA' in line: - # PE file scanning via capa - line = re.sub( - r'(EXTRACTED_FILE_ENABLE_CAPA\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(capaScan)}", line - ) - - elif 'EXTRACTED_FILE_ENABLE_CLAMAV' in line: - # file scanning via clamav - line = re.sub( - r'(EXTRACTED_FILE_ENABLE_CLAMAV\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(clamAvScan)}", - line, - ) - - elif 'EXTRACTED_FILE_UPDATE_RULES' in line: - # rule updates (yara/capa via git, clamav via freshclam) - line = re.sub( - r'(EXTRACTED_FILE_UPDATE_RULES\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(fileScanRuleUpdate)}", - line, - ) - - elif 'SURICATA_UPDATE_RULES' in line: - # Suricata signature updates (via suricata-update) - line = re.sub( - r'(SURICATA_UPDATE_RULES\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(suricataRuleUpdate)}", - line, - ) - - elif 'PCAP_ENABLE_NETSNIFF' in line: - # capture pcaps via netsniff-ng - line = re.sub( - r'(PCAP_ENABLE_NETSNIFF\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(pcapNetSniff)}", line - ) - - elif 'PCAP_ENABLE_TCPDUMP' in line: - # capture pcaps via tcpdump - line = re.sub( - r'(PCAP_ENABLE_TCPDUMP\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(pcapTcpDump)}", line - ) - - elif 'MANAGE_PCAP_FILES' in line: - # Whether or not Arkime is allowed to delete uploaded/captured PCAP - line = re.sub( - r'(MANAGE_PCAP_FILES\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(arkimeManagePCAP)}", line - ) - - elif 'ZEEK_LIVE_CAPTURE' in line: - # live traffic analysis with Zeek - line = re.sub( - r'(ZEEK_LIVE_CAPTURE\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(liveZeek)}", line - ) - - elif 'ZEEK_ROTATED_PCAP' in line: - # rotated captured PCAP analysis with Zeek (not live capture) - line = re.sub( - r'(ZEEK_ROTATED_PCAP\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(autoZeek and (not liveZeek))}", - line, - ) - - elif 'SURICATA_LIVE_CAPTURE' in line: - # live traffic analysis with Suricata - line = re.sub( - r'(SURICATA_LIVE_CAPTURE\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(liveSuricata)}", line - ) - - elif 'SURICATA_ROTATED_PCAP' in line: - # rotated captured PCAP analysis with Suricata (not live capture) - line = re.sub( - r'(SURICATA_ROTATED_PCAP\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(autoSuricata and (not liveSuricata))}", - line, - ) - - elif 'PCAP_IFACE_TWEAK' in line: - # disable NIC hardware offloading features and adjust ring buffers - line = re.sub( - r'(PCAP_IFACE_TWEAK\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(tweakIface)}", line - ) - - elif 'PCAP_IFACE' in line: - # capture interface(s) - line = re.sub(r'(PCAP_IFACE\s*:\s*)(\S+)', fr"\g<1>'{pcapIface}'", line) - - elif 'PCAP_FILTER' in line: - # capture filter - line = re.sub(r'(PCAP_FILTER\s*:)(.*)', fr"\g<1> '{pcapFilter}'", line) - - elif 'ZEEK_AUTO_ANALYZE_PCAP_FILES' in line: - # automatic uploaded pcap analysis with Zeek - line = re.sub( - r'(ZEEK_AUTO_ANALYZE_PCAP_FILES\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(autoZeek)}", - line, - ) - - elif 'SURICATA_AUTO_ANALYZE_PCAP_FILES' in line: - # automatic uploaded pcap analysis with suricata - line = re.sub( - r'(SURICATA_AUTO_ANALYZE_PCAP_FILES\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(autoSuricata)}", - line, - ) - - elif 'LOGSTASH_REVERSE_DNS' in line: - # automatic local reverse dns lookup - line = re.sub( - r'(LOGSTASH_REVERSE_DNS\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(reverseDns)}", line - ) - - elif 'LOGSTASH_OUI_LOOKUP' in line: - # automatic MAC OUI lookup - line = re.sub( - r'(LOGSTASH_OUI_LOOKUP\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(autoOui)}", line - ) - - elif 'LOGSTASH_NETBOX_ENRICHMENT' in line: - # enrich network traffic metadata via NetBox API calls - line = re.sub( - r'(LOGSTASH_NETBOX_ENRICHMENT\s*:(\s*&\S+)?\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(netboxLogstashEnrich)}", - line, - ) - - elif 'NETBOX_DISABLED' in line: - # enable/disable netbox - line = re.sub( - r'(NETBOX_DISABLED\s*:(\s*&\S+)?\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(not netboxEnabled)}", - line, - ) - - elif 'pipeline.workers' in line: - # logstash pipeline workers - line = re.sub(r'(pipeline\.workers\s*:\s*)(\S+)', fr"\g<1>{lsWorkers}", line) - - elif 'DASHBOARDS_DARKMODE' in line: - # turn on dark mode, or not - line = re.sub( - r'(DASHBOARDS_DARKMODE\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(dashboardsDarkMode)}", - line, - ) - - elif 'FREQ_LOOKUP' in line: - # freq.py string randomness calculations - line = re.sub(r'(FREQ_LOOKUP\s*:\s*)(\S+)', fr"\g<1>{TrueOrFalseQuote(autoFreq)}", line) - - elif 'FILEBEAT_TCP_LISTEN' in line: - # expose a filebeat TCP input listener - line = re.sub( - r'(FILEBEAT_TCP_LISTEN\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(filebeatTcpOpen)}", - line, - ) - - elif 'FILEBEAT_TCP_LOG_FORMAT' in line: - # log format expected for events sent to the filebeat TCP input listener - line = re.sub( - r'(FILEBEAT_TCP_LOG_FORMAT\s*:\s*)(\S+)', fr"\g<1>'{filebeatTcpFormat}'", line - ) - - elif 'FILEBEAT_TCP_PARSE_SOURCE_FIELD' in line: - # source field name to parse for events sent to the filebeat TCP input listener - line = re.sub( - r'(FILEBEAT_TCP_PARSE_SOURCE_FIELD\s*:\s*)(\S+)', - fr"\g<1>'{filebeatTcpSourceField}'", - line, - ) - - elif 'FILEBEAT_TCP_PARSE_TARGET_FIELD' in line: - # target field name to store decoded JSON fields for events sent to the filebeat TCP input listener - line = re.sub( - r'(FILEBEAT_TCP_PARSE_TARGET_FIELD\s*:\s*)(\S+)', - fr"\g<1>'{filebeatTcpTargetField}'", - line, - ) - - elif 'FILEBEAT_TCP_PARSE_DROP_FIELD' in line: - # field to drop in events sent to the filebeat TCP input listener - line = re.sub( - r'(FILEBEAT_TCP_PARSE_DROP_FIELD\s*:\s*)(\S+)', fr"\g<1>'{filebeatTcpDropField}'", line - ) - - elif 'FILEBEAT_TCP_TAG' in line: - # tag to append to events sent to the filebeat TCP input listener - line = re.sub(r'(FILEBEAT_TCP_TAG\s*:\s*)(\S+)', fr"\g<1>'{filebeatTcpTag}'", line) - - elif 'OPENSEARCH_LOCAL' in line: - # OpenSearch primary instance is local vs. remote - line = re.sub( - r'(OPENSEARCH_LOCAL\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(not opensearchPrimaryRemote)}", - line, - ) - - elif 'OPENSEARCH_URL' in line: - # OpenSearch primary instance URL - line = re.sub(r'(OPENSEARCH_URL\s*:\s*)(\S+)', fr"\g<1>'{opensearchPrimaryUrl}'", line) - - elif 'OPENSEARCH_SSL_CERTIFICATE_VERIFICATION' in line: - # OpenSearch primary instance needs SSL verification - line = re.sub( - r'(OPENSEARCH_SSL_CERTIFICATE_VERIFICATION\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(opensearchPrimarySslVerify)}", - line, - ) - - elif 'OPENSEARCH_SECONDARY_URL' in line: - # OpenSearch secondary instance URL - line = re.sub( - r'(OPENSEARCH_SECONDARY_URL\s*:\s*)(\S+)', fr"\g<1>'{opensearchSecondaryUrl}'", line - ) - - elif 'OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION' in line: - # OpenSearch secondary instance needs SSL verification - line = re.sub( - r'(OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(opensearchSecondarySslVerify)}", - line, - ) - - elif 'OPENSEARCH_SECONDARY' in line: - # OpenSearch secondary remote instance is enabled - line = re.sub( - r'(OPENSEARCH_SECONDARY\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(opensearchSecondaryRemote)}", - line, - ) - - elif 'ISM_SNAPSHOT_COMPRESSED' in line: - # OpenSearch index state management snapshot compression - line = re.sub( - r'(ISM_SNAPSHOT_COMPRESSED\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(indexSnapshotCompressed)}", - line, - ) - - elif 'OPENSEARCH_INDEX_SIZE_PRUNE_LIMIT' in line: - # delete based on index pattern size - line = re.sub( - r'(OPENSEARCH_INDEX_SIZE_PRUNE_LIMIT\s*:\s*)(\S+)', - fr"\g<1>'{indexPruneSizeLimit}'", - line, - ) - - elif 'OPENSEARCH_INDEX_SIZE_PRUNE_NAME_SORT' in line: - # delete based on index pattern size (sorted by name vs. creation time) - line = re.sub( - r'(OPENSEARCH_INDEX_SIZE_PRUNE_NAME_SORT\s*:\s*)(\S+)', - fr"\g<1>{TrueOrFalseQuote(indexPruneNameSort)}", - line, - ) - - elif (currentSection == 'services') and (not serviceStartLine) and (currentService is not None): + if (currentSection == 'services') and (not serviceStartLine) and (currentService is not None): # down in the individual services sections of the compose file if re.match(r'^\s*restart\s*:.*$', line): @@ -1287,10 +1325,6 @@ def tweak_malcolm_runtime( elif currentService == 'logstash': # stuff specifically in the logstash section - if 'LS_JAVA_OPTS' in line: - # logstash memory allowance - line = re.sub(r'(-Xm[sx])(\w+)', fr'\g<1>{lsMemory}', line) - if re.match(r'^[\s#]*-\s*"([\d\.]+:)?\d+:\d+"\s*$', line): # set bind IP based on whether it should be externally exposed or not line = re.sub( @@ -1301,11 +1335,7 @@ def tweak_malcolm_runtime( elif currentService == 'opensearch': # stuff specifically in the opensearch section - if 'OPENSEARCH_JAVA_OPTS' in line: - # OpenSearch memory allowance - line = re.sub(r'(-Xm[sx])(\w+)', fr'\g<1>{osMemory}', line) - - elif re.match(r'^\s*-.+:/usr/share/opensearch/data(:.+)?\s*$', line): + if re.match(r'^\s*-.+:/usr/share/opensearch/data(:.+)?\s*$', line): # OpenSearch indexes directory line = ReplaceBindMountLocation( line, @@ -2366,6 +2396,7 @@ def main(): global args global requests_imported global yaml_imported + global dotenv_imported # extract arguments from the command line # print (sys.argv[1:]); @@ -2415,6 +2446,16 @@ def main(): default='', help='Single docker-compose YML file to configure', ) + parser.add_argument( + '-e', + '--environment-dir', + required=False, + dest='configDir', + metavar='', + type=str, + default='', + help="Directory containing Malcolm's .env files", + ) parser.add_argument( '-d', '--defaults', @@ -2426,7 +2467,6 @@ def main(): help="Accept defaults to prompts without user interaction", ) parser.add_argument( - '-l', '--logstash-expose', dest='exposeLogstash', type=str2bool, @@ -2436,7 +2476,6 @@ def main(): help="Expose Logstash port to external hosts", ) parser.add_argument( - '-e', '--opensearch-expose', dest='exposeOpenSearch', type=str2bool, @@ -2446,7 +2485,6 @@ def main(): help="Expose OpenSearch port to external hosts", ) parser.add_argument( - '-t', '--filebeat-tcp-expose', dest='exposeFilebeatTcp', type=str2bool, @@ -2456,7 +2494,6 @@ def main(): help="Expose Filebeat TCP port to external hosts", ) parser.add_argument( - '-s', '--sftp-expose', dest='exposeSFTP', type=str2bool, @@ -2492,10 +2529,12 @@ def main(): requests_imported = RequestsDynamic(debug=args.debug, forceInteraction=(not args.acceptDefaultsNonInteractive)) yaml_imported = YAMLDynamic(debug=args.debug, forceInteraction=(not args.acceptDefaultsNonInteractive)) + dotenv_imported = DotEnvDynamic(debug=args.debug, forceInteraction=(not args.acceptDefaultsNonInteractive)) if args.debug: eprint(f"Imported requests: {requests_imported}") eprint(f"Imported yaml: {yaml_imported}") - if (not requests_imported) or (not yaml_imported): + eprint(f"Imported dotenv: {dotenv_imported}") + if (not requests_imported) or (not yaml_imported) or (not dotenv_imported): exit(2) # If Malcolm and images tarballs are provided, we will use them. @@ -2555,13 +2594,25 @@ def main(): if hasattr(installer, 'install_docker_images'): success = installer.install_docker_images(imageFile) - if args.configOnly or (args.configFile and os.path.isfile(args.configFile)): - if not args.configFile: + if ( + args.configOnly + or (args.configDir and os.path.isdir(args.configDir)) + or (args.configFile and os.path.isfile(args.configFile)) + ): + if args.configFile and os.path.isfile(args.configFile): + installPath = os.path.dirname(os.path.realpath(args.configFile)) + + elif args.configDir and os.path.isfile(args.configDir): + installPath = os.path.dirname(os.path.realpath(args.configDir)) + + else: for testPath in [origPath, ScriptPath, os.path.realpath(os.path.join(ScriptPath, ".."))]: - if os.path.isfile(os.path.join(testPath, "docker-compose.yml")): + if os.path.isfile(os.path.join(testPath, "docker-compose.yml")) or os.path.isdir( + os.path.join(testPath, "config") + ): installPath = testPath - else: - installPath = os.path.dirname(os.path.realpath(args.configFile)) + break + success = (installPath is not None) and os.path.isdir(installPath) if args.debug: eprint(f"Malcolm installation detected at {installPath}") diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index a6f468607..b1e1a3793 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -784,6 +784,10 @@ def YAMLDynamic(debug=False, forceInteraction=False): return DoDynamicImport("yaml", "pyyaml", interactive=forceInteraction, debug=debug) +def DotEnvDynamic(debug=False, forceInteraction=False): + return DoDynamicImport("dotenv", "python-dotenv", interactive=forceInteraction, debug=debug) + + ################################################################################################### # do the required auth files for Malcolm exist? def MalcolmAuthFilesExist(): From 3e635ad91b2eea1a84921c07825932f0cf6f887e Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 09:43:35 -0600 Subject: [PATCH 041/239] tweak dockerignore/gitignore --- .dockerignore | 2 +- .gitignore | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.dockerignore b/.dockerignore index 4e33ae8cd..284ba36f7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -27,7 +27,7 @@ arkime-logs arkime-raw malcolm-iso sensor-iso -nginx/nginx_ldap.conf +nginx/nginx_ldap*.conf pcap _site scripts diff --git a/.gitignore b/.gitignore index 991827d72..3fa947016 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,7 @@ /nginx/certs /nginx/ca-trust /nginx/htpasswd -/nginx/nginx_ldap.conf +/nginx/nginx_ldap*.conf /htadmin/config.ini /htadmin/metadata From c67ebc5579546d226bade5359743fd620e77403c Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 09:49:31 -0600 Subject: [PATCH 042/239] fix file-upload start --- docker-compose-standalone.yml | 1 + docker-compose.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index e28e20417..d06c2d790 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -410,6 +410,7 @@ services: - ./config/process.env - ./config/ssl.env - ./config/auth.env + - ./config/upload.env environment: VIRTUAL_HOST : 'upload.malcolm.local' depends_on: diff --git a/docker-compose.yml b/docker-compose.yml index 1fefac40f..252cf7aaf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -461,6 +461,7 @@ services: - ./config/process.env - ./config/ssl.env - ./config/auth.env + - ./config/upload.env environment: VIRTUAL_HOST : 'upload.malcolm.local' depends_on: From 8266cd9c04205e256bf22a4c3e534272810c7fb3 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 10:19:36 -0600 Subject: [PATCH 043/239] set all netbox-related variables --- scripts/install.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/scripts/install.py b/scripts/install.py index 378df1e1d..917d30606 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -1001,6 +1001,24 @@ def tweak_malcolm_runtime( 'NETBOX_DISABLED', TrueOrFalseNoQuote(not netboxEnabled), ), + # enable/disable netbox (postgres) + EnvValue( + os.path.join(envFilesDir, 'netbox-common.env'), + 'NETBOX_POSTGRES_DISABLED', + TrueOrFalseNoQuote(not netboxEnabled), + ), + # enable/disable netbox (redis) + EnvValue( + os.path.join(envFilesDir, 'netbox-common.env'), + 'NETBOX_REDIS_DISABLED', + TrueOrFalseNoQuote(not netboxEnabled), + ), + # enable/disable netbox (redis cache) + EnvValue( + os.path.join(envFilesDir, 'netbox-common.env'), + 'NETBOX_REDIS_CACHE_DISABLED', + TrueOrFalseNoQuote(not netboxEnabled), + ), # HTTPS (nginxSSL=True) vs unencrypted HTTP (nginxSSL=False) EnvValue( os.path.join(envFilesDir, 'nginx.env'), From 700746fc402b367a9d08426947324651b584e482 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 10:21:55 -0600 Subject: [PATCH 044/239] Fix logstash->netbox connection --- docker-compose-standalone.yml | 1 + docker-compose.yml | 1 + kubernetes/13-logstash.yml | 2 ++ 3 files changed, 4 insertions(+) diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index d06c2d790..b2b4942f0 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -108,6 +108,7 @@ services: - ./config/ssl.env - ./config/opensearch.env - ./config/netbox-common.env + - ./config/netbox.env - ./config/beats-common.env - ./config/lookup-common.env - ./config/logstash.env diff --git a/docker-compose.yml b/docker-compose.yml index 252cf7aaf..40f83d2b9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -120,6 +120,7 @@ services: - ./config/ssl.env - ./config/opensearch.env - ./config/netbox-common.env + - ./config/netbox.env - ./config/beats-common.env - ./config/lookup-common.env - ./config/logstash.env diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index 1659d8067..cc0814c76 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -49,6 +49,8 @@ spec: name: opensearch-env - configMapRef: name: netbox-common-env + - configMapRef: + name: netbox-env - configMapRef: name: logstash-env - configMapRef: From 4cb2747cf4518285fe4438c6fe3575676f998dd2 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 10:28:20 -0600 Subject: [PATCH 045/239] arkime should default to WISE on --- Dockerfiles/arkime.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index cff6f4618..1b0232b8c 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -101,7 +101,7 @@ ARG ARKIME_ECS_DATASET=session ARG ARKIME_INTERFACE=eth0 ARG ARKIME_ANALYZE_PCAP_THREADS=1 ARG OPENSEARCH_MAX_SHARDS_PER_NODE=2500 -ARG WISE=off +ARG WISE=on ARG VIEWER=on #Whether or not Arkime is in charge of deleting old PCAP files to reclaim space ARG MANAGE_PCAP_FILES=false From 1654dc18ec704c60f8836f04bf3d3c7c7ca4d651 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 10:35:34 -0600 Subject: [PATCH 046/239] don't prompt for both netsniff and tcpdump --- docs/ubuntu-install-example.md | 2 -- scripts/install.py | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index bc528d210..932bec9f7 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -190,8 +190,6 @@ Should Malcolm capture live network traffic to PCAP files for analysis with Arki Capture packets using netsniff-ng? (Y/n): y -Capture packets using tcpdump? (y/N): n - Should Arkime delete PCAP files based on available storage (see https://arkime.com/faq#pcap-deletion)? (y/N): y Should Malcolm analyze live network traffic with Suricata? (y/N): y diff --git a/scripts/install.py b/scripts/install.py index 917d30606..a1545ab54 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -847,7 +847,8 @@ def tweak_malcolm_runtime( 'Should Malcolm capture live network traffic to PCAP files for analysis with Arkime?', default=False ): pcapNetSniff = InstallerYesOrNo('Capture packets using netsniff-ng?', default=True) - pcapTcpDump = InstallerYesOrNo('Capture packets using tcpdump?', default=(not pcapNetSniff)) + if not pcapNetSniff: + pcapTcpDump = InstallerYesOrNo('Capture packets using tcpdump?', default=True) arkimeManagePCAP = InstallerYesOrNo( 'Should Arkime delete PCAP files based on available storage (see https://arkime.com/faq#pcap-deletion)?', default=False, From 87357cf9c8aa3ef53e5bd31259ad5ec18577f9f5 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 10:49:56 -0600 Subject: [PATCH 047/239] triggerbuild --- .trigger_workflow_build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.trigger_workflow_build b/.trigger_workflow_build index 50520d4f0..3e6f3d089 100644 --- a/.trigger_workflow_build +++ b/.trigger_workflow_build @@ -1,2 +1,2 @@ # this file exists solely for the purpose of being updated and seen by github to trigger a commit build action -4 +5 From 375fdcf0482023eef0dff5649ffd555f27890422 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 12:32:02 -0600 Subject: [PATCH 048/239] prepopulate env files in new directory if they don't exist --- scripts/control.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++ scripts/install.py | 12 ++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/scripts/control.py b/scripts/control.py index b6a8d174a..cf7404e2c 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -250,6 +250,30 @@ def keystore_op(service, dropPriv=False, *keystore_args, **run_process_kwargs): return (err == 0), results +################################################################################################### +def checkEnvFilesExist(): + global args + + # first, if the configDir is completely empty, then populate from defaults + defaultConfigDir = os.path.join(MalcolmPath, 'config') + if ( + (args.configDir is not None) + and os.path.isdir(args.configDir) + and os.path.isdir(defaultConfigDir) + and (not same_file_or_dir(defaultConfigDir, args.configDir)) + and (not os.listdir(args.configDir)) + ): + for defaultEnvExampleFile in glob.glob(os.path.join(defaultConfigDir, '*.env.example')): + shutil.copy2(defaultEnvExampleFile, args.configDir) + + # if a specific config/*.env file doesn't exist, use the *.example.env files as defaults + envExampleFiles = glob.glob(os.path.join(args.configDir, '*.env.example')) + for envExampleFile in envExampleFiles: + envFile = envExampleFile[: -len('.example')] + if not os.path.isfile(envFile): + shutil.copyfile(envExampleFile, envFile) + + ################################################################################################### def status(): global args @@ -1437,6 +1461,16 @@ def main(): default='docker-compose.yml', help='docker-compose YML file', ) + parser.add_argument( + '-e', + '--environment-dir', + required=False, + dest='configDir', + metavar='', + type=str, + default='', + help="Directory containing Malcolm's .env files", + ) parser.add_argument( '-s', '--service', @@ -1568,6 +1602,22 @@ def main(): ): raise Exception(f'{ScriptName} should not be run as root') + # if .env directory is unspecified, use the default ./config directory + for firstLoop in (True, False): + if (args.configDir is None) or (not os.path.isdir(args.configDir)): + if firstLoop: + if args.configDir is None: + args.configDir = os.path.join(MalcolmPath, 'config') + try: + os.makedirs(args.configDir) + except OSError as exc: + if (exc.errno == errno.EEXIST) and os.path.isdir(args.configDir): + pass + else: + raise + else: + raise Exception(f"Could not determine configuration directory containing Malcolm's .env files") + # create local temporary directory for docker-compose because we may have noexec on /tmp try: os.makedirs(MalcolmTmpPath) @@ -1634,6 +1684,10 @@ def main(): ): raise Exception(f'NetBox configuration database file must be specified with --netbox-restore') + # the compose file references various .env files in just about every operation this script does, + # so make sure they exist right off the bat + checkEnvFilesExist() + # stop Malcolm (and wipe data if requestsed) if args.cmdRestart or args.cmdStop or args.cmdWipe: stop(wipe=args.cmdWipe) diff --git a/scripts/install.py b/scripts/install.py index a1545ab54..8ea94ab34 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -872,7 +872,17 @@ def tweak_malcolm_runtime( # modify values in .env files in envFilesDir - # first, look at the ".env.example" files and if the corresponding .env file doesn't exist, create it + # first, if the envFilesDir is completely empty, then populate from defaults + defaultConfigDir = os.path.join(malcolm_install_path, 'config') + if ( + os.path.isdir(defaultConfigDir) + and (not same_file_or_dir(defaultConfigDir, envFilesDir)) + and (not os.listdir(envFilesDir)) + ): + for defaultEnvExampleFile in glob.glob(os.path.join(defaultConfigDir, '*.env.example')): + shutil.copy2(defaultEnvExampleFile, envFilesDir) + + # if a specific config/*.env file doesn't exist, use the *.example.env files as defaults envExampleFiles = glob.glob(os.path.join(envFilesDir, '*.env.example')) for envExampleFile in envExampleFiles: envFile = envExampleFile[: -len('.example')] From ca5821136078f706d565c3f2cbad02a2a6dc358e Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 12:38:29 -0600 Subject: [PATCH 049/239] prepopulate env files in new directory if they don't exist --- scripts/install.py | 151 +++++++++++++++++++++++---------------------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/scripts/install.py b/scripts/install.py index 8ea94ab34..96080c7b1 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -5,6 +5,7 @@ import argparse import datetime +import errno import fileinput import getpass import glob @@ -336,17 +337,8 @@ def tweak_malcolm_runtime( global args global dotenv_imported - envFilesDir = None composeFiles = [] - # determine directory containing .env files - if (not args.configDir) or (not os.path.isdir(args.configDir)): - # use default config directory - envFilesDir = os.path.join(malcolm_install_path, 'config') - else: - # configuration directory explicitly specified - envFilesDir = args.configDir - # determine docker-compose file(s) if not args.configFile: # get a list of all of the docker-compose files @@ -357,7 +349,7 @@ def tweak_malcolm_runtime( composeFiles = [os.path.realpath(args.configFile)] malcolm_install_path = os.path.dirname(composeFiles[0]) - if (not envFilesDir) or (not os.path.isdir(envFilesDir)): + if (not args.configDir) or (not os.path.isdir(args.configDir)): raise Exception("Could not determine configuration directory containing Malcolm's .env files") # figure out what UID/GID to run non-rood processes under docker as @@ -389,7 +381,7 @@ def tweak_malcolm_runtime( if self.debug: eprint( - f'{malcolm_install_path} with "{composeFiles}" and "{envFilesDir}", system memory is {self.totalMemoryGigs} GiB' + f'{malcolm_install_path} with "{composeFiles}" and "{args.configDir}", system memory is {self.totalMemoryGigs} GiB' ) if self.totalMemoryGigs >= 63.0: @@ -870,20 +862,20 @@ def tweak_malcolm_runtime( dashboardsDarkMode = InstallerYesOrNo('Enable dark mode for OpenSearch Dashboards?', default=True) - # modify values in .env files in envFilesDir + # modify values in .env files in args.configDir - # first, if the envFilesDir is completely empty, then populate from defaults + # first, if the args.configDir is completely empty, then populate from defaults defaultConfigDir = os.path.join(malcolm_install_path, 'config') if ( os.path.isdir(defaultConfigDir) - and (not same_file_or_dir(defaultConfigDir, envFilesDir)) - and (not os.listdir(envFilesDir)) + and (not same_file_or_dir(defaultConfigDir, args.configDir)) + and (not os.listdir(args.configDir)) ): for defaultEnvExampleFile in glob.glob(os.path.join(defaultConfigDir, '*.env.example')): - shutil.copy2(defaultEnvExampleFile, envFilesDir) + shutil.copy2(defaultEnvExampleFile, args.configDir) # if a specific config/*.env file doesn't exist, use the *.example.env files as defaults - envExampleFiles = glob.glob(os.path.join(envFilesDir, '*.env.example')) + envExampleFiles = glob.glob(os.path.join(args.configDir, '*.env.example')) for envExampleFile in envExampleFiles: envFile = envExampleFile[: -len('.example')] if not os.path.isfile(envFile): @@ -894,337 +886,337 @@ def tweak_malcolm_runtime( EnvValues = [ # Whether or not Arkime is allowed to delete uploaded/captured PCAP EnvValue( - os.path.join(envFilesDir, 'arkime.env'), + os.path.join(args.configDir, 'arkime.env'), 'MANAGE_PCAP_FILES', TrueOrFalseNoQuote(arkimeManagePCAP), ), # basic (useBasicAuth=True) vs ldap (useBasicAuth=False) EnvValue( - os.path.join(envFilesDir, 'auth-common.env'), + os.path.join(args.configDir, 'auth-common.env'), 'NGINX_BASIC_AUTH', TrueOrFalseNoQuote(useBasicAuth), ), # StartTLS vs. ldap:// or ldaps:// EnvValue( - os.path.join(envFilesDir, 'auth-common.env'), + os.path.join(args.configDir, 'auth-common.env'), 'NGINX_LDAP_TLS_STUNNEL', TrueOrFalseNoQuote(((not useBasicAuth) and ldapStartTLS)), ), # turn on dark mode, or not EnvValue( - os.path.join(envFilesDir, 'dashboards-helper.env'), + os.path.join(args.configDir, 'dashboards-helper.env'), 'DASHBOARDS_DARKMODE', TrueOrFalseNoQuote(dashboardsDarkMode), ), # OpenSearch index state management snapshot compression EnvValue( - os.path.join(envFilesDir, 'dashboards-helper.env'), + os.path.join(args.configDir, 'dashboards-helper.env'), 'ISM_SNAPSHOT_COMPRESSED', TrueOrFalseNoQuote(indexSnapshotCompressed), ), # delete based on index pattern size EnvValue( - os.path.join(envFilesDir, 'dashboards-helper.env'), + os.path.join(args.configDir, 'dashboards-helper.env'), 'OPENSEARCH_INDEX_SIZE_PRUNE_LIMIT', indexPruneSizeLimit, ), # delete based on index pattern size (sorted by name vs. creation time) EnvValue( - os.path.join(envFilesDir, 'dashboards-helper.env'), + os.path.join(args.configDir, 'dashboards-helper.env'), 'OPENSEARCH_INDEX_SIZE_PRUNE_NAME_SORT', TrueOrFalseNoQuote(indexPruneNameSort), ), # expose a filebeat TCP input listener EnvValue( - os.path.join(envFilesDir, 'filebeat.env'), + os.path.join(args.configDir, 'filebeat.env'), 'FILEBEAT_TCP_LISTEN', TrueOrFalseNoQuote(filebeatTcpOpen), ), # log format expected for events sent to the filebeat TCP input listener EnvValue( - os.path.join(envFilesDir, 'filebeat.env'), + os.path.join(args.configDir, 'filebeat.env'), 'FILEBEAT_TCP_LOG_FORMAT', filebeatTcpFormat, ), # source field name to parse for events sent to the filebeat TCP input listener EnvValue( - os.path.join(envFilesDir, 'filebeat.env'), + os.path.join(args.configDir, 'filebeat.env'), 'FILEBEAT_TCP_PARSE_SOURCE_FIELD', filebeatTcpSourceField, ), # target field name to store decoded JSON fields for events sent to the filebeat TCP input listener EnvValue( - os.path.join(envFilesDir, 'filebeat.env'), + os.path.join(args.configDir, 'filebeat.env'), 'FILEBEAT_TCP_PARSE_TARGET_FIELD', filebeatTcpTargetField, ), # field to drop in events sent to the filebeat TCP input listener EnvValue( - os.path.join(envFilesDir, 'filebeat.env'), + os.path.join(args.configDir, 'filebeat.env'), 'FILEBEAT_TCP_PARSE_DROP_FIELD', filebeatTcpDropField, ), # tag to append to events sent to the filebeat TCP input listener EnvValue( - os.path.join(envFilesDir, 'filebeat.env'), + os.path.join(args.configDir, 'filebeat.env'), 'FILEBEAT_TCP_TAG', filebeatTcpTag, ), # logstash memory allowance EnvValue( - os.path.join(envFilesDir, 'logstash.env'), + os.path.join(args.configDir, 'logstash.env'), 'LS_JAVA_OPTS', re.sub(r'(-Xm[sx])(\w+)', fr'\g<1>{lsMemory}', LOGSTASH_JAVA_OPTS_DEFAULT), ), # automatic local reverse dns lookup EnvValue( - os.path.join(envFilesDir, 'logstash.env'), + os.path.join(args.configDir, 'logstash.env'), 'LOGSTASH_REVERSE_DNS', TrueOrFalseNoQuote(reverseDns), ), # automatic MAC OUI lookup EnvValue( - os.path.join(envFilesDir, 'logstash.env'), + os.path.join(args.configDir, 'logstash.env'), 'LOGSTASH_OUI_LOOKUP', TrueOrFalseNoQuote(autoOui), ), # enrich network traffic metadata via NetBox API calls EnvValue( - os.path.join(envFilesDir, 'logstash.env'), + os.path.join(args.configDir, 'logstash.env'), 'LOGSTASH_NETBOX_ENRICHMENT', TrueOrFalseNoQuote(netboxLogstashEnrich), ), # logstash pipeline workers EnvValue( - os.path.join(envFilesDir, 'logstash.env'), + os.path.join(args.configDir, 'logstash.env'), 'pipeline.workers', lsWorkers, ), # freq.py string randomness calculations EnvValue( - os.path.join(envFilesDir, 'lookup-common.env'), + os.path.join(args.configDir, 'lookup-common.env'), 'FREQ_LOOKUP', TrueOrFalseNoQuote(autoFreq), ), # enable/disable netbox EnvValue( - os.path.join(envFilesDir, 'netbox-common.env'), + os.path.join(args.configDir, 'netbox-common.env'), 'NETBOX_DISABLED', TrueOrFalseNoQuote(not netboxEnabled), ), # enable/disable netbox (postgres) EnvValue( - os.path.join(envFilesDir, 'netbox-common.env'), + os.path.join(args.configDir, 'netbox-common.env'), 'NETBOX_POSTGRES_DISABLED', TrueOrFalseNoQuote(not netboxEnabled), ), # enable/disable netbox (redis) EnvValue( - os.path.join(envFilesDir, 'netbox-common.env'), + os.path.join(args.configDir, 'netbox-common.env'), 'NETBOX_REDIS_DISABLED', TrueOrFalseNoQuote(not netboxEnabled), ), # enable/disable netbox (redis cache) EnvValue( - os.path.join(envFilesDir, 'netbox-common.env'), + os.path.join(args.configDir, 'netbox-common.env'), 'NETBOX_REDIS_CACHE_DISABLED', TrueOrFalseNoQuote(not netboxEnabled), ), # HTTPS (nginxSSL=True) vs unencrypted HTTP (nginxSSL=False) EnvValue( - os.path.join(envFilesDir, 'nginx.env'), + os.path.join(args.configDir, 'nginx.env'), 'NGINX_SSL', TrueOrFalseNoQuote(nginxSSL), ), # OpenSearch primary instance is local vs. remote EnvValue( - os.path.join(envFilesDir, 'opensearch.env'), + os.path.join(args.configDir, 'opensearch.env'), 'OPENSEARCH_LOCAL', TrueOrFalseNoQuote(not opensearchPrimaryRemote), ), # OpenSearch primary instance URL EnvValue( - os.path.join(envFilesDir, 'opensearch.env'), + os.path.join(args.configDir, 'opensearch.env'), 'OPENSEARCH_URL', opensearchPrimaryUrl, ), # OpenSearch primary instance needs SSL verification EnvValue( - os.path.join(envFilesDir, 'opensearch.env'), + os.path.join(args.configDir, 'opensearch.env'), 'OPENSEARCH_SSL_CERTIFICATE_VERIFICATION', TrueOrFalseNoQuote(opensearchPrimarySslVerify), ), # OpenSearch secondary instance URL EnvValue( - os.path.join(envFilesDir, 'opensearch.env'), + os.path.join(args.configDir, 'opensearch.env'), 'OPENSEARCH_SECONDARY_URL', opensearchSecondaryUrl, ), # OpenSearch secondary instance needs SSL verification EnvValue( - os.path.join(envFilesDir, 'opensearch.env'), + os.path.join(args.configDir, 'opensearch.env'), 'OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION', TrueOrFalseNoQuote(opensearchSecondarySslVerify), ), # OpenSearch secondary remote instance is enabled EnvValue( - os.path.join(envFilesDir, 'opensearch.env'), + os.path.join(args.configDir, 'opensearch.env'), 'OPENSEARCH_SECONDARY', TrueOrFalseNoQuote(opensearchSecondaryRemote), ), # OpenSearch memory allowance EnvValue( - os.path.join(envFilesDir, 'opensearch.env'), + os.path.join(args.configDir, 'opensearch.env'), 'OPENSEARCH_JAVA_OPTS', re.sub(r'(-Xm[sx])(\w+)', fr'\g<1>{osMemory}', OPENSEARCH_JAVA_OPTS_DEFAULT), ), # capture pcaps via netsniff-ng EnvValue( - os.path.join(envFilesDir, 'pcap-capture.env'), + os.path.join(args.configDir, 'pcap-capture.env'), 'PCAP_ENABLE_NETSNIFF', TrueOrFalseNoQuote(pcapNetSniff), ), # capture pcaps via tcpdump EnvValue( - os.path.join(envFilesDir, 'pcap-capture.env'), + os.path.join(args.configDir, 'pcap-capture.env'), 'PCAP_ENABLE_TCPDUMP', TrueOrFalseNoQuote(pcapTcpDump and (not pcapNetSniff)), ), # disable NIC hardware offloading features and adjust ring buffers EnvValue( - os.path.join(envFilesDir, 'pcap-capture.env'), + os.path.join(args.configDir, 'pcap-capture.env'), 'PCAP_IFACE_TWEAK', TrueOrFalseNoQuote(tweakIface), ), # capture interface(s) EnvValue( - os.path.join(envFilesDir, 'pcap-capture.env'), + os.path.join(args.configDir, 'pcap-capture.env'), 'PCAP_IFACE', pcapIface, ), # capture filter EnvValue( - os.path.join(envFilesDir, 'pcap-capture.env'), + os.path.join(args.configDir, 'pcap-capture.env'), 'PCAP_FILTER', pcapFilter, ), # process UID EnvValue( - os.path.join(envFilesDir, 'process.env'), + os.path.join(args.configDir, 'process.env'), 'PUID', puid, ), # process GID EnvValue( - os.path.join(envFilesDir, 'process.env'), + os.path.join(args.configDir, 'process.env'), 'PGID', pgid, ), # Suricata signature updates (via suricata-update) EnvValue( - os.path.join(envFilesDir, 'suricata.env'), + os.path.join(args.configDir, 'suricata.env'), 'SURICATA_UPDATE_RULES', TrueOrFalseNoQuote(suricataRuleUpdate), ), # live traffic analysis with Suricata EnvValue( - os.path.join(envFilesDir, 'suricata-live.env'), + os.path.join(args.configDir, 'suricata-live.env'), 'SURICATA_LIVE_CAPTURE', TrueOrFalseNoQuote(liveSuricata), ), # rotated captured PCAP analysis with Suricata (not live capture) EnvValue( - os.path.join(envFilesDir, 'suricata-offline.env'), + os.path.join(args.configDir, 'suricata-offline.env'), 'SURICATA_ROTATED_PCAP', TrueOrFalseNoQuote(autoSuricata and (not liveSuricata)), ), # automatic uploaded pcap analysis with suricata EnvValue( - os.path.join(envFilesDir, 'suricata-offline.env'), + os.path.join(args.configDir, 'suricata-offline.env'), 'SURICATA_AUTO_ANALYZE_PCAP_FILES', TrueOrFalseNoQuote(autoSuricata), ), # capture source "node name" for locally processed PCAP files EnvValue( - os.path.join(envFilesDir, 'upload-common.env'), + os.path.join(args.configDir, 'upload-common.env'), 'PCAP_NODE_NAME', HostName, ), # zeek file extraction mode EnvValue( - os.path.join(envFilesDir, 'zeek.env'), + os.path.join(args.configDir, 'zeek.env'), 'ZEEK_EXTRACTOR_MODE', fileCarveMode, ), # zeek file preservation mode EnvValue( - os.path.join(envFilesDir, 'zeek.env'), + os.path.join(args.configDir, 'zeek.env'), 'EXTRACTED_FILE_PRESERVATION', filePreserveMode, ), # HTTP server for extracted files EnvValue( - os.path.join(envFilesDir, 'zeek.env'), + os.path.join(args.configDir, 'zeek.env'), 'EXTRACTED_FILE_HTTP_SERVER_ENABLE', TrueOrFalseNoQuote(fileCarveHttpServer), ), # encrypt HTTP server for extracted files EnvValue( - os.path.join(envFilesDir, 'zeek.env'), + os.path.join(args.configDir, 'zeek.env'), 'EXTRACTED_FILE_HTTP_SERVER_ENCRYPT', TrueOrFalseNoQuote(fileCarveHttpServer and (len(fileCarveHttpServeEncryptKey) > 0)), ), # key for encrypted HTTP-served extracted files (' -> '' for escaping in YAML) EnvValue( - os.path.join(envFilesDir, 'zeek.env'), + os.path.join(args.configDir, 'zeek.env'), 'EXTRACTED_FILE_HTTP_SERVER_KEY', fileCarveHttpServeEncryptKey, ), # virustotal API key EnvValue( - os.path.join(envFilesDir, 'zeek.env'), + os.path.join(args.configDir, 'zeek.env'), 'VTOT_API2_KEY', vtotApiKey, ), # file scanning via yara EnvValue( - os.path.join(envFilesDir, 'zeek.env'), + os.path.join(args.configDir, 'zeek.env'), 'EXTRACTED_FILE_ENABLE_YARA', TrueOrFalseNoQuote(yaraScan), ), # PE file scanning via capa EnvValue( - os.path.join(envFilesDir, 'zeek.env'), + os.path.join(args.configDir, 'zeek.env'), 'EXTRACTED_FILE_ENABLE_CAPA', TrueOrFalseNoQuote(capaScan), ), # file scanning via clamav EnvValue( - os.path.join(envFilesDir, 'zeek.env'), + os.path.join(args.configDir, 'zeek.env'), 'EXTRACTED_FILE_ENABLE_CLAMAV', TrueOrFalseNoQuote(clamAvScan), ), # rule updates (yara/capa via git, clamav via freshclam) EnvValue( - os.path.join(envFilesDir, 'zeek.env'), + os.path.join(args.configDir, 'zeek.env'), 'EXTRACTED_FILE_UPDATE_RULES', TrueOrFalseNoQuote(fileScanRuleUpdate), ), # live traffic analysis with Zeek EnvValue( - os.path.join(envFilesDir, 'zeek-live.env'), + os.path.join(args.configDir, 'zeek-live.env'), 'ZEEK_LIVE_CAPTURE', TrueOrFalseNoQuote(liveZeek), ), # rotated captured PCAP analysis with Zeek (not live capture) EnvValue( - os.path.join(envFilesDir, 'zeek-offline.env'), + os.path.join(args.configDir, 'zeek-offline.env'), 'ZEEK_ROTATED_PCAP', TrueOrFalseNoQuote(autoZeek and (not liveZeek)), ), # automatic uploaded pcap analysis with Zeek EnvValue( - os.path.join(envFilesDir, 'zeek-offline.env'), + os.path.join(args.configDir, 'zeek-offline.env'), 'ZEEK_AUTO_ANALYZE_PCAP_FILES', TrueOrFalseNoQuote(autoZeek), ), @@ -2623,10 +2615,21 @@ def main(): if hasattr(installer, 'install_docker_images'): success = installer.install_docker_images(imageFile) + # if .env directory is unspecified, use the default ./config directory + if args.configDir is None: + args.configDir = os.path.join(MalcolmPath, 'config') + try: + os.makedirs(args.configDir) + except OSError as exc: + if (exc.errno == errno.EEXIST) and os.path.isdir(args.configDir): + pass + else: + raise + if ( args.configOnly - or (args.configDir and os.path.isdir(args.configDir)) or (args.configFile and os.path.isfile(args.configFile)) + or (args.configDir and os.path.isdir(args.configDir)) ): if args.configFile and os.path.isfile(args.configFile): installPath = os.path.dirname(os.path.realpath(args.configFile)) From 06da99bfd38849a6136356d88494181c80f46f5b Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 12:47:49 -0600 Subject: [PATCH 050/239] Work in progress on env files --- scripts/control.py | 14 +++++++------- scripts/install.py | 2 +- scripts/malcolm_common.py | 15 +++++++++------ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index cf7404e2c..27ac1e9a4 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -772,11 +772,11 @@ def start(): # make sure the auth files exist. if we are in an interactive shell and we're # missing any of the auth files, prompt to create them now - if sys.__stdin__.isatty() and (not MalcolmAuthFilesExist()): + if sys.__stdin__.isatty() and (not MalcolmAuthFilesExist(configDir=args.configDir)): authSetup() # still missing? sorry charlie - if not MalcolmAuthFilesExist(): + if not MalcolmAuthFilesExist(configDir=args.configDir): raise Exception( 'Malcolm administrator account authentication files are missing, please run ./scripts/auth_setup to generate them' ) @@ -805,7 +805,7 @@ def start(): ]: # chmod 600 authFile os.chmod(authFile, stat.S_IRUSR | stat.S_IWUSR) - with pushd(os.path.join(MalcolmPath, 'config')): + with pushd(args.configDir): for envFile in glob.glob("*.env"): # chmod 600 envFile os.chmod(envFile, stat.S_IRUSR | stat.S_IWUSR) @@ -907,7 +907,7 @@ def authSetup(wipe=False): eprint("Passwords do not match") # get previous admin username to remove from htpasswd file if it's changed - authEnvFile = os.path.join(MalcolmPath, os.path.join('config', 'auth.env')) + authEnvFile = os.path.join(args.configDir, 'auth.env') if os.path.isfile(authEnvFile): prevAuthInfo = defaultdict(str) with open(authEnvFile, 'r') as f: @@ -1349,9 +1349,9 @@ def authSetup(wipe=False): if YesOrNo( '(Re)generate internal passwords for NetBox', - default=not os.path.isfile(os.path.join(MalcolmPath, os.path.join('config', 'netbox.env'))), + default=not os.path.isfile(os.path.join(args.configDir, 'netbox.env')), ): - with pushd(os.path.join(MalcolmPath, 'config')): + with pushd(args.configDir): netboxPwAlphabet = string.ascii_letters + string.digits + '_' netboxKeyAlphabet = string.ascii_letters + string.digits + '%@<=>?~^_-' netboxPostGresPassword = ''.join(secrets.choice(netboxPwAlphabet) for i in range(24)) @@ -1468,7 +1468,7 @@ def main(): dest='configDir', metavar='', type=str, - default='', + default=None, help="Directory containing Malcolm's .env files", ) parser.add_argument( diff --git a/scripts/install.py b/scripts/install.py index 96080c7b1..414f3816b 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -2474,7 +2474,7 @@ def main(): dest='configDir', metavar='', type=str, - default='', + default=None, help="Directory containing Malcolm's .env files", ) parser.add_argument( diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index b1e1a3793..c181c016b 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -790,18 +790,21 @@ def DotEnvDynamic(debug=False, forceInteraction=False): ################################################################################################### # do the required auth files for Malcolm exist? -def MalcolmAuthFilesExist(): +def MalcolmAuthFilesExist(configDir=None): + configDirToCheck = ( + configDir if configDir is not None and os.path.isdir(configDir) else os.path.join(MalcolmPath, 'config') + ) return ( os.path.isfile(os.path.join(MalcolmPath, os.path.join('nginx', 'htpasswd'))) and os.path.isfile(os.path.join(MalcolmPath, os.path.join('nginx', 'nginx_ldap.conf'))) and os.path.isfile(os.path.join(MalcolmPath, os.path.join('nginx', os.path.join('certs', 'cert.pem')))) and os.path.isfile(os.path.join(MalcolmPath, os.path.join('nginx', os.path.join('certs', 'key.pem')))) and os.path.isfile(os.path.join(MalcolmPath, os.path.join('htadmin', 'config.ini'))) - and os.path.isfile(os.path.join(MalcolmPath, os.path.join('config', 'netbox.env'))) - and os.path.isfile(os.path.join(MalcolmPath, os.path.join('config', 'netbox-postgres.env'))) - and os.path.isfile(os.path.join(MalcolmPath, os.path.join('config', 'netbox-redis-cache.env'))) - and os.path.isfile(os.path.join(MalcolmPath, os.path.join('config', 'netbox-redis.env'))) - and os.path.isfile(os.path.join(MalcolmPath, os.path.join('config', 'auth.env'))) + and os.path.isfile(os.path.join(configDirToCheck, 'netbox.env')) + and os.path.isfile(os.path.join(configDirToCheck, 'netbox-postgres.env')) + and os.path.isfile(os.path.join(configDirToCheck, 'netbox-redis-cache.env')) + and os.path.isfile(os.path.join(configDirToCheck, 'netbox-redis.env')) + and os.path.isfile(os.path.join(configDirToCheck, 'auth.env')) and os.path.isfile(os.path.join(MalcolmPath, '.opensearch.primary.curlrc')) ) From c2a34154b79edafd541a9813137b516674f8f9d0 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Mon, 27 Mar 2023 13:19:54 -0600 Subject: [PATCH 051/239] Dashboards enable --- kubernetes/03-dashboards.yml | 2 -- kubernetes/08-dashboards-helper.yml | 2 -- kubernetes/opensearch.env | 6 +++--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/kubernetes/03-dashboards.yml b/kubernetes/03-dashboards.yml index efca04a0e..869fd6b41 100644 --- a/kubernetes/03-dashboards.yml +++ b/kubernetes/03-dashboards.yml @@ -43,8 +43,6 @@ spec: - configMapRef: name: opensearch-env env: - - name: DASHBOARDS_DISABLED - value: "true" - name: VIRTUAL_HOST value: "dashboards.malcolm.local" volumeMounts: diff --git a/kubernetes/08-dashboards-helper.yml b/kubernetes/08-dashboards-helper.yml index 043ece66c..7ffc05dba 100644 --- a/kubernetes/08-dashboards-helper.yml +++ b/kubernetes/08-dashboards-helper.yml @@ -45,8 +45,6 @@ spec: - configMapRef: name: dashboards-helper-env env: - - name: DASHBOARDS_HELPER_DISABLED - value: "true" - name: VIRTUAL_HOST value: "dashboards-helper.malcolm.local" volumeMounts: diff --git a/kubernetes/opensearch.env b/kubernetes/opensearch.env index bddeed0db..9685d2d9e 100644 --- a/kubernetes/opensearch.env +++ b/kubernetes/opensearch.env @@ -3,12 +3,12 @@ # primary data store. Set to 'false' if you're connecting to another OpenSearch # cluster, in which case the other environment variables in this section must also # be set with the connection parameters. -OPENSEARCH_LOCAL=true +OPENSEARCH_LOCAL=false # URL for connecting to OpenSearch instance. When using Malcolm's internal instance # of OpenSearch (i.e., OPENSEARCH_LOCAL is 'true') this should be # 'http://opensearch:9200', otherwise specify the primary remote instance URL # in the format 'protocol://host:port'. -OPENSEARCH_URL=http://opensearch:9200 +OPENSEARCH_URL=http://10.9.0.226:9200 # Used when OPENSEARCH_LOCAL is 'false', the cURL-formatted config file contains login # credentials for the primary OpenSearch instance. It can be generated for you by the # ./scripts/auth_setup script. The notable parameters expected from this file would be @@ -16,7 +16,7 @@ OPENSEARCH_URL=http://opensearch:9200 # setting below is 'false'). See cURL config file format at # https://everything.curl.dev/cmdline/configfile. This file is bind mounted locally # from .opensearch.primary.curlrc as /var/local/curlrc/opensearch.primary.curlrc -OPENSEARCH_CREDS_CONFIG_FILE=/var/local/curlrc/opensearch.primary.curlrc +OPENSEARCH_CREDS_CONFIG_FILE=/var/local/curlrc/.opensearch.primary.curlrc # Whether or not connections to the primary remote OpenSearch instance require full # TLS certificate validation for the connection (this may fail if using self-signed # certificates). From 513eab507dbd6e6f01f401bfa19faf05b6e8c659 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 14:46:36 -0600 Subject: [PATCH 052/239] remove debug .env files --- netbox/env/netbox.env | 56 -------------------------------------- netbox/env/postgres.env | 3 -- netbox/env/redis-cache.env | 1 - netbox/env/redis.env | 1 - 4 files changed, 61 deletions(-) delete mode 100644 netbox/env/netbox.env delete mode 100644 netbox/env/postgres.env delete mode 100644 netbox/env/redis-cache.env delete mode 100644 netbox/env/redis.env diff --git a/netbox/env/netbox.env b/netbox/env/netbox.env deleted file mode 100644 index aa7ed0e27..000000000 --- a/netbox/env/netbox.env +++ /dev/null @@ -1,56 +0,0 @@ -CORS_ORIGIN_ALLOW_ALL=True -CSRF_TRUSTED_ORIGINS=http://* https://* -BASE_PATH=netbox -REMOTE_AUTH_ENABLED=True -REMOTE_AUTH_BACKEND=netbox.authentication.RemoteUserBackend -REMOTE_AUTH_HEADER=HTTP_X_REMOTE_AUTH -REMOTE_AUTH_AUTO_CREATE_USER=True -REMOTE_AUTH_DEFAULT_GROUPS=standard -REMOTE_AUTH_DEFAULT_PERMISSIONS=standard_permission -REMOTE_AUTH_STAFF_GROUPS=administrator -REMOTE_AUTH_STAFF_USERS= -REMOTE_AUTH_SUPERUSER_GROUPS=administrator -REMOTE_AUTH_SUPERUSERS= -EXEMPT_VIEW_PERMISSIONS=* -DB_HOST=netbox-postgres -DB_NAME=netbox -DB_PASSWORD=lbReO9W70qHnumx1WfB9dO6L -DB_USER=netbox -EMAIL_FROM=netbox@bar.com -EMAIL_PASSWORD= -EMAIL_PORT=25 -EMAIL_SERVER=localhost -EMAIL_SSL_CERTFILE= -EMAIL_SSL_KEYFILE= -EMAIL_TIMEOUT=5 -EMAIL_USERNAME=netbox -# EMAIL_USE_SSL and EMAIL_USE_TLS are mutually exclusive, i.e. they can't both be `true`! -EMAIL_USE_SSL=false -EMAIL_USE_TLS=false -GRAPHQL_ENABLED=true -HOUSEKEEPING_INTERVAL=86400 -MAX_PAGE_SIZE=1000 -MEDIA_ROOT=/opt/netbox/netbox/media -METRICS_ENABLED=false -NAPALM_PASSWORD= -NAPALM_TIMEOUT=10 -NAPALM_USERNAME= -REDIS_CACHE_DATABASE=1 -REDIS_CACHE_HOST=netbox-redis-cache -REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY=false -REDIS_CACHE_PASSWORD=HHzhpfgiK11nAxCiZ6xSIebx -REDIS_CACHE_SSL=false -REDIS_DATABASE=0 -REDIS_HOST=netbox-redis -REDIS_INSECURE_SKIP_TLS_VERIFY=false -REDIS_PASSWORD=S2irBePmJ7hGzgzVWRVW0wNe -REDIS_SSL=false -RELEASE_CHECK_URL=https://api.github.com/repos/netbox-community/netbox/releases -SECRET_KEY=4zUze@9-AualT^KZVf40j@0YPj6?VHHTyroj52_E0UVWM1Cih% -SKIP_STARTUP_SCRIPTS=true -SKIP_SUPERUSER=false -SUPERUSER_API_TOKEN=vZMCRNIOejwttF6WFyKycngjE6BlmYmctcKpTg6m -SUPERUSER_EMAIL=admin@example.com -SUPERUSER_NAME=admin -SUPERUSER_PASSWORD=xReZAuYf6oiLeK6Wl8sMlJOa -WEBHOOKS_ENABLED=true diff --git a/netbox/env/postgres.env b/netbox/env/postgres.env deleted file mode 100644 index 7333668db..000000000 --- a/netbox/env/postgres.env +++ /dev/null @@ -1,3 +0,0 @@ -POSTGRES_DB=netbox -POSTGRES_PASSWORD=lbReO9W70qHnumx1WfB9dO6L -POSTGRES_USER=netbox diff --git a/netbox/env/redis-cache.env b/netbox/env/redis-cache.env deleted file mode 100644 index 2cb7292f7..000000000 --- a/netbox/env/redis-cache.env +++ /dev/null @@ -1 +0,0 @@ -REDIS_PASSWORD=HHzhpfgiK11nAxCiZ6xSIebx diff --git a/netbox/env/redis.env b/netbox/env/redis.env deleted file mode 100644 index 7d5fd58d4..000000000 --- a/netbox/env/redis.env +++ /dev/null @@ -1 +0,0 @@ -REDIS_PASSWORD=S2irBePmJ7hGzgzVWRVW0wNe From a2641f719800cc50c0651ff2613f65cf4a73077c Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 16:03:08 -0600 Subject: [PATCH 053/239] enable pcap-monitor container --- kubernetes/04-upload.yml | 13 +++++++++++-- kubernetes/05-pcap-monitor.yml | 20 +++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 89e038e1c..edf0a986b 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -50,10 +50,19 @@ spec: - configMapRef: name: upload-env env: - - name: UPLOAD_DISABLED - value: "false" - name: VIRTUAL_HOST value: "upload.malcolm.local" + livenessProbe: + exec: + command: + - wget + - -qO- + - http://localhost + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: upload-var-local-catrust-volume diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml index 88d407112..4db087b51 100644 --- a/kubernetes/05-pcap-monitor.yml +++ b/kubernetes/05-pcap-monitor.yml @@ -44,9 +44,18 @@ spec: name: opensearch-env - configMapRef: name: upload-common-env - env: - - name: PCAPMON_DISABLED - value: "true" + livenessProbe: + exec: + command: + - supervisorctl + - status + - watch-upload + - pcap-publisher + initialDelaySeconds: 90 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: pcapmon-var-local-catrust-volume @@ -54,6 +63,8 @@ spec: name: pcapmon-opensearch-curlrc-volume - mountPath: "/pcap" name: pcapmon-pcap-volume + - mountPath: "/zeek" + name: pcapmon-zeek-volume volumes: - name: pcapmon-var-local-catrust-volume configMap: @@ -64,3 +75,6 @@ spec: - name: pcapmon-pcap-volume persistentVolumeClaim: claimName: pcap-claim + - name: pcapmon-zeek-volume + persistentVolumeClaim: + claimName: zeek-claim \ No newline at end of file From 82db12442a6e2ec48853b396228b7c20ef99fd0f Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 27 Mar 2023 16:30:54 -0600 Subject: [PATCH 054/239] enable file-monitor --- kubernetes/04-upload.yml | 2 +- kubernetes/11-file-monitor.yml | 28 ++++++++++++++++++++++++++-- kubernetes/20-pcap-capture.yml | 2 +- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index edf0a986b..267eb5abe 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -68,7 +68,7 @@ spec: name: upload-var-local-catrust-volume - mountPath: "/var/www/upload/server/php/chroot/files" name: upload-pcap-volume - subPath: upload + subPath: "upload" volumes: - name: upload-var-local-catrust-volume configMap: diff --git a/kubernetes/11-file-monitor.yml b/kubernetes/11-file-monitor.yml index 6fb64f324..3b6064b4f 100644 --- a/kubernetes/11-file-monitor.yml +++ b/kubernetes/11-file-monitor.yml @@ -43,14 +43,38 @@ spec: - configMapRef: name: zeek-env env: - - name: FILE_MONITOR_DISABLED - value: "true" - name: VIRTUAL_HOST value: "file-monitor.malcolm.local" + livenessProbe: + exec: + command: + - supervisorctl + - status + - watcher + - logger + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: file-monitor-var-local-catrust-volume + - mountPath: "/zeek/extract_files" + name: file-monitor-zeek-volume + subPath: "extract_files" + - mountPath: "/zeek/logs" + name: file-monitor-zeek-volume + subPath: "current" + - mountPath: "/yara-rules/custom/configmap" + name: file-monitor-yara-rules-custom-volume volumes: - name: file-monitor-var-local-catrust-volume configMap: name: var-local-catrust + - name: file-monitor-zeek-volume + persistentVolumeClaim: + claimName: zeek-claim + - name: file-monitor-yara-rules-custom-volume + configMap: + name: yara-rules \ No newline at end of file diff --git a/kubernetes/20-pcap-capture.yml b/kubernetes/20-pcap-capture.yml index ca4bd8b46..30dda3815 100644 --- a/kubernetes/20-pcap-capture.yml +++ b/kubernetes/20-pcap-capture.yml @@ -35,7 +35,7 @@ spec: name: capture-var-local-catrust-volume - mountPath: "/pcap" name: capture-pcap-volume - subPath: upload + subPath: "upload" volumes: - name: capture-var-local-catrust-volume configMap: From d6964208d431406d810a71a2b99696468c459672 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 28 Mar 2023 08:24:22 -0600 Subject: [PATCH 055/239] work in progress on services (API, logstash); add capabilities --- kubernetes/02-opensearch.yml | 4 ++++ kubernetes/07-api.yml | 16 ++++++++++++++-- kubernetes/09-zeek.yml | 7 +++++++ kubernetes/10-suricata.yml | 7 +++++++ kubernetes/13-logstash.yml | 23 ++++++++++++++++++----- kubernetes/20-pcap-capture.yml | 7 +++++++ kubernetes/21-zeek-live.yml | 7 +++++++ kubernetes/22-suricata-live.yml | 7 +++++++ 8 files changed, 71 insertions(+), 7 deletions(-) diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index 4515bedbb..c2e8610dc 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -33,6 +33,10 @@ spec: imagePullPolicy: Always stdin: false tty: true + securityContext: + capabilities: + add: + - IPC_LOCK ports: - containerPort: 9200 envFrom: diff --git a/kubernetes/07-api.yml b/kubernetes/07-api.yml index 4d40587c4..6b494282c 100644 --- a/kubernetes/07-api.yml +++ b/kubernetes/07-api.yml @@ -33,6 +33,8 @@ spec: imagePullPolicy: Always stdin: false tty: true + command: ["gunicorn"] + args: ["--bind", "0:5000", "manage:app"] ports: - containerPort: 5000 envFrom: @@ -43,10 +45,20 @@ spec: - configMapRef: name: opensearch-env env: - - name: API_DISABLED - value: "true" - name: VIRTUAL_HOST value: "api.malcolm.local" + livenessProbe: + exec: + command: + - curl + - --silent + - --fail + - http://localhost:5000/mapi/ping + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: api-var-local-catrust-volume diff --git a/kubernetes/09-zeek.yml b/kubernetes/09-zeek.yml index bd10573f1..ffecbfde3 100644 --- a/kubernetes/09-zeek.yml +++ b/kubernetes/09-zeek.yml @@ -20,6 +20,13 @@ spec: imagePullPolicy: Always stdin: false tty: true + securityContext: + capabilities: + add: + - IPC_LOCK + - NET_ADMIN + - NET_RAW + - SYS_ADMIN envFrom: - configMapRef: name: process-env diff --git a/kubernetes/10-suricata.yml b/kubernetes/10-suricata.yml index c289ab9e0..02ecefad8 100644 --- a/kubernetes/10-suricata.yml +++ b/kubernetes/10-suricata.yml @@ -20,6 +20,13 @@ spec: imagePullPolicy: Always stdin: false tty: true + securityContext: + capabilities: + add: + - IPC_LOCK + - NET_ADMIN + - NET_RAW + - SYS_ADMIN envFrom: - configMapRef: name: process-env diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index cc0814c76..d90ae2965 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -37,6 +37,10 @@ spec: imagePullPolicy: Always stdin: false tty: true + securityContext: + capabilities: + add: + - IPC_LOCK ports: - containerPort: 5044 - containerPort: 9600 @@ -51,15 +55,24 @@ spec: name: netbox-common-env - configMapRef: name: netbox-env - - configMapRef: - name: logstash-env - configMapRef: name: beats-common-env - configMapRef: name: lookup-common-env - env: - - name: LOGSTASH_DISABLED - value: "true" + - configMapRef: + name: logstash-env + livenessProbe: + exec: + command: + - curl + - --silent + - --fail + - http://localhost:9600 + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: logstash-var-local-catrust-volume diff --git a/kubernetes/20-pcap-capture.yml b/kubernetes/20-pcap-capture.yml index 30dda3815..f92a8d35b 100644 --- a/kubernetes/20-pcap-capture.yml +++ b/kubernetes/20-pcap-capture.yml @@ -20,6 +20,13 @@ spec: imagePullPolicy: Always stdin: false tty: true + securityContext: + capabilities: + add: + - IPC_LOCK + - NET_ADMIN + - NET_RAW + - SYS_ADMIN envFrom: - configMapRef: name: process-env diff --git a/kubernetes/21-zeek-live.yml b/kubernetes/21-zeek-live.yml index d241443df..54746ba24 100644 --- a/kubernetes/21-zeek-live.yml +++ b/kubernetes/21-zeek-live.yml @@ -20,6 +20,13 @@ spec: imagePullPolicy: Always stdin: false tty: true + securityContext: + capabilities: + add: + - IPC_LOCK + - NET_ADMIN + - NET_RAW + - SYS_ADMIN envFrom: - configMapRef: name: process-env diff --git a/kubernetes/22-suricata-live.yml b/kubernetes/22-suricata-live.yml index 045c8b405..d92f9133d 100644 --- a/kubernetes/22-suricata-live.yml +++ b/kubernetes/22-suricata-live.yml @@ -20,6 +20,13 @@ spec: imagePullPolicy: Always stdin: false tty: true + securityContext: + capabilities: + add: + - IPC_LOCK + - NET_ADMIN + - NET_RAW + - SYS_ADMIN envFrom: - configMapRef: name: process-env From d07a3fc6d4edd26469ac2d0b8b45fbc89354e83d Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 28 Mar 2023 08:31:18 -0600 Subject: [PATCH 056/239] enable filebeat --- kubernetes/12-filebeat.yml | 29 ++++++++++++++++++++++++----- kubernetes/13-logstash.yml | 2 +- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml index 6f6f4ef06..42d61ee55 100644 --- a/kubernetes/12-filebeat.yml +++ b/kubernetes/12-filebeat.yml @@ -44,15 +44,23 @@ spec: name: nginx-env - configMapRef: name: opensearch-env - - configMapRef: - name: filebeat-env - configMapRef: name: upload-common-env - configMapRef: name: beats-common-env - env: - - name: FILEBEAT_DISABLED - value: "true" + - configMapRef: + name: filebeat-env + livenessProbe: + exec: + command: + - supervisorctl + - status + - filebeat + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: filebeat-var-local-catrust-volume @@ -60,6 +68,11 @@ spec: name: filebeat-opensearch-curlrc-volume - mountPath: /certs/configmap name: filebeat-certs-volume + - mountPath: "/zeek" + name: filebeat-zeek-volume + - mountPath: "/suricata" + name: filebeat-suricata-volume + # TODO: live nginx-logs from nginx-proxy container volumes: - name: filebeat-var-local-catrust-volume configMap: @@ -70,3 +83,9 @@ spec: - name: filebeat-certs-volume configMap: name: filebeat-certs + - name: filebeat-zeek-volume + persistentVolumeClaim: + claimName: zeek-claim + - name: filebeat-suricata-volume + persistentVolumeClaim: + claimName: suricata-claim diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index d90ae2965..602f10c71 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -68,7 +68,7 @@ spec: - --silent - --fail - http://localhost:9600 - initialDelaySeconds: 60 + initialDelaySeconds: 600 periodSeconds: 30 timeoutSeconds: 15 successThreshold: 1 From 4eeb2152475f1b0b4dafd4e43d096b81dec8af42 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 28 Mar 2023 09:04:34 -0600 Subject: [PATCH 057/239] enable zeek and suricata --- kubernetes/09-zeek.yml | 45 ++++++++++++++++++++++++++++---------- kubernetes/10-suricata.yml | 40 +++++++++++++++++++++++---------- 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/kubernetes/09-zeek.yml b/kubernetes/09-zeek.yml index ffecbfde3..22f00a1e8 100644 --- a/kubernetes/09-zeek.yml +++ b/kubernetes/09-zeek.yml @@ -2,20 +2,20 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: zeek-deployment + name: zeek-offline-deployment namespace: malcolm spec: selector: matchLabels: - name: zeek-deployment + name: zeek-offline-deployment replicas: 1 template: metadata: labels: - name: zeek-deployment + name: zeek-offline-deployment spec: containers: - - name: zeek-container + - name: zeek-offline-container image: ghcr.io/idaholab/malcolm/zeek:kubernetes imagePullPolicy: Always stdin: false @@ -38,18 +38,41 @@ spec: name: zeek-env - configMapRef: name: zeek-offline-env - env: - - name: ZEEK_DISABLED - value: "true" + livenessProbe: + exec: + command: + - supervisorctl + - status + - pcap-zeek + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: zeek-var-local-catrust-volume + name: zeek-offline-var-local-catrust-volume - mountPath: "/pcap" - name: zeek-pcap-volume + name: zeek-offline-pcap-volume + - mountPath: "/zeek/extract_files" + name: zeek-offline-zeek-volume + subPath: "extract_files" + - mountPath: "/zeek/upload" + name: zeek-offline-zeek-volume + subPath: "upload" + - mountPath: "/opt/zeek/share/zeek/site/intel" + name: zeek-offline-zeek-intel + subPath: "zeek/intel" volumes: - - name: zeek-var-local-catrust-volume + - name: zeek-offline-var-local-catrust-volume configMap: name: var-local-catrust - - name: zeek-pcap-volume + - name: zeek-offline-pcap-volume persistentVolumeClaim: claimName: pcap-claim + - name: zeek-offline-zeek-volume + persistentVolumeClaim: + claimName: zeek-claim + - name: zeek-offline-zeek-intel + persistentVolumeClaim: + claimName: config-claim diff --git a/kubernetes/10-suricata.yml b/kubernetes/10-suricata.yml index 02ecefad8..45b21d013 100644 --- a/kubernetes/10-suricata.yml +++ b/kubernetes/10-suricata.yml @@ -2,20 +2,20 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: suricata-deployment + name: suricata-offline-deployment namespace: malcolm spec: selector: matchLabels: - name: suricata-deployment + name: suricata-offline-deployment replicas: 1 template: metadata: labels: - name: suricata-deployment + name: suricata-offline-deployment spec: containers: - - name: suricata-container + - name: suricata-offline-container image: ghcr.io/idaholab/malcolm/suricata:kubernetes imagePullPolicy: Always stdin: false @@ -38,18 +38,36 @@ spec: name: suricata-env - configMapRef: name: suricata-offline-env - env: - - name: SURICATA_DISABLED - value: "true" + livenessProbe: + exec: + command: + - supervisorctl + - status + - pcap-suricata + initialDelaySeconds: 120 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: suricata-var-local-catrust-volume + name: suricata-offline-var-local-catrust-volume - mountPath: "/data/pcap" - name: suricata-pcap-volume + name: suricata-offline-pcap-volume + - mountPath: "/var/log/suricata" + name: suricata-offline-suricata-logs-volume + - mountPath: "/opt/suricata/rules/configmap" + name: suricata-offline-custom-rules-volume volumes: - - name: suricata-var-local-catrust-volume + - name: suricata-offline-var-local-catrust-volume configMap: name: var-local-catrust - - name: suricata-pcap-volume + - name: suricata-offline-pcap-volume persistentVolumeClaim: claimName: pcap-claim + - name: suricata-offline-suricata-logs-volume + persistentVolumeClaim: + claimName: suricata-claim + - name: suricata-offline-custom-rules-volume + configMap: + name: suricata-rules \ No newline at end of file From be65cf436a7edf2a7a6e1273653cc4a3b88c4d8c Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 28 Mar 2023 09:39:46 -0600 Subject: [PATCH 058/239] work in progress for pcap-monitor --- Dockerfiles/pcap-monitor.Dockerfile | 2 +- docker-compose-standalone.yml | 2 +- docker-compose.yml | 2 +- kubernetes/05-pcap-monitor.yml | 18 +++++++++--------- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Dockerfiles/pcap-monitor.Dockerfile b/Dockerfiles/pcap-monitor.Dockerfile index 51f5d1183..8947fa2db 100644 --- a/Dockerfiles/pcap-monitor.Dockerfile +++ b/Dockerfiles/pcap-monitor.Dockerfile @@ -79,7 +79,7 @@ ENTRYPOINT ["/usr/bin/tini", \ "--", \ "/usr/local/bin/docker-uid-gid-setup.sh", \ "/usr/local/bin/service_check_passthrough.sh", \ - "-s", "pcapmon"] + "-s", "pcap-monitor"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-u", "root", "-n"] diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index b2b4942f0..a297cb18f 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -378,7 +378,7 @@ services: restart: "no" stdin_open: false tty: true - hostname: pcapmon + hostname: pcap-monitor networks: - default env_file: diff --git a/docker-compose.yml b/docker-compose.yml index 40f83d2b9..a3060b43e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -426,7 +426,7 @@ services: restart: "no" stdin_open: false tty: true - hostname: pcapmon + hostname: pcap-monitor networks: - default env_file: diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml index 4db087b51..b3838dd8a 100644 --- a/kubernetes/05-pcap-monitor.yml +++ b/kubernetes/05-pcap-monitor.yml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: - name: pcapmon + name: pcap-monitor namespace: malcolm spec: ports: @@ -58,23 +58,23 @@ spec: failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap - name: pcapmon-var-local-catrust-volume + name: pcap-monitor-var-local-catrust-volume - mountPath: /var/local/curlrc/configmap - name: pcapmon-opensearch-curlrc-volume + name: pcap-monitor-opensearch-curlrc-volume - mountPath: "/pcap" - name: pcapmon-pcap-volume + name: pcap-monitor-pcap-volume - mountPath: "/zeek" - name: pcapmon-zeek-volume + name: pcap-monitor-zeek-volume volumes: - - name: pcapmon-var-local-catrust-volume + - name: pcap-monitor-var-local-catrust-volume configMap: name: var-local-catrust - - name: pcapmon-opensearch-curlrc-volume + - name: pcap-monitor-opensearch-curlrc-volume configMap: name: opensearch-curlrc - - name: pcapmon-pcap-volume + - name: pcap-monitor-pcap-volume persistentVolumeClaim: claimName: pcap-claim - - name: pcapmon-zeek-volume + - name: pcap-monitor-zeek-volume persistentVolumeClaim: claimName: zeek-claim \ No newline at end of file From 87ccea9b4837ed33939b5f5000595173383539c8 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 28 Mar 2023 12:34:20 -0600 Subject: [PATCH 059/239] enable arkime --- kubernetes/06-arkime.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index 23c1db584..76a4d62a2 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -49,10 +49,21 @@ spec: - configMapRef: name: arkime-env env: - - name: ARKIME_DISABLED - value: "true" - name: VIRTUAL_HOST value: "arkime.malcolm.local" + livenessProbe: + exec: + command: + - curl + - --insecure + - --silent + - --fail + - https://localhost:8005/_ns_/nstest.html + initialDelaySeconds: 210 + periodSeconds: 90 + timeoutSeconds: 30 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: arkime-var-local-catrust-volume From 2ac6c0de62a2590a469efd5d51038d5252335fcc Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 28 Mar 2023 13:10:28 -0600 Subject: [PATCH 060/239] enable netbox --- kubernetes/15-netbox-redis.yml | 9 ++++++++ kubernetes/16-netbox-redis-cache.yml | 3 +++ kubernetes/17-netbox-postgres.yml | 7 ++++++ kubernetes/18-netbox.yml | 33 ++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index 20deda4ea..7c318c98b 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -33,6 +33,8 @@ spec: imagePullPolicy: Always stdin: false tty: true + command: ["sh"] + args: ["-c", "redis-server --appendonly yes --requirepass $(REDIS_PASSWORD)"] ports: - containerPort: 6379 envFrom: @@ -47,10 +49,17 @@ spec: env: - name: VIRTUAL_HOST value: "netbox-redis.malcolm.local" + # TODO: livenessProbe volumeMounts: - mountPath: /var/local/ca-trust/configmap name: netbox-redis-var-local-catrust-volume + - mountPath: /data + name: netbox-redis-volume + subPath: netbox/redis volumes: - name: netbox-redis-var-local-catrust-volume configMap: name: var-local-catrust + - name: netbox-redis-volume + persistentVolumeClaim: + claimName: config-claim \ No newline at end of file diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index fa165fd3f..28225810b 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -33,6 +33,8 @@ spec: imagePullPolicy: Always stdin: false tty: true + command: ["sh"] + args: ["-c", "redis-server --requirepass $(REDIS_PASSWORD)"] ports: - containerPort: 6379 envFrom: @@ -47,6 +49,7 @@ spec: env: - name: VIRTUAL_HOST value: "netbox-redis-cache.malcolm.local" + # TODO: livenessProbe volumeMounts: - mountPath: /var/local/ca-trust/configmap name: netbox-redis-cache-var-local-catrust-volume diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index 4bb1121c2..c2c3cb870 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -47,10 +47,17 @@ spec: env: - name: VIRTUAL_HOST value: "netbox-postgres.malcolm.local" + # TODO: livenessProbe volumeMounts: - mountPath: /var/local/ca-trust/configmap name: netbox-postgres-var-local-catrust-volume + - mountPath: /var/lib/postgresql/data + name: netbox-postgres-volume + subPath: netbox/postgres volumes: - name: netbox-postgres-var-local-catrust-volume configMap: name: var-local-catrust + - name: netbox-postgres-volume + persistentVolumeClaim: + claimName: config-claim \ No newline at end of file diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index 090242d20..a8c4d4f69 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -56,11 +56,31 @@ spec: env: - name: VIRTUAL_HOST value: "netbox.malcolm.local" + livenessProbe: + exec: + command: + - curl + - --silent + - http://localhost:8080/netbox/api + initialDelaySeconds: 120 + periodSeconds: 60 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: netbox-var-local-catrust-volume - mountPath: /usr/local/share/configmap name: netbox-netmap-json-volume + - mountPath: /etc/netbox/config/configmap + name: netbox-config-volume + - mountPath: /etc/netbox/reports/configmap + name: netbox-reports-volume + - mountPath: /etc/netbox/scripts/configmap + name: netbox-scripts-volume + - mountPath: /opt/netbox/netbox/media + name: netbox-media-volume + subPath: netbox/media volumes: - name: netbox-var-local-catrust-volume configMap: @@ -68,3 +88,16 @@ spec: - name: netbox-netmap-json-volume configMap: name: netbox-netmap-json + - name: netbox-config-volume + configMap: + name: netbox-config + - name: netbox-reports-volume + configMap: + name: netbox-reports + - name: netbox-scripts-volume + configMap: + name: netbox-scripts + - name: netbox-media-volume + persistentVolumeClaim: + claimName: config-claim + From bc748bef869b7f396b513cd02f92c5d8d43f7478 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 28 Mar 2023 13:22:30 -0600 Subject: [PATCH 061/239] tweak liveness probes --- kubernetes/15-netbox-redis.yml | 12 +++++++++++- kubernetes/16-netbox-redis-cache.yml | 12 +++++++++++- kubernetes/17-netbox-postgres.yml | 12 +++++++++++- kubernetes/18-netbox.yml | 2 +- kubernetes/23-freq.yml | 12 ++++++++++++ 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index 7c318c98b..f3d1c3532 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -49,7 +49,17 @@ spec: env: - name: VIRTUAL_HOST value: "netbox-redis.malcolm.local" - # TODO: livenessProbe + livenessProbe: + exec: + command: + - bash + - -c + - "[[ $(NETBOX_REDIS_DISABLED) == 'true' ]] || ( pidof redis-server || exit 1 )" + initialDelaySeconds: 45 + periodSeconds: 60 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: netbox-redis-var-local-catrust-volume diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index 28225810b..f12b8400c 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -49,7 +49,17 @@ spec: env: - name: VIRTUAL_HOST value: "netbox-redis-cache.malcolm.local" - # TODO: livenessProbe + livenessProbe: + exec: + command: + - bash + - -c + - "[[ $(NETBOX_REDIS_DISABLED) == 'true' ]] || ( pidof redis-server || exit 1 )" + initialDelaySeconds: 45 + periodSeconds: 60 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: netbox-redis-cache-var-local-catrust-volume diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index c2c3cb870..b6ceefb3f 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -47,7 +47,17 @@ spec: env: - name: VIRTUAL_HOST value: "netbox-postgres.malcolm.local" - # TODO: livenessProbe + livenessProbe: + exec: + command: + - bash + - -c + - "[[ $(NETBOX_POSTGRES_DISABLED) == 'true' ]] || pg_isready -d $(POSTGRES_DB) -U $(POSTGRES_USER)" + initialDelaySeconds: 45 + periodSeconds: 60 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: netbox-postgres-var-local-catrust-volume diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index a8c4d4f69..2add598a0 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -62,7 +62,7 @@ spec: - curl - --silent - http://localhost:8080/netbox/api - initialDelaySeconds: 120 + initialDelaySeconds: 300 periodSeconds: 60 timeoutSeconds: 15 successThreshold: 1 diff --git a/kubernetes/23-freq.yml b/kubernetes/23-freq.yml index 4f33850a7..db4c12fe9 100644 --- a/kubernetes/23-freq.yml +++ b/kubernetes/23-freq.yml @@ -45,6 +45,18 @@ spec: env: - name: VIRTUAL_HOST value: "freq.malcolm.local" + livenessProbe: + exec: + command: + - curl + - --silent + - --fail + - http://localhost:10004 + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: freq-var-local-catrust-volume From 954c94a19c913479f03e219d65c218840a3dbd85 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 28 Mar 2023 13:29:24 -0600 Subject: [PATCH 062/239] tweaks to liveness probes --- kubernetes/15-netbox-redis.yml | 4 ++-- kubernetes/16-netbox-redis-cache.yml | 4 ++-- kubernetes/17-netbox-postgres.yml | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index f3d1c3532..a7e49557c 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -52,9 +52,9 @@ spec: livenessProbe: exec: command: - - bash + - sh - -c - - "[[ $(NETBOX_REDIS_DISABLED) == 'true' ]] || ( pidof redis-server || exit 1 )" + - "pidof redis-server || pidof goStatic || exit 1" initialDelaySeconds: 45 periodSeconds: 60 timeoutSeconds: 15 diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index f12b8400c..2fd51376b 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -52,9 +52,9 @@ spec: livenessProbe: exec: command: - - bash + - sh - -c - - "[[ $(NETBOX_REDIS_DISABLED) == 'true' ]] || ( pidof redis-server || exit 1 )" + - "pidof redis-server || pidof goStatic || exit 1" initialDelaySeconds: 45 periodSeconds: 60 timeoutSeconds: 15 diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index b6ceefb3f..b0446bb2a 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -50,10 +50,10 @@ spec: livenessProbe: exec: command: - - bash + - sh - -c - - "[[ $(NETBOX_POSTGRES_DISABLED) == 'true' ]] || pg_isready -d $(POSTGRES_DB) -U $(POSTGRES_USER)" - initialDelaySeconds: 45 + - "pg_isready || pidof goStatic || exit 1" + initialDelaySeconds: 90 periodSeconds: 60 timeoutSeconds: 15 successThreshold: 1 From a9186c0d301e170c17b11154963d32fa13e2c365 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Tue, 28 Mar 2023 13:02:24 -0600 Subject: [PATCH 063/239] Add dashboards health checks --- kubernetes/03-dashboards.yml | 12 ++++++++++++ kubernetes/08-dashboards-helper.yml | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/kubernetes/03-dashboards.yml b/kubernetes/03-dashboards.yml index 869fd6b41..31ebff9dd 100644 --- a/kubernetes/03-dashboards.yml +++ b/kubernetes/03-dashboards.yml @@ -45,6 +45,18 @@ spec: env: - name: VIRTUAL_HOST value: "dashboards.malcolm.local" + livenessProbe: + exec: + command: + - curl + - --silent + - --fail + - http://localhost:5601/dashboards/api/status + initialDelaySeconds: 210 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: dashboards-var-local-catrust-volume diff --git a/kubernetes/08-dashboards-helper.yml b/kubernetes/08-dashboards-helper.yml index 7ffc05dba..6e1a5981b 100644 --- a/kubernetes/08-dashboards-helper.yml +++ b/kubernetes/08-dashboards-helper.yml @@ -47,6 +47,18 @@ spec: env: - name: VIRTUAL_HOST value: "dashboards-helper.malcolm.local" + livenessProbe: + exec: + command: + - supervisorctl + - status + - cron + - maps + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 10 volumeMounts: - mountPath: /var/local/ca-trust/configmap name: dashboards-helper-var-local-catrust-volume From a542cfea62ca2ab6dbd7570da0de421859ab5360 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 28 Mar 2023 15:32:50 -0600 Subject: [PATCH 064/239] replace curl/wget liveness checks with httpGet --- kubernetes/03-dashboards.yml | 10 ++++------ kubernetes/04-upload.yml | 9 ++++----- kubernetes/06-arkime.yml | 11 ++++------- kubernetes/07-api.yml | 10 ++++------ kubernetes/13-logstash.yml | 10 ++++------ kubernetes/18-netbox.yml | 9 ++++----- kubernetes/23-freq.yml | 10 ++++------ kubernetes/99-nginx-proxy.yml | 11 +++++------ nginx/nginx.conf | 11 +++++++++++ nginx/nginx_readonly.conf | 11 +++++++++++ 10 files changed, 55 insertions(+), 47 deletions(-) diff --git a/kubernetes/03-dashboards.yml b/kubernetes/03-dashboards.yml index 31ebff9dd..b6b55e27c 100644 --- a/kubernetes/03-dashboards.yml +++ b/kubernetes/03-dashboards.yml @@ -46,12 +46,10 @@ spec: - name: VIRTUAL_HOST value: "dashboards.malcolm.local" livenessProbe: - exec: - command: - - curl - - --silent - - --fail - - http://localhost:5601/dashboards/api/status + httpGet: + path: /dashboards/api/status + port: 5601 + scheme: HTTP initialDelaySeconds: 210 periodSeconds: 30 timeoutSeconds: 15 diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 267eb5abe..be7673b05 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -53,11 +53,10 @@ spec: - name: VIRTUAL_HOST value: "upload.malcolm.local" livenessProbe: - exec: - command: - - wget - - -qO- - - http://localhost + httpGet: + path: / + port: 80 + scheme: HTTP initialDelaySeconds: 60 periodSeconds: 30 timeoutSeconds: 15 diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index 76a4d62a2..e5a33de8f 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -52,13 +52,10 @@ spec: - name: VIRTUAL_HOST value: "arkime.malcolm.local" livenessProbe: - exec: - command: - - curl - - --insecure - - --silent - - --fail - - https://localhost:8005/_ns_/nstest.html + httpGet: + path: /_ns_/nstest.html + port: 8005 + scheme: HTTPS initialDelaySeconds: 210 periodSeconds: 90 timeoutSeconds: 30 diff --git a/kubernetes/07-api.yml b/kubernetes/07-api.yml index 6b494282c..d23a7a1af 100644 --- a/kubernetes/07-api.yml +++ b/kubernetes/07-api.yml @@ -48,12 +48,10 @@ spec: - name: VIRTUAL_HOST value: "api.malcolm.local" livenessProbe: - exec: - command: - - curl - - --silent - - --fail - - http://localhost:5000/mapi/ping + httpGet: + path: /mapi/ping + port: 5000 + scheme: HTTP initialDelaySeconds: 60 periodSeconds: 30 timeoutSeconds: 15 diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index 602f10c71..ab22874bc 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -62,12 +62,10 @@ spec: - configMapRef: name: logstash-env livenessProbe: - exec: - command: - - curl - - --silent - - --fail - - http://localhost:9600 + httpGet: + path: / + port: 9600 + scheme: HTTP initialDelaySeconds: 600 periodSeconds: 30 timeoutSeconds: 15 diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index 2add598a0..c6ba2589d 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -57,11 +57,10 @@ spec: - name: VIRTUAL_HOST value: "netbox.malcolm.local" livenessProbe: - exec: - command: - - curl - - --silent - - http://localhost:8080/netbox/api + httpGet: + path: /netbox/api + port: 8080 + scheme: HTTP initialDelaySeconds: 300 periodSeconds: 60 timeoutSeconds: 15 diff --git a/kubernetes/23-freq.yml b/kubernetes/23-freq.yml index db4c12fe9..b0c0130eb 100644 --- a/kubernetes/23-freq.yml +++ b/kubernetes/23-freq.yml @@ -46,12 +46,10 @@ spec: - name: VIRTUAL_HOST value: "freq.malcolm.local" livenessProbe: - exec: - command: - - curl - - --silent - - --fail - - http://localhost:10004 + httpGet: + path: / + port: 10004 + scheme: HTTP initialDelaySeconds: 60 periodSeconds: 30 timeoutSeconds: 15 diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index eeee321ff..f55e8bea1 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -35,6 +35,7 @@ spec: tty: true ports: - containerPort: 443 + - containerPort: 8443 envFrom: - configMapRef: name: process-env @@ -45,12 +46,10 @@ spec: - configMapRef: name: nginx-env livenessProbe: - exec: - command: - - curl - - --insecure - - --silent - - https://localhost:443 + httpGet: + path: / + port: 8443 + scheme: HTTPS initialDelaySeconds: 120 periodSeconds: 30 timeoutSeconds: 15 diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 3a747828d..42b619eb3 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -69,6 +69,17 @@ http { server file-monitor:8440; } + # health check ("here I am") without authentication + server { + listen 8443; + include /etc/nginx/nginx_ssl_config.conf; + + location / { + add_header Content-Type text/plain; + return 200 'Malcolm\n'; + } + } + # htadmin (htpasswd/user management) server { listen 488; diff --git a/nginx/nginx_readonly.conf b/nginx/nginx_readonly.conf index 4b6b3ef34..61e6b0012 100644 --- a/nginx/nginx_readonly.conf +++ b/nginx/nginx_readonly.conf @@ -53,6 +53,17 @@ http { server file-monitor:8440; } + # health check ("here I am") without authentication + server { + listen 8443; + include /etc/nginx/nginx_ssl_config.conf; + + location / { + add_header Content-Type text/plain; + return 200 'Malcolm\n'; + } + } + # Main web interface server { listen 443; From 2b95321fd3dc37c406bea6e693bbfec240aad1de Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 28 Mar 2023 15:49:00 -0600 Subject: [PATCH 065/239] restore more complicated scripting for livenessprobes for netbox-related containers (checking environment variable, etc.) --- kubernetes/15-netbox-redis.yml | 11 ++++++++--- kubernetes/16-netbox-redis-cache.yml | 11 ++++++++--- kubernetes/17-netbox-postgres.yml | 11 ++++++++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index a7e49557c..1be2acd21 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -52,9 +52,14 @@ spec: livenessProbe: exec: command: - - sh - - -c - - "pidof redis-server || pidof goStatic || exit 1" + - sh + - "-c" + - | + /bin/bash <<'EOF' + + [[ "$NETBOX_REDIS_DISABLED" == 'true' ]] || ( pidof redis-server || exit 1 ) + + EOF initialDelaySeconds: 45 periodSeconds: 60 timeoutSeconds: 15 diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index 2fd51376b..2fa333cb9 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -52,9 +52,14 @@ spec: livenessProbe: exec: command: - - sh - - -c - - "pidof redis-server || pidof goStatic || exit 1" + - sh + - "-c" + - | + /bin/bash <<'EOF' + + [[ "$NETBOX_REDIS_DISABLED" == 'true' ]] || ( pidof redis-server || exit 1 ) + + EOF initialDelaySeconds: 45 periodSeconds: 60 timeoutSeconds: 15 diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index b0446bb2a..b81177855 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -50,9 +50,14 @@ spec: livenessProbe: exec: command: - - sh - - -c - - "pg_isready || pidof goStatic || exit 1" + - sh + - "-c" + - | + /bin/bash <<'EOF' + + [[ "$NETBOX_POSTGRES_DISABLED" == 'true' ]] || pg_isready -d "$POSTGRES_DB" -U "$POSTGRES_USER" + + EOF initialDelaySeconds: 90 periodSeconds: 60 timeoutSeconds: 15 From 95fb337d6c5cfafa3653a97e7cc5f5b2883bf25e Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 28 Mar 2023 16:41:48 -0600 Subject: [PATCH 066/239] tweak netbox health check --- kubernetes/18-netbox.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index c6ba2589d..ad5b5b6f4 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -58,7 +58,7 @@ spec: value: "netbox.malcolm.local" livenessProbe: httpGet: - path: /netbox/api + path: /netbox/api/ port: 8080 scheme: HTTP initialDelaySeconds: 300 From 222039361e548704cc40b1453e8c89a7c6e032ea Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Wed, 29 Mar 2023 16:10:16 -0600 Subject: [PATCH 067/239] added python watchdog library for idaholab/Malcolm#168 --- Dockerfiles/file-monitor.Dockerfile | 2 +- Dockerfiles/filebeat.Dockerfile | 2 +- Dockerfiles/pcap-monitor.Dockerfile | 2 +- Dockerfiles/suricata.Dockerfile | 4 ++++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index 786bb410b..eb1f03543 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -134,7 +134,7 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l python3-requests \ python3-zmq \ rsync && \ - pip3 install clamd supervisor yara-python python-magic psutil pycryptodome && \ + pip3 install clamd supervisor yara-python python-magic psutil pycryptodome watchdog && \ curl -fsSLO "$SUPERCRONIC_URL" && \ echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - && \ chmod +x "$SUPERCRONIC" && \ diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index 5634c3042..d3ea91d10 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -91,7 +91,7 @@ RUN apt-get -q update && \ unar \ unzip \ xz-utils && \ - python3 -m pip install patool entrypoint2 pyunpack python-magic ordered-set supervisor && \ + python3 -m pip install patool entrypoint2 pyunpack python-magic ordered-set supervisor watchdog && \ curl -fsSLO "$SUPERCRONIC_URL" && \ echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - && \ chmod +x "$SUPERCRONIC" && \ diff --git a/Dockerfiles/pcap-monitor.Dockerfile b/Dockerfiles/pcap-monitor.Dockerfile index 8947fa2db..153569de0 100644 --- a/Dockerfiles/pcap-monitor.Dockerfile +++ b/Dockerfiles/pcap-monitor.Dockerfile @@ -60,7 +60,7 @@ RUN apt-get -q update && \ vim-tiny && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ - pip3 install --no-cache-dir opensearch-py pyzmq pyinotify python-magic requests && \ + pip3 install --no-cache-dir opensearch-py pyzmq pyinotify python-magic requests watchdog && \ groupadd --gid ${DEFAULT_GID} ${PGROUP} && \ useradd -M --uid ${DEFAULT_UID} --gid ${DEFAULT_GID} ${PUSER} diff --git a/Dockerfiles/suricata.Dockerfile b/Dockerfiles/suricata.Dockerfile index 2d0c9e720..648446ceb 100644 --- a/Dockerfiles/suricata.Dockerfile +++ b/Dockerfiles/suricata.Dockerfile @@ -90,13 +90,17 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l moreutils \ procps \ psmisc \ + python3-pip \ python3-ruamel.yaml \ + python3-setuptools \ + python3-wheel \ python3-zmq \ rsync \ supervisor \ vim-tiny \ tini \ zlib1g && \ + pip3 install --no-cache-dir watchdog && \ curl -fsSLO "$SUPERCRONIC_URL" && \ echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - && \ chmod +x "$SUPERCRONIC" && \ From a512492346e8bde373c82b4c86bc9a43ef471bd3 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Thu, 30 Mar 2023 12:06:27 -0600 Subject: [PATCH 068/239] Nginx container edits for idaholab/Malcolm#169 --- docker-compose-standalone.yml | 20 ++++++++++---------- docker-compose.yml | 20 ++++++++++---------- nginx/nginx_auth_basic.conf | 2 +- nginx/scripts/docker_entrypoint.sh | 3 +++ 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index a297cb18f..f93027835 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -445,7 +445,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./htadmin/config.ini:/var/www/htadmin/config/config.ini:rw - ./htadmin/metadata:/var/www/htadmin/config/metadata:rw - - ./nginx/htpasswd:/var/www/htadmin/config/htpasswd:rw + - ./nginx/htpasswd:/var/www/htadmin/config/auth/htpasswd:rw healthcheck: test: ["CMD", "curl", "--silent", "--fail", "http://localhost"] interval: 60s @@ -639,7 +639,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - nginx-log-path:/var/log/nginx:rw - ./nginx/nginx_ldap.conf:/etc/nginx/nginx_ldap.conf:ro - - ./nginx/htpasswd:/etc/nginx/htpasswd:ro + - ./nginx/htpasswd:/etc/nginx/auth/htpasswd:ro - ./nginx/certs:/etc/nginx/certs:ro - ./nginx/certs/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro healthcheck: @@ -649,15 +649,15 @@ services: retries: 3 start_period: 120s labels: - traefik.enable: "false" - # traefik.http.routers.osmalcolm.rule: 'Host(`opensearch.malcolm.example.org`)' - # traefik.http.routers.osmalcolm.entrypoints: 'websecure' - # traefik.http.routers.osmalcolm.tls.certresolver: 'myresolver' + traefik.enable: 'false' + # traefik.http.routers.osmalcolm.rule: 'Host(``)' + # traefik.http.routers.osmalcolm.entrypoints: '' + # traefik.http.routers.osmalcolm.tls.certresolver: '' # traefik.http.routers.osmalcolm.service: 'osmalcolm' # traefik.http.services.osmalcolm.loadbalancer.server.port: '9200' - # traefik.http.routers.malcolm.rule: 'Host(`malcolm.example.org`)' - # traefik.http.routers.malcolm.entrypoints: 'websecure' - # traefik.http.routers.malcolm.tls.certresolver: 'myresolver' + # traefik.http.routers.malcolm.rule: 'Host(``)' + # traefik.http.routers.malcolm.entrypoints: '' + # traefik.http.routers.malcolm.tls.certresolver: '' # traefik.http.routers.malcolm.service: 'malcolm' # traefik.http.services.malcolm.loadbalancer.server.port: '443' @@ -667,4 +667,4 @@ volumes: networks: default: - external: false \ No newline at end of file + external: false diff --git a/docker-compose.yml b/docker-compose.yml index a3060b43e..f262700fb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -499,7 +499,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./htadmin/config.ini:/var/www/htadmin/config/config.ini:rw - ./htadmin/metadata:/var/www/htadmin/config/metadata:rw - - ./nginx/htpasswd:/var/www/htadmin/config/htpasswd:rw + - ./nginx/htpasswd:/var/www/htadmin/config/auth/htpasswd:rw healthcheck: test: ["CMD", "curl", "--silent", "--fail", "http://localhost"] interval: 60s @@ -715,7 +715,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - nginx-log-path:/var/log/nginx:rw - ./nginx/nginx_ldap.conf:/etc/nginx/nginx_ldap.conf:ro - - ./nginx/htpasswd:/etc/nginx/htpasswd:ro + - ./nginx/htpasswd:/etc/nginx/auth/htpasswd:ro - ./nginx/certs:/etc/nginx/certs:ro - ./nginx/certs/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro healthcheck: @@ -725,15 +725,15 @@ services: retries: 3 start_period: 120s labels: - traefik.enable: "false" - # traefik.http.routers.osmalcolm.rule: 'Host(`opensearch.malcolm.example.org`)' - # traefik.http.routers.osmalcolm.entrypoints: 'websecure' - # traefik.http.routers.osmalcolm.tls.certresolver: 'myresolver' + traefik.enable: 'false' + # traefik.http.routers.osmalcolm.rule: 'Host(``)' + # traefik.http.routers.osmalcolm.entrypoints: '' + # traefik.http.routers.osmalcolm.tls.certresolver: '' # traefik.http.routers.osmalcolm.service: 'osmalcolm' # traefik.http.services.osmalcolm.loadbalancer.server.port: '9200' - # traefik.http.routers.malcolm.rule: 'Host(`malcolm.example.org`)' - # traefik.http.routers.malcolm.entrypoints: 'websecure' - # traefik.http.routers.malcolm.tls.certresolver: 'myresolver' + # traefik.http.routers.malcolm.rule: 'Host(``)' + # traefik.http.routers.malcolm.entrypoints: '' + # traefik.http.routers.malcolm.tls.certresolver: '' # traefik.http.routers.malcolm.service: 'malcolm' # traefik.http.services.malcolm.loadbalancer.server.port: '443' @@ -743,4 +743,4 @@ volumes: networks: default: - external: false \ No newline at end of file + external: false diff --git a/nginx/nginx_auth_basic.conf b/nginx/nginx_auth_basic.conf index 9b42438fd..3dc64c678 100644 --- a/nginx/nginx_auth_basic.conf +++ b/nginx/nginx_auth_basic.conf @@ -1,3 +1,3 @@ auth_basic "Authentication Required"; -auth_basic_user_file /etc/nginx/htpasswd; +auth_basic_user_file /etc/nginx/auth/htpasswd; set $authenticated_user $remote_user; diff --git a/nginx/scripts/docker_entrypoint.sh b/nginx/scripts/docker_entrypoint.sh index a8bf3a3ea..992751ec3 100755 --- a/nginx/scripts/docker_entrypoint.sh +++ b/nginx/scripts/docker_entrypoint.sh @@ -237,5 +237,8 @@ EOF fi # basic vs. ldap +if [![-f /etc/nginx/auth/]] ; then + cp /etc/nginx/auth/default/htpasswd /etc/nginx/auth/ + # start supervisor (which will spawn nginx, stunnel, etc.) or whatever the default command is exec "$@" From 0f228c179792f2fff5c70221f58f44e32dffd092 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Thu, 30 Mar 2023 13:38:57 -0600 Subject: [PATCH 069/239] Fix bash scripting syntax error for idaholab/Malcolm#169 --- nginx/scripts/docker_entrypoint.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/nginx/scripts/docker_entrypoint.sh b/nginx/scripts/docker_entrypoint.sh index 992751ec3..ec079fdd8 100755 --- a/nginx/scripts/docker_entrypoint.sh +++ b/nginx/scripts/docker_entrypoint.sh @@ -239,6 +239,7 @@ fi # basic vs. ldap if [![-f /etc/nginx/auth/]] ; then cp /etc/nginx/auth/default/htpasswd /etc/nginx/auth/ +fi # start supervisor (which will spawn nginx, stunnel, etc.) or whatever the default command is exec "$@" From f949f8686e12325a83227ed351a3071e9825d9db Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Thu, 30 Mar 2023 14:11:53 -0600 Subject: [PATCH 070/239] Fix spacing errors for idaholab/Malcoolm#169 --- nginx/scripts/docker_entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx/scripts/docker_entrypoint.sh b/nginx/scripts/docker_entrypoint.sh index ec079fdd8..b931e15cf 100755 --- a/nginx/scripts/docker_entrypoint.sh +++ b/nginx/scripts/docker_entrypoint.sh @@ -237,7 +237,7 @@ EOF fi # basic vs. ldap -if [![-f /etc/nginx/auth/]] ; then +if [[ ! -f /etc/nginx/auth/htpasswd ]]; then cp /etc/nginx/auth/default/htpasswd /etc/nginx/auth/ fi From 30df74fa6f543bfc93338dc2abad3c933bb0bc42 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Thu, 30 Mar 2023 15:12:47 -0600 Subject: [PATCH 071/239] Add in htadmin backup configs for idaholab/Malcolm#169 --- htadmin/htadmin.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/htadmin/htadmin.sh b/htadmin/htadmin.sh index 8ab93a399..c9cb0de64 100644 --- a/htadmin/htadmin.sh +++ b/htadmin/htadmin.sh @@ -19,4 +19,12 @@ else EOF python3 -m http.server 80 popd >/dev/null 2>&1 -fi \ No newline at end of file +fi + +if [[ ! -f /var/www/htadmin/config/config.ini ]]; then + cp /var/www/htadmin/config/default/config.ini /var/www/htadmin/config/config.ini +fi + +if [[ ! -f /var/www/htadmin/config/metadata ]]; then + cp /var/www/htadmin/config/default/metadata /var/www/htadmin/config/metadata +fi From 7dc4a260437148630e74c1f9d97c225aafea6b46 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Thu, 30 Mar 2023 15:52:19 -0600 Subject: [PATCH 072/239] Put in config files before starting htadmin for idaholab/Malcolm#169 --- htadmin/htadmin.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/htadmin/htadmin.sh b/htadmin/htadmin.sh index c9cb0de64..0d844ed99 100644 --- a/htadmin/htadmin.sh +++ b/htadmin/htadmin.sh @@ -2,6 +2,14 @@ HTADMIN_ENABLED=${NGINX_BASIC_AUTH:-"true"} +if [[ ! -f /var/www/htadmin/config/config.ini ]]; then + cp /var/www/htadmin/config/default/config.ini /var/www/htadmin/config/config.ini +fi + +if [[ ! -f /var/www/htadmin/config/metadata ]]; then + cp /var/www/htadmin/config/default/metadata /var/www/htadmin/config/metadata +fi + if [[ "$HTADMIN_ENABLED" == "true" ]]; then sleep 10 nginx -g "daemon off;" @@ -20,11 +28,3 @@ EOF python3 -m http.server 80 popd >/dev/null 2>&1 fi - -if [[ ! -f /var/www/htadmin/config/config.ini ]]; then - cp /var/www/htadmin/config/default/config.ini /var/www/htadmin/config/config.ini -fi - -if [[ ! -f /var/www/htadmin/config/metadata ]]; then - cp /var/www/htadmin/config/default/metadata /var/www/htadmin/config/metadata -fi From 1623c23e207dcc5f2e617b730d2b2c216c0bedd1 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 30 Mar 2023 16:27:58 -0600 Subject: [PATCH 073/239] Make auth_setup give you a choice instead of having to go through everything every time --- scripts/control.py | 1158 ++++++++++++++++--------------- shared/bin/configure-capture.py | 3 - 2 files changed, 614 insertions(+), 547 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index e79cfc193..b4581c894 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -887,579 +887,649 @@ def authSetup(wipe=False): global dockerComposeBin global opensslBin - if YesOrNo('Store administrator username/password for local Malcolm access?', default=True): - # prompt username and password - usernamePrevious = None - password = None - passwordConfirm = None - passwordEncrypted = '' - - while True: - username = AskForString("Administrator username") - if len(username) > 0: - break - - while True: - password = AskForPassword(f"{username} password: ") - passwordConfirm = AskForPassword(f"{username} password (again): ") - if password == passwordConfirm: - break - eprint("Passwords do not match") - - # get previous admin username to remove from htpasswd file if it's changed - authEnvFile = os.path.join(args.configDir, 'auth.env') - if os.path.isfile(authEnvFile): - prevAuthInfo = defaultdict(str) - with open(authEnvFile, 'r') as f: - for line in f: - try: - k, v = line.rstrip().split("=") - prevAuthInfo[k] = v.strip('"') - except: - pass - if len(prevAuthInfo['MALCOLM_USERNAME']) > 0: - usernamePrevious = prevAuthInfo['MALCOLM_USERNAME'] - - # get openssl hash of password - err, out = run_process([opensslBin, 'passwd', '-1', '-stdin'], stdin=password, stderr=False, debug=args.debug) - if (err == 0) and (len(out) > 0) and (len(out[0]) > 0): - passwordEncrypted = out[0] - else: - raise Exception('Unable to generate password hash with openssl') - - # write auth.env (used by htadmin and file-upload containers) - with open(authEnvFile, 'w') as f: - f.write( - "# Malcolm Administrator username and encrypted password for nginx reverse proxy (and upload server's SFTP access)\n" - ) - f.write(f'MALCOLM_USERNAME={username}\n') - f.write(f'MALCOLM_PASSWORD={b64encode(passwordEncrypted.encode()).decode("ascii")}\n') - os.chmod(authEnvFile, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) - - # create or update the htpasswd file - htpasswdFile = os.path.join(MalcolmPath, os.path.join('nginx', 'htpasswd')) - htpasswdCmd = ['htpasswd', '-i', '-B', htpasswdFile, username] - if not os.path.isfile(htpasswdFile): - htpasswdCmd.insert(1, '-c') - err, out = run_process(htpasswdCmd, stdin=password, stderr=True, debug=args.debug) - if err != 0: - raise Exception(f'Unable to generate htpasswd file: {out}') - - # if the admininstrator username has changed, remove the previous administrator username from htpasswd - if (usernamePrevious is not None) and (usernamePrevious != username): - htpasswdLines = list() - with open(htpasswdFile, 'r') as f: - htpasswdLines = f.readlines() - with open(htpasswdFile, 'w') as f: - for line in htpasswdLines: - if not line.startswith(f"{usernamePrevious}:"): - f.write(line) - - # configure default LDAP stuff (they'll have to edit it by hand later) - ldapConfFile = os.path.join(MalcolmPath, os.path.join('nginx', 'nginx_ldap.conf')) - if not os.path.isfile(ldapConfFile): - ldapDefaults = defaultdict(str) - if os.path.isfile(os.path.join(MalcolmPath, '.ldap_config_defaults')): - ldapDefaults = defaultdict(str) - with open(os.path.join(MalcolmPath, '.ldap_config_defaults'), 'r') as f: - for line in f: - try: - k, v = line.rstrip().split("=") - ldapDefaults[k] = v.strip('"').strip("'") - except: - pass - ldapProto = ldapDefaults.get("LDAP_PROTO", "ldap://") - ldapHost = ldapDefaults.get("LDAP_HOST", "ds.example.com") - ldapPort = ldapDefaults.get("LDAP_PORT", "3268") - ldapType = ldapDefaults.get("LDAP_SERVER_TYPE", "winldap") - if ldapType == "openldap": - ldapUri = 'DC=example,DC=com?uid?sub?(objectClass=posixAccount)' - ldapGroupAttr = "memberUid" - ldapGroupAttrIsDN = "off" - else: - ldapUri = 'DC=example,DC=com?sAMAccountName?sub?(objectClass=person)' - ldapGroupAttr = "member" - ldapGroupAttrIsDN = "on" - with open(ldapConfFile, 'w') as f: - f.write('# This is a sample configuration for the ldap_server section of nginx.conf.\n') - f.write('# Yours will vary depending on how your Active Directory/LDAP server is configured.\n') - f.write('# See https://github.com/kvspb/nginx-auth-ldap#available-config-parameters for options.\n\n') - f.write('ldap_server ad_server {\n') - f.write(f' url "{ldapProto}{ldapHost}:{ldapPort}/{ldapUri}";\n\n') - f.write(' binddn "bind_dn";\n') - f.write(' binddn_passwd "bind_dn_password";\n\n') - f.write(f' group_attribute {ldapGroupAttr};\n') - f.write(f' group_attribute_is_dn {ldapGroupAttrIsDN};\n') - f.write(' require group "CN=malcolm,OU=groups,DC=example,DC=com";\n') - f.write(' require valid_user;\n') - f.write(' satisfy all;\n') - f.write('}\n\n') - f.write('auth_ldap_cache_enabled on;\n') - f.write('auth_ldap_cache_expiration_time 10000;\n') - f.write('auth_ldap_cache_size 1000;\n') - os.chmod(ldapConfFile, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) - - # populate htadmin config file - with open(os.path.join(MalcolmPath, os.path.join('htadmin', 'config.ini')), 'w') as f: - f.write('; HTAdmin config file.\n\n') - f.write('[application]\n') - f.write('; Change this to customize your title:\n') - f.write('app_title = Malcolm User Management\n\n') - f.write('; htpasswd file\n') - f.write('secure_path = ./config/htpasswd\n') - f.write('; metadata file\n') - f.write('metadata_path = ./config/metadata\n\n') - f.write('; administrator user/password (htpasswd -b -c -B ...)\n') - f.write(f'admin_user = {username}\n\n') - f.write('; username field quality checks\n') - f.write(';\n') - f.write('min_username_len = 4\n') - f.write('max_username_len = 12\n\n') - f.write('; Password field quality checks\n') - f.write(';\n') - f.write('min_password_len = 6\n') - f.write('max_password_len = 20\n\n') - - # touch the metadata file - open(os.path.join(MalcolmPath, os.path.join('htadmin', 'metadata')), 'a').close() - - DisplayMessage( - f'Additional local accounts can be created at https://localhost:488/ when Malcolm is running', - ) - - # generate HTTPS self-signed certificates - if YesOrNo('(Re)generate self-signed certificates for HTTPS access', default=True): - with pushd(os.path.join(MalcolmPath, os.path.join('nginx', 'certs'))): - # remove previous files - for oldfile in glob.glob("*.pem"): - os.remove(oldfile) - - # generate dhparam ------------------------------- - err, out = run_process( - [opensslBin, 'dhparam', '-out', 'dhparam.pem', '2048'], stderr=True, debug=args.debug - ) - if err != 0: - raise Exception(f'Unable to generate dhparam.pem file: {out}') - - # generate key/cert ------------------------------- - err, out = run_process( - [ - opensslBin, - 'req', - '-subj', - '/CN=localhost', - '-x509', - '-newkey', - 'rsa:4096', - '-nodes', - '-keyout', - 'key.pem', - '-out', - 'cert.pem', - '-days', - '3650', - ], - stderr=True, - debug=args.debug, - ) - if err != 0: - raise Exception(f'Unable to generate key.pem/cert.pem file(s): {out}') - - # generate beats/logstash self-signed certificates + # for beats/logstash self-signed certificates logstashPath = os.path.join(MalcolmPath, os.path.join('logstash', 'certs')) filebeatPath = os.path.join(MalcolmPath, os.path.join('filebeat', 'certs')) - if YesOrNo('(Re)generate self-signed certificates for a remote log forwarder', default=True): - with pushd(logstashPath): - # make clean to clean previous files - for pat in ['*.srl', '*.csr', '*.key', '*.crt', '*.pem']: - for oldfile in glob.glob(pat): - os.remove(oldfile) - - # ----------------------------------------------- - # generate new ca/server/client certificates/keys - # ca ------------------------------- - err, out = run_process([opensslBin, 'genrsa', '-out', 'ca.key', '2048'], stderr=True, debug=args.debug) - if err != 0: - raise Exception(f'Unable to generate ca.key: {out}') - err, out = run_process( - [ - opensslBin, - 'req', - '-x509', - '-new', - '-nodes', - '-key', - 'ca.key', - '-sha256', - '-days', - '9999', - '-subj', - '/C=US/ST=ID/O=sensor/OU=ca', - '-out', - 'ca.crt', - ], - stderr=True, - debug=args.debug, + txRxScript = None + if (pyPlatform != PLATFORM_WINDOWS) and Which("croc"): + txRxScript = 'tx-rx-secure.sh' if Which('tx-rx-secure.sh') else None + if not txRxScript: + txRxScript = os.path.join( + MalcolmPath, os.path.join('shared', os.path.join('bin', os.path.join('tx-rx-secure.sh'))) ) - if err != 0: - raise Exception(f'Unable to generate ca.crt: {out}') + txRxScript = txRxScript if (txRxScript and os.path.isfile(txRxScript)) else '/usr/local/bin/tx-rx-secure.sh' + txRxScript = txRxScript if (txRxScript and os.path.isfile(txRxScript)) else '/usr/bin/tx-rx-secure.sh' + txRxScript = txRxScript if (txRxScript and os.path.isfile(txRxScript)) else None - # server ------------------------------- - err, out = run_process([opensslBin, 'genrsa', '-out', 'server.key', '2048'], stderr=True, debug=args.debug) - if err != 0: - raise Exception(f'Unable to generate server.key: {out}') + # don't make them go through every thing every time, give them a choice instead + authModeChoices = ( + ( + 'all', + "Configure all authentication-related settings", + True, + ), + ( + 'admin', + "Store administrator username/password for local Malcolm access", + False, + ), + ( + 'webcerts', + "(Re)generate self-signed certificates for HTTPS access", + False, + ), + ( + 'fwcerts', + "(Re)generate self-signed certificates for a remote log forwarder", + False, + ), + ( + 'remoteos', + "Configure remote primary or secondary OpenSearch instance", + False, + ), + ( + 'email', + "Store username/password for email alert sender account", + False, + ), + ( + 'netbox', + "(Re)generate internal passwords for NetBox", + False, + ), + ( + 'txfwcerts', + "Transfer self-signed client certificates to a remote log forwarder", + False, + ), + )[: 8 if txRxScript else -1] + + authMode = ChooseOne( + 'Configure Authentication', + choices=authModeChoices, + ) - err, out = run_process( - [ - opensslBin, - 'req', - '-sha512', - '-new', - '-key', - 'server.key', - '-out', - 'server.csr', - '-config', - 'server.conf', - ], - stderr=True, - debug=args.debug, - ) - if err != 0: - raise Exception(f'Unable to generate server.csr: {out}') + for authItem in authModeChoices[1:]: + if ((authMode == 'all') and YesOrNo(f'{authItem[1]}?', default=True)) or ( + (authMode != 'all') and (authMode == authItem[0]) + ): + if authItem[0] == 'admin': + # prompt username and password + usernamePrevious = None + password = None + passwordConfirm = None + passwordEncrypted = '' - err, out = run_process( - [ - opensslBin, - 'x509', - '-days', - '3650', - '-req', - '-sha512', - '-in', - 'server.csr', - '-CAcreateserial', - '-CA', - 'ca.crt', - '-CAkey', - 'ca.key', - '-out', - 'server.crt', - '-extensions', - 'v3_req', - '-extfile', - 'server.conf', - ], - stderr=True, - debug=args.debug, - ) - if err != 0: - raise Exception(f'Unable to generate server.crt: {out}') + while True: + username = AskForString("Administrator username") + if len(username) > 0: + break - shutil.move("server.key", "server.key.pem") - err, out = run_process( - [opensslBin, 'pkcs8', '-in', 'server.key.pem', '-topk8', '-nocrypt', '-out', 'server.key'], - stderr=True, - debug=args.debug, - ) - if err != 0: - raise Exception(f'Unable to generate server.key: {out}') + while True: + password = AskForPassword(f"{username} password: ") + passwordConfirm = AskForPassword(f"{username} password (again): ") + if password == passwordConfirm: + break + eprint("Passwords do not match") - # client ------------------------------- - err, out = run_process([opensslBin, 'genrsa', '-out', 'client.key', '2048'], stderr=True, debug=args.debug) - if err != 0: - raise Exception(f'Unable to generate client.key: {out}') + # get previous admin username to remove from htpasswd file if it's changed + authEnvFile = os.path.join(args.configDir, 'auth.env') + if os.path.isfile(authEnvFile): + prevAuthInfo = defaultdict(str) + with open(authEnvFile, 'r') as f: + for line in f: + try: + k, v = line.rstrip().split("=") + prevAuthInfo[k] = v.strip('"') + except: + pass + if len(prevAuthInfo['MALCOLM_USERNAME']) > 0: + usernamePrevious = prevAuthInfo['MALCOLM_USERNAME'] + + # get openssl hash of password + err, out = run_process( + [opensslBin, 'passwd', '-1', '-stdin'], + stdin=password, + stderr=False, + debug=args.debug, + ) + if (err == 0) and (len(out) > 0) and (len(out[0]) > 0): + passwordEncrypted = out[0] + else: + raise Exception('Unable to generate password hash with openssl') - err, out = run_process( - [ - opensslBin, - 'req', - '-sha512', - '-new', - '-key', - 'client.key', - '-out', - 'client.csr', - '-config', - 'client.conf', - ], - stderr=True, - debug=args.debug, - ) - if err != 0: - raise Exception(f'Unable to generate client.csr: {out}') + # write auth.env (used by htadmin and file-upload containers) + with open(authEnvFile, 'w') as f: + f.write( + "# Malcolm Administrator username and encrypted password for nginx reverse proxy (and upload server's SFTP access)\n" + ) + f.write(f'MALCOLM_USERNAME={username}\n') + f.write(f'MALCOLM_PASSWORD={b64encode(passwordEncrypted.encode()).decode("ascii")}\n') + os.chmod(authEnvFile, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) + + # create or update the htpasswd file + htpasswdFile = os.path.join(MalcolmPath, os.path.join('nginx', 'htpasswd')) + htpasswdCmd = ['htpasswd', '-i', '-B', htpasswdFile, username] + if not os.path.isfile(htpasswdFile): + htpasswdCmd.insert(1, '-c') + err, out = run_process(htpasswdCmd, stdin=password, stderr=True, debug=args.debug) + if err != 0: + raise Exception(f'Unable to generate htpasswd file: {out}') + + # if the admininstrator username has changed, remove the previous administrator username from htpasswd + if (usernamePrevious is not None) and (usernamePrevious != username): + htpasswdLines = list() + with open(htpasswdFile, 'r') as f: + htpasswdLines = f.readlines() + with open(htpasswdFile, 'w') as f: + for line in htpasswdLines: + if not line.startswith(f"{usernamePrevious}:"): + f.write(line) + + # configure default LDAP stuff (they'll have to edit it by hand later) + ldapConfFile = os.path.join(MalcolmPath, os.path.join('nginx', 'nginx_ldap.conf')) + if not os.path.isfile(ldapConfFile): + ldapDefaults = defaultdict(str) + if os.path.isfile(os.path.join(MalcolmPath, '.ldap_config_defaults')): + ldapDefaults = defaultdict(str) + with open(os.path.join(MalcolmPath, '.ldap_config_defaults'), 'r') as f: + for line in f: + try: + k, v = line.rstrip().split("=") + ldapDefaults[k] = v.strip('"').strip("'") + except: + pass + ldapProto = ldapDefaults.get("LDAP_PROTO", "ldap://") + ldapHost = ldapDefaults.get("LDAP_HOST", "ds.example.com") + ldapPort = ldapDefaults.get("LDAP_PORT", "3268") + ldapType = ldapDefaults.get("LDAP_SERVER_TYPE", "winldap") + if ldapType == "openldap": + ldapUri = 'DC=example,DC=com?uid?sub?(objectClass=posixAccount)' + ldapGroupAttr = "memberUid" + ldapGroupAttrIsDN = "off" + else: + ldapUri = 'DC=example,DC=com?sAMAccountName?sub?(objectClass=person)' + ldapGroupAttr = "member" + ldapGroupAttrIsDN = "on" + with open(ldapConfFile, 'w') as f: + f.write('# This is a sample configuration for the ldap_server section of nginx.conf.\n') + f.write('# Yours will vary depending on how your Active Directory/LDAP server is configured.\n') + f.write( + '# See https://github.com/kvspb/nginx-auth-ldap#available-config-parameters for options.\n\n' + ) + f.write('ldap_server ad_server {\n') + f.write(f' url "{ldapProto}{ldapHost}:{ldapPort}/{ldapUri}";\n\n') + f.write(' binddn "bind_dn";\n') + f.write(' binddn_passwd "bind_dn_password";\n\n') + f.write(f' group_attribute {ldapGroupAttr};\n') + f.write(f' group_attribute_is_dn {ldapGroupAttrIsDN};\n') + f.write(' require group "CN=malcolm,OU=groups,DC=example,DC=com";\n') + f.write(' require valid_user;\n') + f.write(' satisfy all;\n') + f.write('}\n\n') + f.write('auth_ldap_cache_enabled on;\n') + f.write('auth_ldap_cache_expiration_time 10000;\n') + f.write('auth_ldap_cache_size 1000;\n') + os.chmod(ldapConfFile, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) + + # populate htadmin config file + with open(os.path.join(MalcolmPath, os.path.join('htadmin', 'config.ini')), 'w') as f: + f.write('; HTAdmin config file.\n\n') + f.write('[application]\n') + f.write('; Change this to customize your title:\n') + f.write('app_title = Malcolm User Management\n\n') + f.write('; htpasswd file\n') + f.write('secure_path = ./config/htpasswd\n') + f.write('; metadata file\n') + f.write('metadata_path = ./config/metadata\n\n') + f.write('; administrator user/password (htpasswd -b -c -B ...)\n') + f.write(f'admin_user = {username}\n\n') + f.write('; username field quality checks\n') + f.write(';\n') + f.write('min_username_len = 4\n') + f.write('max_username_len = 12\n\n') + f.write('; Password field quality checks\n') + f.write(';\n') + f.write('min_password_len = 6\n') + f.write('max_password_len = 20\n\n') + + # touch the metadata file + open(os.path.join(MalcolmPath, os.path.join('htadmin', 'metadata')), 'a').close() + + DisplayMessage( + f'Additional local accounts can be created at https://localhost:488/ when Malcolm is running', + ) - err, out = run_process( - [ - opensslBin, - 'x509', - '-days', - '3650', - '-req', - '-sha512', - '-in', - 'client.csr', - '-CAcreateserial', - '-CA', - 'ca.crt', - '-CAkey', - 'ca.key', - '-out', - 'client.crt', - '-extensions', - 'v3_req', - '-extensions', - 'usr_cert', - '-extfile', - 'client.conf', - ], - stderr=True, - debug=args.debug, - ) - if err != 0: - raise Exception(f'Unable to generate client.crt: {out}') - # ----------------------------------------------- + # generate HTTPS self-signed certificates + elif authItem[0] == 'webcerts': + with pushd(os.path.join(MalcolmPath, os.path.join('nginx', 'certs'))): + # remove previous files + for oldfile in glob.glob("*.pem"): + os.remove(oldfile) + + # generate dhparam ------------------------------- + err, out = run_process( + [opensslBin, 'dhparam', '-out', 'dhparam.pem', '2048'], + stderr=True, + debug=args.debug, + ) + if err != 0: + raise Exception(f'Unable to generate dhparam.pem file: {out}') + + # generate key/cert ------------------------------- + err, out = run_process( + [ + opensslBin, + 'req', + '-subj', + '/CN=localhost', + '-x509', + '-newkey', + 'rsa:4096', + '-nodes', + '-keyout', + 'key.pem', + '-out', + 'cert.pem', + '-days', + '3650', + ], + stderr=True, + debug=args.debug, + ) + if err != 0: + raise Exception(f'Unable to generate key.pem/cert.pem file(s): {out}') + + elif authItem[0] == 'fwcerts': + with pushd(logstashPath): + # make clean to clean previous files + for pat in ['*.srl', '*.csr', '*.key', '*.crt', '*.pem']: + for oldfile in glob.glob(pat): + os.remove(oldfile) + + # ----------------------------------------------- + # generate new ca/server/client certificates/keys + # ca ------------------------------- + err, out = run_process( + [opensslBin, 'genrsa', '-out', 'ca.key', '2048'], + stderr=True, + debug=args.debug, + ) + if err != 0: + raise Exception(f'Unable to generate ca.key: {out}') + + err, out = run_process( + [ + opensslBin, + 'req', + '-x509', + '-new', + '-nodes', + '-key', + 'ca.key', + '-sha256', + '-days', + '9999', + '-subj', + '/C=US/ST=ID/O=sensor/OU=ca', + '-out', + 'ca.crt', + ], + stderr=True, + debug=args.debug, + ) + if err != 0: + raise Exception(f'Unable to generate ca.crt: {out}') + + # server ------------------------------- + err, out = run_process( + [opensslBin, 'genrsa', '-out', 'server.key', '2048'], + stderr=True, + debug=args.debug, + ) + if err != 0: + raise Exception(f'Unable to generate server.key: {out}') + + err, out = run_process( + [ + opensslBin, + 'req', + '-sha512', + '-new', + '-key', + 'server.key', + '-out', + 'server.csr', + '-config', + 'server.conf', + ], + stderr=True, + debug=args.debug, + ) + if err != 0: + raise Exception(f'Unable to generate server.csr: {out}') + + err, out = run_process( + [ + opensslBin, + 'x509', + '-days', + '3650', + '-req', + '-sha512', + '-in', + 'server.csr', + '-CAcreateserial', + '-CA', + 'ca.crt', + '-CAkey', + 'ca.key', + '-out', + 'server.crt', + '-extensions', + 'v3_req', + '-extfile', + 'server.conf', + ], + stderr=True, + debug=args.debug, + ) + if err != 0: + raise Exception(f'Unable to generate server.crt: {out}') + + shutil.move("server.key", "server.key.pem") + err, out = run_process( + [opensslBin, 'pkcs8', '-in', 'server.key.pem', '-topk8', '-nocrypt', '-out', 'server.key'], + stderr=True, + debug=args.debug, + ) + if err != 0: + raise Exception(f'Unable to generate server.key: {out}') + + # client ------------------------------- + err, out = run_process( + [opensslBin, 'genrsa', '-out', 'client.key', '2048'], + stderr=True, + debug=args.debug, + ) + if err != 0: + raise Exception(f'Unable to generate client.key: {out}') + + err, out = run_process( + [ + opensslBin, + 'req', + '-sha512', + '-new', + '-key', + 'client.key', + '-out', + 'client.csr', + '-config', + 'client.conf', + ], + stderr=True, + debug=args.debug, + ) + if err != 0: + raise Exception(f'Unable to generate client.csr: {out}') + + err, out = run_process( + [ + opensslBin, + 'x509', + '-days', + '3650', + '-req', + '-sha512', + '-in', + 'client.csr', + '-CAcreateserial', + '-CA', + 'ca.crt', + '-CAkey', + 'ca.key', + '-out', + 'client.crt', + '-extensions', + 'v3_req', + '-extensions', + 'usr_cert', + '-extfile', + 'client.conf', + ], + stderr=True, + debug=args.debug, + ) + if err != 0: + raise Exception(f'Unable to generate client.crt: {out}') + # ----------------------------------------------- - # mkdir filebeat/certs if it doesn't exist - try: - os.makedirs(filebeatPath) - except OSError as exc: - if (exc.errno == errno.EEXIST) and os.path.isdir(filebeatPath): - pass - else: - raise + # mkdir filebeat/certs if it doesn't exist + try: + os.makedirs(filebeatPath) + except OSError as exc: + if (exc.errno == errno.EEXIST) and os.path.isdir(filebeatPath): + pass + else: + raise - # remove previous files in filebeat/certs - for oldfile in glob.glob(os.path.join(filebeatPath, "*")): - os.remove(oldfile) + # remove previous files in filebeat/certs + for oldfile in glob.glob(os.path.join(filebeatPath, "*")): + os.remove(oldfile) + + # copy the ca so logstasn and filebeat both have it + shutil.copy2(os.path.join(logstashPath, "ca.crt"), filebeatPath) + + # move the client certs for filebeat + for f in ['client.key', 'client.crt']: + shutil.move(os.path.join(logstashPath, f), filebeatPath) + + # remove leftovers + for pat in ['*.srl', '*.csr', '*.pem']: + for oldfile in glob.glob(pat): + os.remove(oldfile) + + # create and populate connection parameters file for remote OpenSearch instance(s) + elif authItem[0] == 'remoteos': + for instance in ['primary', 'secondary']: + openSearchCredFileName = os.path.join(MalcolmPath, f'.opensearch.{instance}.curlrc') + if YesOrNo( + f'Store username/password for {instance} remote OpenSearch instance?', + default=False, + ): + prevCurlContents = ParseCurlFile(openSearchCredFileName) - # copy the ca so logstasn and filebeat both have it - shutil.copy2(os.path.join(logstashPath, "ca.crt"), filebeatPath) + # prompt host, username and password + esUsername = None + esPassword = None + esPasswordConfirm = None - # move the client certs for filebeat - for f in ['client.key', 'client.crt']: - shutil.move(os.path.join(logstashPath, f), filebeatPath) + while True: + esUsername = AskForString( + "OpenSearch username", + default=prevCurlContents['user'], + ) + if (len(esUsername) > 0) and (':' not in esUsername): + break + eprint("Username is blank (or contains a colon, which is not allowed)") - # remove leftovers - for pat in ['*.srl', '*.csr', '*.pem']: - for oldfile in glob.glob(pat): - os.remove(oldfile) + while True: + esPassword = AskForPassword(f"{esUsername} password: ") + if ( + (len(esPassword) == 0) + and (prevCurlContents['password'] is not None) + and YesOrNo(f'Use previously entered password for "{esUsername}"?', default=True) + ): + esPassword = prevCurlContents['password'] + esPasswordConfirm = esPassword + else: + esPasswordConfirm = AskForPassword(f"{esUsername} password (again): ") + if (esPassword == esPasswordConfirm) and (len(esPassword) > 0): + break + eprint("Passwords do not match") - # create and populate connection parameters file for remote OpenSearch instance(s) - if YesOrNo( - 'Will Malcolm be using an existing remote primary or secondary OpenSearch instance?', - default=False, - ): - for instance in ['primary', 'secondary']: - openSearchCredFileName = os.path.join(MalcolmPath, f'.opensearch.{instance}.curlrc') - if YesOrNo( - f'Store username/password for {instance} remote OpenSearch instance?', - default=False, - ): - prevCurlContents = ParseCurlFile(openSearchCredFileName) + esSslVerify = YesOrNo( + f'Require SSL certificate validation for OpenSearch communication?', + default=(not (('k' in prevCurlContents) or ('insecure' in prevCurlContents))), + ) - # prompt host, username and password - esUsername = None - esPassword = None - esPasswordConfirm = None + with open(openSearchCredFileName, 'w') as f: + f.write(f'user: "{EscapeForCurl(esUsername)}:{EscapeForCurl(esPassword)}"\n') + if not esSslVerify: + f.write('insecure\n') + else: + try: + os.remove(openSearchCredFileName) + except: + pass + open(openSearchCredFileName, 'a').close() + os.chmod(openSearchCredFileName, stat.S_IRUSR | stat.S_IWUSR) + + # OpenSearch authenticate sender account credentials + # https://opensearch.org/docs/latest/monitoring-plugins/alerting/monitors/#authenticate-sender-account + elif authItem[0] == 'email': + # prompt username and password + emailPassword = None + emailPasswordConfirm = None + emailSender = AskForString("OpenSearch alerting email sender name") while True: - esUsername = AskForString( - "OpenSearch username", - default=prevCurlContents['user'], - ) - if (len(esUsername) > 0) and (':' not in esUsername): + emailUsername = AskForString("Email account username") + if len(emailUsername) > 0: break - eprint("Username is blank (or contains a colon, which is not allowed)") while True: - esPassword = AskForPassword(f"{esUsername} password: ") - if ( - (len(esPassword) == 0) - and (prevCurlContents['password'] is not None) - and YesOrNo(f'Use previously entered password for "{esUsername}"?', default=True) - ): - esPassword = prevCurlContents['password'] - esPasswordConfirm = esPassword - else: - esPasswordConfirm = AskForPassword(f"{esUsername} password (again): ") - if (esPassword == esPasswordConfirm) and (len(esPassword) > 0): + emailPassword = AskForPassword(f"{emailUsername} password: ") + emailPasswordConfirm = AskForPassword(f"{emailUsername} password (again): ") + if emailPassword == emailPasswordConfirm: break eprint("Passwords do not match") - esSslVerify = YesOrNo( - f'Require SSL certificate validation for OpenSearch communication?', - default=(not (('k' in prevCurlContents) or ('insecure' in prevCurlContents))), - ) - - with open(openSearchCredFileName, 'w') as f: - f.write(f'user: "{EscapeForCurl(esUsername)}:{EscapeForCurl(esPassword)}"\n') - if not esSslVerify: - f.write('insecure\n') - - else: - try: - os.remove(openSearchCredFileName) - except: - pass - open(openSearchCredFileName, 'a').close() - os.chmod(openSearchCredFileName, stat.S_IRUSR | stat.S_IWUSR) - - # OpenSearch authenticate sender account credentials - # https://opensearch.org/docs/latest/monitoring-plugins/alerting/monitors/#authenticate-sender-account - if YesOrNo( - 'Store username/password for email alert sender account? (see https://opensearch.org/docs/latest/monitoring-plugins/alerting/monitors/#authenticate-sender-account)', - default=False, - ): - # prompt username and password - emailPassword = None - emailPasswordConfirm = None - emailSender = AskForString("OpenSearch alerting email sender name") - while True: - emailUsername = AskForString("Email account username") - if len(emailUsername) > 0: - break - - while True: - emailPassword = AskForPassword(f"{emailUsername} password: ") - emailPasswordConfirm = AskForPassword(f"{emailUsername} password (again): ") - if emailPassword == emailPasswordConfirm: - break - eprint("Passwords do not match") - - # create OpenSearch keystore file, don't complain if it already exists, and set the keystore items - usernameKey = f'plugins.alerting.destination.email.{emailSender}.username' - passwordKey = f'plugins.alerting.destination.email.{emailSender}.password' - - keystore_op('opensearch', True, 'create', stdin='N') - keystore_op('opensearch', True, 'remove', usernameKey) - keystore_op('opensearch', True, 'add', usernameKey, '--stdin', stdin=emailUsername) - keystore_op('opensearch', True, 'remove', passwordKey) - keystore_op('opensearch', True, 'add', passwordKey, '--stdin', stdin=emailPassword) - success, results = keystore_op('opensearch', True, 'list') - results = [ - x for x in results if x and (not x.upper().startswith('WARNING')) and (not x.upper().startswith('KEYSTORE')) - ] - if success and (usernameKey in results) and (passwordKey in results): - eprint(f"Email alert sender account variables stored: {', '.join(results)}") - else: - eprint("Failed to store email alert sender account variables:\n") - eprint("\n".join(results)) - - if YesOrNo( - '(Re)generate internal passwords for NetBox', - default=not os.path.isfile(os.path.join(args.configDir, 'netbox.env')), - ): - with pushd(args.configDir): - netboxPwAlphabet = string.ascii_letters + string.digits + '_' - netboxKeyAlphabet = string.ascii_letters + string.digits + '%@<=>?~^_-' - netboxPostGresPassword = ''.join(secrets.choice(netboxPwAlphabet) for i in range(24)) - netboxRedisPassword = ''.join(secrets.choice(netboxPwAlphabet) for i in range(24)) - netboxRedisCachePassword = ''.join(secrets.choice(netboxPwAlphabet) for i in range(24)) - netboxSuPassword = ''.join(secrets.choice(netboxPwAlphabet) for i in range(24)) - netboxSuToken = ''.join(secrets.choice(netboxPwAlphabet) for i in range(40)) - netboxSecretKey = ''.join(secrets.choice(netboxKeyAlphabet) for i in range(50)) - - with open('netbox-postgres.env', 'w') as f: - f.write('POSTGRES_DB=netbox\n') - f.write(f'POSTGRES_PASSWORD={netboxPostGresPassword}\n') - f.write('POSTGRES_USER=netbox\n') - os.chmod('netbox-postgres.env', stat.S_IRUSR | stat.S_IWUSR) - - with open('netbox-redis-cache.env', 'w') as f: - f.write(f'REDIS_PASSWORD={netboxRedisCachePassword}\n') - os.chmod('netbox-redis-cache.env', stat.S_IRUSR | stat.S_IWUSR) - - with open('netbox-redis.env', 'w') as f: - f.write(f'REDIS_PASSWORD={netboxRedisPassword}\n') - os.chmod('netbox-redis.env', stat.S_IRUSR | stat.S_IWUSR) - - if (not os.path.isfile('netbox.env')) and (os.path.isfile('netbox.env.example')): - shutil.copy2('netbox.env.example', 'netbox.env') - - with fileinput.FileInput('netbox.env', inplace=True, backup=None) as envFile: - for line in envFile: - line = line.rstrip("\n") - - if line.startswith('DB_PASSWORD'): - line = re.sub( - r'(DB_PASSWORD\s*=\s*)(\S+)', - fr"\g<1>{netboxPostGresPassword}", - line, - ) - elif line.startswith('REDIS_CACHE_PASSWORD'): - line = re.sub( - r'(REDIS_CACHE_PASSWORD\s*=\s*)(\S+)', - fr"\g<1>{netboxRedisCachePassword}", - line, - ) - elif line.startswith('REDIS_PASSWORD'): - line = re.sub( - r'(REDIS_PASSWORD\s*=\s*)(\S+)', - fr"\g<1>{netboxRedisPassword}", - line, - ) - elif line.startswith('SECRET_KEY'): - line = re.sub( - r'(SECRET_KEY\s*=\s*)(\S+)', - fr"\g<1>{netboxSecretKey}", - line, - ) - elif line.startswith('SUPERUSER_PASSWORD'): - line = re.sub( - r'(SUPERUSER_PASSWORD\s*=\s*)(\S+)', - fr"\g<1>{netboxSuPassword}", - line, - ) - elif line.startswith('SUPERUSER_API_TOKEN'): - line = re.sub( - r'(SUPERUSER_API_TOKEN\s*=\s*)(\S+)', - fr"\g<1>{netboxSuToken}", - line, - ) - - print(line) + # create OpenSearch keystore file, don't complain if it already exists, and set the keystore items + usernameKey = f'plugins.alerting.destination.email.{emailSender}.username' + passwordKey = f'plugins.alerting.destination.email.{emailSender}.password' + + keystore_op('opensearch', True, 'create', stdin='N') + keystore_op('opensearch', True, 'remove', usernameKey) + keystore_op('opensearch', True, 'add', usernameKey, '--stdin', stdin=emailUsername) + keystore_op('opensearch', True, 'remove', passwordKey) + keystore_op('opensearch', True, 'add', passwordKey, '--stdin', stdin=emailPassword) + success, results = keystore_op('opensearch', True, 'list') + results = [ + x + for x in results + if x and (not x.upper().startswith('WARNING')) and (not x.upper().startswith('KEYSTORE')) + ] + if success and (usernameKey in results) and (passwordKey in results): + eprint(f"Email alert sender account variables stored: {', '.join(results)}") + else: + eprint("Failed to store email alert sender account variables:\n") + eprint("\n".join(results)) + + elif authItem[0] == 'netbox': + with pushd(args.configDir): + netboxPwAlphabet = string.ascii_letters + string.digits + '_' + netboxKeyAlphabet = string.ascii_letters + string.digits + '%@<=>?~^_-' + netboxPostGresPassword = ''.join(secrets.choice(netboxPwAlphabet) for i in range(24)) + netboxRedisPassword = ''.join(secrets.choice(netboxPwAlphabet) for i in range(24)) + netboxRedisCachePassword = ''.join(secrets.choice(netboxPwAlphabet) for i in range(24)) + netboxSuPassword = ''.join(secrets.choice(netboxPwAlphabet) for i in range(24)) + netboxSuToken = ''.join(secrets.choice(netboxPwAlphabet) for i in range(40)) + netboxSecretKey = ''.join(secrets.choice(netboxKeyAlphabet) for i in range(50)) + + with open('netbox-postgres.env', 'w') as f: + f.write('POSTGRES_DB=netbox\n') + f.write(f'POSTGRES_PASSWORD={netboxPostGresPassword}\n') + f.write('POSTGRES_USER=netbox\n') + os.chmod('netbox-postgres.env', stat.S_IRUSR | stat.S_IWUSR) + + with open('netbox-redis-cache.env', 'w') as f: + f.write(f'REDIS_PASSWORD={netboxRedisCachePassword}\n') + os.chmod('netbox-redis-cache.env', stat.S_IRUSR | stat.S_IWUSR) + + with open('netbox-redis.env', 'w') as f: + f.write(f'REDIS_PASSWORD={netboxRedisPassword}\n') + os.chmod('netbox-redis.env', stat.S_IRUSR | stat.S_IWUSR) + + if (not os.path.isfile('netbox.env')) and (os.path.isfile('netbox.env.example')): + shutil.copy2('netbox.env.example', 'netbox.env') + + with fileinput.FileInput('netbox.env', inplace=True, backup=None) as envFile: + for line in envFile: + line = line.rstrip("\n") + + if line.startswith('DB_PASSWORD'): + line = re.sub( + r'(DB_PASSWORD\s*=\s*)(\S+)', + fr"\g<1>{netboxPostGresPassword}", + line, + ) + elif line.startswith('REDIS_CACHE_PASSWORD'): + line = re.sub( + r'(REDIS_CACHE_PASSWORD\s*=\s*)(\S+)', + fr"\g<1>{netboxRedisCachePassword}", + line, + ) + elif line.startswith('REDIS_PASSWORD'): + line = re.sub( + r'(REDIS_PASSWORD\s*=\s*)(\S+)', + fr"\g<1>{netboxRedisPassword}", + line, + ) + elif line.startswith('SECRET_KEY'): + line = re.sub( + r'(SECRET_KEY\s*=\s*)(\S+)', + fr"\g<1>{netboxSecretKey}", + line, + ) + elif line.startswith('SUPERUSER_PASSWORD'): + line = re.sub( + r'(SUPERUSER_PASSWORD\s*=\s*)(\S+)', + fr"\g<1>{netboxSuPassword}", + line, + ) + elif line.startswith('SUPERUSER_API_TOKEN'): + line = re.sub( + r'(SUPERUSER_API_TOKEN\s*=\s*)(\S+)', + fr"\g<1>{netboxSuToken}", + line, + ) - os.chmod('netbox.env', stat.S_IRUSR | stat.S_IWUSR) + print(line) - if (pyPlatform != PLATFORM_WINDOWS) and Which("croc"): - txRxScript = 'tx-rx-secure.sh' if Which('tx-rx-secure.sh') else None - if not txRxScript: - txRxScript = os.path.join( - MalcolmPath, os.path.join('shared', os.path.join('bin', os.path.join('tx-rx-secure.sh'))) - ) - txRxScript = txRxScript if (txRxScript and os.path.isfile(txRxScript)) else '/usr/local/bin/tx-rx-secure.sh' - txRxScript = txRxScript if (txRxScript and os.path.isfile(txRxScript)) else '/usr/bin/tx-rx-secure.sh' - txRxScript = txRxScript if (txRxScript and os.path.isfile(txRxScript)) else None - if txRxScript and YesOrNo('Transfer self-signed client certificates to a remote log forwarder?', default=False): - DisplayMessage( - f'Run configure-capture on the remote log forwarder, select "Configure Forwarding," then "Receive client SSL files', - ) - with pushd(filebeatPath): - with Popen( - [txRxScript, '-t', "ca.crt", "client.crt", "client.key"], - stdout=PIPE, - stderr=STDOUT, - bufsize=0 if MainDialog else -1, - ) as p: - if MainDialog: - DisplayProgramBox( - fileDescriptor=p.stdout.fileno(), - text='ssl-client-transmit', - clearScreen=True, - ) - else: - while True: - output = p.stdout.readline() - if (len(output) == 0) and (p.poll() is not None): - break - if output: - print(output.decode('utf-8').rstrip()) - else: - time.sleep(0.5) + os.chmod('netbox.env', stat.S_IRUSR | stat.S_IWUSR) - p.poll() + elif authItem[0] == 'txfwcerts': + DisplayMessage( + f'Run configure-capture on the remote log forwarder, select "Configure Forwarding," then "Receive client SSL files..."', + ) + with pushd(filebeatPath): + with Popen( + [txRxScript, '-t', "ca.crt", "client.crt", "client.key"], + stdout=PIPE, + stderr=STDOUT, + bufsize=0 if MainDialog else -1, + ) as p: + if MainDialog: + DisplayProgramBox( + fileDescriptor=p.stdout.fileno(), + text='ssl-client-transmit', + clearScreen=True, + ) + else: + while True: + output = p.stdout.readline() + if (len(output) == 0) and (p.poll() is not None): + break + if output: + print(output.decode('utf-8').rstrip()) + else: + time.sleep(0.5) + + p.poll() ################################################################################################### diff --git a/shared/bin/configure-capture.py b/shared/bin/configure-capture.py index 1ded52734..2a56182ac 100755 --- a/shared/bin/configure-capture.py +++ b/shared/bin/configure-capture.py @@ -1215,9 +1215,6 @@ def main(): if (code == Dialog.CANCEL) or (code == Dialog.ESC): raise CancelledError - code = d.msgbox( - text='Run configure-capture on the remote log forwarder, select "Configure Forwarding," then "Receive client SSL files..."', - ) with Popen( [txRxScript, '-s', tx_ip, '-r', rx_token, '-o', BEAT_LS_CERT_DIR_DEFAULT], stdout=PIPE, From 6f42b1a1017f5abd5f15384fa4420b20ac54b111 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 31 Mar 2023 15:53:44 -0600 Subject: [PATCH 074/239] work in progress for some python refactoring --- shared/bin/configure-capture.py | 4 +- shared/bin/configure-interfaces.py | 5 +- shared/bin/ics-oui-parse.py | 20 +- shared/bin/malcolm_utils.py | 383 ++++++++++++++++++ shared/bin/manuf-oui-parse.py | 28 +- shared/bin/opensearch_index_size_prune.py | 20 +- shared/bin/opensearch_read_only.py | 19 +- shared/bin/pcap_processor.py | 14 +- shared/bin/pcap_utils.py | 117 ------ shared/bin/pcap_watcher.py | 3 + shared/bin/sensorcommon.py | 94 +---- shared/bin/suricata_config_populate.py | 132 +----- shared/bin/suricata_update_config_populate.py | 63 +-- shared/bin/watch_common.py | 186 +++++++++ shared/bin/zeek_carve_logger.py | 21 +- shared/bin/zeek_carve_scanner.py | 13 +- shared/bin/zeek_carve_utils.py | 213 ++++------ shared/bin/zeek_carve_watcher.py | 8 +- shared/bin/zeek_carved_http_server.py | 61 +-- shared/bin/zeek_intel_from_threat_feed.py | 6 +- shared/bin/zeek_threat_feed_utils.py | 72 +--- 21 files changed, 705 insertions(+), 777 deletions(-) create mode 100644 shared/bin/malcolm_utils.py create mode 100644 shared/bin/watch_common.py diff --git a/shared/bin/configure-capture.py b/shared/bin/configure-capture.py index f667fa1ae..f0273f32e 100755 --- a/shared/bin/configure-capture.py +++ b/shared/bin/configure-capture.py @@ -14,9 +14,11 @@ from collections import defaultdict from dialog import Dialog +from subprocess import PIPE, STDOUT, Popen, CalledProcessError + from zeek_carve_utils import * from sensorcommon import * -from subprocess import PIPE, STDOUT, Popen, CalledProcessError +from malcolm_common import run_process class Constants: diff --git a/shared/bin/configure-interfaces.py b/shared/bin/configure-interfaces.py index 7308f36e5..e144d82df 100755 --- a/shared/bin/configure-interfaces.py +++ b/shared/bin/configure-interfaces.py @@ -15,6 +15,8 @@ from debinterface.interfaces import Interfaces from sensorcommon import * +from malcolm_utils import run_process + class Constants: DHCP = 'dhcp' @@ -79,6 +81,7 @@ class Constants: d = Dialog(dialog='dialog', autowidgetsize=True) d.set_background_title(Constants.MSG_BACKGROUND_TITLE) + ################################################################################################### # if the given interface is up, "ifdown" it def network_stop(selected_iface): @@ -112,7 +115,6 @@ def network_start(selected_iface): ################################################################################################### # for a given interface, bring it down, write its new settings, and bring it back up def write_and_display_results(interfaces, selected_iface): - ecode, stop_results = network_stop(selected_iface) stop_results = list( filter( @@ -182,7 +184,6 @@ def main(): while not quit_flag: os.chdir(start_dir) try: - # welcome code = d.yesno(Constants.MSG_WELCOME_TITLE, yes_label="Continue", no_label="Quit") if code == Dialog.CANCEL or code == Dialog.ESC: diff --git a/shared/bin/ics-oui-parse.py b/shared/bin/ics-oui-parse.py index 00cc795d1..08477fb18 100755 --- a/shared/bin/ics-oui-parse.py +++ b/shared/bin/ics-oui-parse.py @@ -14,6 +14,9 @@ from netaddr import * from operator import itemgetter +import malcolm_utils +from malcolm_utils import eprint, str2bool + ################################################################################################### args = None debug = False @@ -24,23 +27,6 @@ padded_mac_low = '00:00:00:00:00:00' padded_mac_high = 'FF:FF:FF:FF:FF:FF' -################################################################################################### -# print to stderr -def eprint(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - sys.stderr.flush() - - -################################################################################################### -# convenient boolean argument parsing -def str2bool(v): - if v.lower() in ("yes", "true", "t", "y", "1"): - return True - elif v.lower() in ("no", "false", "f", "n", "0"): - return False - else: - raise ValueError("Boolean value expected") - ################################################################################################### # main diff --git a/shared/bin/malcolm_utils.py b/shared/bin/malcolm_utils.py new file mode 100644 index 000000000..36915da96 --- /dev/null +++ b/shared/bin/malcolm_utils.py @@ -0,0 +1,383 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import json +import hashlib +import contextlib +import subprocess +import sys + +from collections import OrderedDict +from multiprocessing import RawValue +from threading import Lock +from base64 import b64decode +from tempfile import NamedTemporaryFile +from Crypto.Cipher import AES + +################################################################################################### +# EVP_BytesToKey +# +# reference: https://github.com/openssl/openssl/blob/6f0ac0e2f27d9240516edb9a23b7863e7ad02898/crypto/evp/evp_key.c#L74 +# https://gist.github.com/chrono-meter/d122cbefc6f6248a0af554995f072460 +EVP_KEY_SIZE = 32 +OPENSSL_ENC_MAGIC = b'Salted__' +PKCS5_SALT_LEN = 8 + + +def EVP_BytesToKey(key_length: int, iv_length: int, md, salt: bytes, data: bytes, count: int = 1) -> (bytes, bytes): + assert data + assert salt == b'' or len(salt) == PKCS5_SALT_LEN + + md_buf = b'' + key = b'' + iv = b'' + addmd = 0 + + while key_length > len(key) or iv_length > len(iv): + c = md() + if addmd: + c.update(md_buf) + addmd += 1 + c.update(data) + c.update(salt) + md_buf = c.digest() + for i in range(1, count): + md_buf = md(md_buf) + + md_buf2 = md_buf + + if key_length > len(key): + key, md_buf2 = key + md_buf2[: key_length - len(key)], md_buf2[key_length - len(key) :] + + if iv_length > len(iv): + iv = iv + md_buf2[: iv_length - len(iv)] + + return key, iv + + +def base64_decode_if_prefixed(s: str): + if s.startswith('base64:'): + return b64decode(s[7:]).decode('utf-8') + else: + return s + + +def LoadStrIfJson(jsonStr): + try: + return json.loads(jsonStr) + except ValueError as e: + return None + + +def LoadFileIfJson(fileHandle): + try: + return json.load(fileHandle) + except ValueError as e: + return None + + +@contextlib.contextmanager +def temporary_filename(suffix=None): + try: + f = tempfile.NamedTemporaryFile(suffix=suffix, delete=False) + tmp_name = f.name + f.close() + yield tmp_name + finally: + os.unlink(tmp_name) + + +################################################################################################### +@contextlib.contextmanager +def pushd(directory): + prevDir = os.getcwd() + os.chdir(directory) + try: + yield + finally: + os.chdir(prevDir) + + +################################################################################################### +def get_iterable(x): + if isinstance(x, Iterable) and not isinstance(x, str): + return x + else: + return (x,) + + +################################################################################################### +def deep_get(d, keys, default=None): + k = get_iterable(keys) + if d is None: + return default + if not keys: + return d + return deep_get(d.get(k[0]), k[1:], default) + + +################################################################################################### +def deep_set(d, keys, value, deleteIfNone=False): + k = get_iterable(keys) + for key in k[:-1]: + if (key not in d) or (not isinstance(d[key], dict)): + d[key] = dict() + d = d[key] + d[k[-1]] = value + if (deleteIfNone == True) and (value is None): + d.pop(k[-1], None) + + +################################################################################################### +# open a file and close it, updating its access time +def touch(filename): + open(filename, 'a').close() + os.utime(filename, None) + + +################################################################################################### +# print to stderr +def eprint(*args, **kwargs): + if "timestamp" in kwargs and kwargs["timestamp"]: + print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), *args, file=sys.stderr, **kwargs) + else: + print(*args, file=sys.stderr, **kwargs) + if "flush" in kwargs and kwargs["flush"]: + sys.stderr.flush() + + +################################################################################################### +# urlencode each character of a string +def aggressive_url_encode(string): + return "".join("%{0:0>2}".format(format(ord(char), "x")) for char in string) + + +################################################################################################### +# strip a prefix from the beginning of a string if needed +def remove_prefix(text, prefix): + if (len(prefix) > 0) and text.startswith(prefix): + return text[len(prefix) :] + else: + return text + + +################################################################################################### +# nice human-readable file sizes +def sizeof_fmt(num, suffix='B'): + for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: + if abs(num) < 1024.0: + return "%3.1f%s%s" % (num, unit, suffix) + num /= 1024.0 + return "%.1f%s%s" % (num, 'Yi', suffix) + + +################################################################################################### +# convenient boolean argument parsing +def str2bool(v): + if v.lower() in ('yes', 'true', 't', 'y', '1'): + return True + elif v.lower() in ('no', 'false', 'f', 'n', '0'): + return False + else: + raise ValueError('Boolean value expected.') + + +################################################################################################### +def val2bool(v): + try: + if v is None: + return False + elif isinstance(v, bool): + return v + elif isinstance(v, str): + if v.lower() in ("yes", "true", "t", "y"): + return True + elif v.lower() in ("no", "false", "f", "n"): + return False + else: + raise ValueError(f'Boolean value expected (got {v})') + else: + raise ValueError(f'Boolean value expected (got {v})') + except: + # just pitch it back and let the caller worry about it + return v + + +################################################################################################### +# will it float? +def isfloat(value): + try: + float(value) + return True + except ValueError: + return False + + +################################################################################################### +# check a string or list to see if something is a valid IP address +def isipaddress(value): + result = True + try: + if isinstance(value, list) or isinstance(value, tuple) or isinstance(value, set): + for v in value: + ip = ipaddress.ip_address(v) + else: + ip = ipaddress.ip_address(value) + except: + result = False + return result + + +################################################################################################### +def same_file_or_dir(path1, path2): + try: + return os.path.samefile(path1, path2) + except Exception: + return False + + +################################################################################################### +# calculate a sha256 hash of a file +def sha256sum(filename): + h = hashlib.sha256() + b = bytearray(64 * 1024) + mv = memoryview(b) + with open(filename, 'rb', buffering=0) as f: + for n in iter(lambda: f.readinto(mv), 0): + h.update(mv[:n]) + return h.hexdigest() + + +################################################################################################### +# recursive dictionary key search +def dictsearch(d, target): + val = filter( + None, [[b] if a == target else dictsearch(b, target) if isinstance(b, dict) else None for a, b in d.items()] + ) + return [i for b in val for i in b] + + +################################################################################################### +# atomic integer class and context manager +class AtomicInt: + def __init__(self, value=0): + self.val = RawValue('i', value) + self.lock = Lock() + + def increment(self): + with self.lock: + self.val.value += 1 + return self.val.value + + def decrement(self): + with self.lock: + self.val.value -= 1 + return self.val.value + + def value(self): + with self.lock: + return self.val.value + + def __enter__(self): + return self.increment() + + def __exit__(self, type, value, traceback): + return self.decrement() + + +################################################################################################### +# atomic integer class and context manager +class ContextLockedOrderedDict(OrderedDict): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.lock = Lock() + + def __enter__(self): + self.lock.acquire() + return self + + def __exit__(self, type, value, traceback): + self.lock.release() + return self + + +################################################################################################### +# run command with arguments and return its exit code, stdout, and stderr +def check_output_input(*popenargs, **kwargs): + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be overridden') + + if 'stderr' in kwargs: + raise ValueError('stderr argument not allowed, it will be overridden') + + if 'input' in kwargs and kwargs['input']: + if 'stdin' in kwargs: + raise ValueError('stdin and input arguments may not both be used') + inputdata = kwargs['input'] + kwargs['stdin'] = PIPE + else: + inputdata = None + kwargs.pop('input', None) + + process = Popen(*popenargs, stdout=PIPE, stderr=PIPE, **kwargs) + try: + output, errput = process.communicate(inputdata) + except: + process.kill() + process.wait() + raise + + retcode = process.poll() + + return retcode, output, errput + + +################################################################################################### +# run command with arguments and return its exit code and output +def run_process(command, stdout=True, stderr=True, stdin=None, cwd=None, env=None, debug=False, logger=None): + retcode = -1 + output = [] + + try: + # run the command + retcode, cmdout, cmderr = check_output_input(command, input=stdin.encode() if stdin else None, cwd=cwd, env=env) + + # split the output on newlines to return a list + if stderr and (len(cmderr) > 0): + output.extend(cmderr.decode(sys.getdefaultencoding()).split('\n')) + if stdout and (len(cmdout) > 0): + output.extend(cmdout.decode(sys.getdefaultencoding()).split('\n')) + + except (FileNotFoundError, OSError, IOError) as e: + if stderr: + output.append("Command {} not found or unable to execute".format(command)) + + if debug: + dbgStr = "{}{} returned {}: {}".format( + command, "({})".format(stdin[:80] + bool(stdin[80:]) * '...' if stdin else ""), retcode, output + ) + if logger is not None: + logger.debug(dbgStr) + else: + eprint(dbgStr) + + return retcode, output + + +################################################################################################### +# execute a shell process returning its exit code and output +def run_subprocess(command, stdout=True, stderr=False, stdin=None, timeout=60): + retcode = -1 + output = [] + p = subprocess.run( + [command], input=stdin, universal_newlines=True, capture_output=True, shell=True, timeout=timeout + ) + if p: + retcode = p.returncode + if stderr and p.stderr: + output.extend(p.stderr.splitlines()) + if stdout and p.stdout: + output.extend(p.stdout.splitlines()) + + return retcode, output diff --git a/shared/bin/manuf-oui-parse.py b/shared/bin/manuf-oui-parse.py index 4ea487960..7cc0be2f4 100755 --- a/shared/bin/manuf-oui-parse.py +++ b/shared/bin/manuf-oui-parse.py @@ -9,6 +9,9 @@ import sys import tempfile +import malcolm_utils +from malcolm_utils import eprint, str2bool + try: import ruamel.yaml as yaml except ImportError: @@ -25,22 +28,6 @@ scriptPath = os.path.dirname(os.path.realpath(__file__)) origPath = os.getcwd() -################################################################################################### -# print to stderr -def eprint(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - - -################################################################################################### -# convenient boolean argument parsing -def str2bool(v): - if v.lower() in ('yes', 'true', 't', 'y', '1'): - return True - elif v.lower() in ('no', 'false', 'f', 'n', '0'): - return False - else: - raise argparse.ArgumentTypeError('Boolean value expected.') - def strip_mac(mac): return mac_pattern.sub("", mac) @@ -57,7 +44,14 @@ def main(): parser = argparse.ArgumentParser(description=scriptName, add_help=False, usage='{} '.format(scriptName)) parser.add_argument( - '-v', '--verbose', dest='debug', type=str2bool, nargs='?', const=True, default=False, help="Verbose output" + '-v', + '--verbose', + dest='debug', + type=str2bool, + nargs='?', + const=True, + default=False, + help="Verbose output", ) parser.add_argument( '-i', diff --git a/shared/bin/opensearch_index_size_prune.py b/shared/bin/opensearch_index_size_prune.py index e2a7e5249..4b136c2b3 100755 --- a/shared/bin/opensearch_index_size_prune.py +++ b/shared/bin/opensearch_index_size_prune.py @@ -14,28 +14,15 @@ from collections import defaultdict from requests.auth import HTTPBasicAuth +import malcolm_utils +from malcolm_utils import eprint, str2bool + ################################################################################################### debug = False scriptName = os.path.basename(__file__) scriptPath = os.path.dirname(os.path.realpath(__file__)) urllib3.disable_warnings() -################################################################################################### -# print to stderr -def eprint(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - - -################################################################################################### -# convenient boolean argument parsing -def str2bool(v): - if v.lower() in ('yes', 'true', 't', 'y', '1'): - return True - elif v.lower() in ('no', 'false', 'f', 'n', '0'): - return False - else: - raise argparse.ArgumentTypeError('Boolean value expected.') - ################################################################################################### # main @@ -209,7 +196,6 @@ def main(): raise Exception(f'Invalid limit percentage {args.limit}') if limitPercent is not None: - # get allocation statistics for node(s) to do percentage calculation esDiskUsageStats = [] osInfoResponse = requests.get( diff --git a/shared/bin/opensearch_read_only.py b/shared/bin/opensearch_read_only.py index 8fef64818..e4d86e276 100755 --- a/shared/bin/opensearch_read_only.py +++ b/shared/bin/opensearch_read_only.py @@ -14,28 +14,15 @@ from collections import defaultdict from requests.auth import HTTPBasicAuth +import malcolm_utils +from malcolm_utils import eprint, str2bool + ################################################################################################### debug = False scriptName = os.path.basename(__file__) scriptPath = os.path.dirname(os.path.realpath(__file__)) urllib3.disable_warnings() -################################################################################################### -# print to stderr -def eprint(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - - -################################################################################################### -# convenient boolean argument parsing -def str2bool(v): - if v.lower() in ('yes', 'true', 't', 'y', '1'): - return True - elif v.lower() in ('no', 'false', 'f', 'n', '0'): - return False - else: - raise argparse.ArgumentTypeError('Boolean value expected.') - ################################################################################################### # main diff --git a/shared/bin/pcap_processor.py b/shared/bin/pcap_processor.py index a458a8672..1637e8037 100755 --- a/shared/bin/pcap_processor.py +++ b/shared/bin/pcap_processor.py @@ -22,6 +22,9 @@ import zmq from pcap_utils import * +import malcolm_utils +from malcolm_utils import eprint, str2bool, AtomicInt + from multiprocessing.pool import ThreadPool from collections import deque from itertools import chain, repeat @@ -77,6 +80,7 @@ arkimeProvider = os.getenv('ARKIME_ECS_PROVIDER', 'arkime') arkimeDataset = os.getenv('ARKIME_ECS_DATASET', 'session') + ################################################################################################### # handle sigint/sigterm and set a global shutdown variable def shutdown_handler(signum, frame): @@ -132,7 +136,6 @@ def arkimeCaptureFileWorker(arkimeWorkerArgs): time.sleep(1) else: if isinstance(fileInfo, dict) and (FILE_INFO_DICT_NAME in fileInfo): - if pcapBaseDir and os.path.isdir(pcapBaseDir): fileInfo[FILE_INFO_DICT_NAME] = os.path.join(pcapBaseDir, fileInfo[FILE_INFO_DICT_NAME]) @@ -217,7 +220,6 @@ def zeekFileWorker(zeekWorkerArgs): time.sleep(1) else: if isinstance(fileInfo, dict) and (FILE_INFO_DICT_NAME in fileInfo) and os.path.isdir(uploadDir): - if pcapBaseDir and os.path.isdir(pcapBaseDir): fileInfo[FILE_INFO_DICT_NAME] = os.path.join(pcapBaseDir, fileInfo[FILE_INFO_DICT_NAME]) @@ -237,7 +239,6 @@ def zeekFileWorker(zeekWorkerArgs): ) ) ): - extractFileMode = defaultExtractFileMode # if file carving was specified via tag, make note of it @@ -266,7 +267,6 @@ def zeekFileWorker(zeekWorkerArgs): # create a temporary work directory where zeek will be executed to generate the log files with tempfile.TemporaryDirectory() as tmpLogDir: if os.path.isdir(tmpLogDir): - processTimeUsec = int(round(time.time() * 1000000)) # use Zeek to process the pcap @@ -302,7 +302,6 @@ def zeekFileWorker(zeekWorkerArgs): # make sure log files were generated logFiles = [logFile for logFile in os.listdir(tmpLogDir) if logFile.endswith('.log')] if len(logFiles) > 0: - # tar up the results tgzFileName = os.path.join( tmpLogDir, @@ -373,7 +372,6 @@ def suricataFileWorker(suricataWorkerArgs): time.sleep(1) else: if isinstance(fileInfo, dict) and (FILE_INFO_DICT_NAME in fileInfo): - # Suricata this PCAP if it's tagged "AUTOSURICATA" or if the global autoSuricata flag is turned on. # However, skip "live" PCAPs Malcolm is capturing and rotating through for Arkime capture, # as Suricata now does its own network capture in Malcolm standalone mode. @@ -391,12 +389,10 @@ def suricataFileWorker(suricataWorkerArgs): ) ) ): - if pcapBaseDir and os.path.isdir(pcapBaseDir): fileInfo[FILE_INFO_DICT_NAME] = os.path.join(pcapBaseDir, fileInfo[FILE_INFO_DICT_NAME]) if os.path.isfile(fileInfo[FILE_INFO_DICT_NAME]): - # finalize tags list fileInfo[FILE_INFO_DICT_TAGS] = ( [ @@ -413,7 +409,6 @@ def suricataFileWorker(suricataWorkerArgs): # create a temporary work directory where suricata will be executed to generate the log files with tempfile.TemporaryDirectory() as tmpLogDir: if os.path.isdir(tmpLogDir): - processTimeUsec = int(round(time.time() * 1000000)) # put together suricata execution command @@ -467,7 +462,6 @@ def suricataFileWorker(suricataWorkerArgs): ################################################################################################### # main def main(): - processingMode = None if 'pcap_processor' in scriptName: eprint( diff --git a/shared/bin/pcap_utils.py b/shared/bin/pcap_utils.py index b1c166a2a..2c9bf4f57 100644 --- a/shared/bin/pcap_utils.py +++ b/shared/bin/pcap_utils.py @@ -23,123 +23,6 @@ FILE_INFO_FILE_MIME = "mime" FILE_INFO_DICT_NODE = "node" -################################################################################################### -# print to stderr -def eprint(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - sys.stderr.flush() - - -################################################################################################### -# convenient boolean argument parsing -def str2bool(v): - if v.lower() in ('yes', 'true', 't', 'y', '1'): - return True - elif v.lower() in ('no', 'false', 'f', 'n', '0'): - return False - else: - raise argparse.ArgumentTypeError('Boolean value expected.') - - -################################################################################################### -# strip a prefix from the beginning of a string if needed -def remove_prefix(text, prefix): - if (len(prefix) > 0) and text.startswith(prefix): - return text[len(prefix) :] - else: - return text - - -################################################################################################### -# open a file and close it, updating its access time -def touch(filename): - open(filename, 'a').close() - os.utime(filename, None) - - -################################################################################################### -# run command with arguments and return its exit code, stdout, and stderr -def check_output_input(*popenargs, **kwargs): - - if 'stdout' in kwargs: - raise ValueError('stdout argument not allowed, it will be overridden') - - if 'stderr' in kwargs: - raise ValueError('stderr argument not allowed, it will be overridden') - - if 'input' in kwargs and kwargs['input']: - if 'stdin' in kwargs: - raise ValueError('stdin and input arguments may not both be used') - inputdata = kwargs['input'] - kwargs['stdin'] = PIPE - else: - inputdata = None - kwargs.pop('input', None) - - process = Popen(*popenargs, stdout=PIPE, stderr=PIPE, **kwargs) - try: - output, errput = process.communicate(inputdata) - except: - process.kill() - process.wait() - raise - - retcode = process.poll() - - return retcode, output, errput - - -################################################################################################### -# run command with arguments and return its exit code and output -def run_process(command, stdout=True, stderr=True, stdin=None, cwd=None, env=None, debug=False): - - retcode = -1 - output = [] - - try: - # run the command - retcode, cmdout, cmderr = check_output_input(command, input=stdin.encode() if stdin else None, cwd=cwd, env=env) - - # split the output on newlines to return a list - if stderr and (len(cmderr) > 0): - output.extend(cmderr.decode(sys.getdefaultencoding()).split('\n')) - if stdout and (len(cmdout) > 0): - output.extend(cmdout.decode(sys.getdefaultencoding()).split('\n')) - - except (FileNotFoundError, OSError, IOError) as e: - if stderr: - output.append("Command {} not found or unable to execute".format(command)) - - if debug: - eprint( - "{}{} returned {}: {}".format( - command, "({})".format(stdin[:80] + bool(stdin[80:]) * '...' if stdin else ""), retcode, output - ) - ) - - return retcode, output - - -################################################################################################### -class AtomicInt: - def __init__(self, value=0): - self.val = RawValue('i', value) - self.lock = Lock() - - def increment(self): - with self.lock: - self.val.value += 1 - return self.val.value - - def decrement(self): - with self.lock: - self.val.value -= 1 - return self.val.value - - def value(self): - with self.lock: - return self.val.value - ################################################################################################### # split a PCAP filename up into tags diff --git a/shared/bin/pcap_watcher.py b/shared/bin/pcap_watcher.py index 2b785cdc2..0b3062fe3 100755 --- a/shared/bin/pcap_watcher.py +++ b/shared/bin/pcap_watcher.py @@ -24,6 +24,9 @@ import zmq from pcap_utils import * +import malcolm_utils +from malcolm_utils import eprint, str2bool + from collections import defaultdict from opensearchpy import OpenSearch, Search diff --git a/shared/bin/sensorcommon.py b/shared/bin/sensorcommon.py index 409fe0c77..693462126 100644 --- a/shared/bin/sensorcommon.py +++ b/shared/bin/sensorcommon.py @@ -9,7 +9,6 @@ import os import socket import ssl -import subprocess import sys import urllib.request @@ -21,8 +20,11 @@ from multiprocessing import RawValue from threading import Lock +from malcolm_utils import run_subprocess + NIC_BLINK_SECONDS = 10 + ################################################################################################### class CancelledError(Exception): """Raised when user cancels the operation""" @@ -44,91 +46,6 @@ def clearquit(): sys.exit(0) -################################################################################################### -# print to stderr -def eprint(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - - -################################################################################################### -# urlencode each character of a string -def aggressive_url_encode(string): - return "".join("%{0:0>2}".format(format(ord(char), "x")) for char in string) - - -################################################################################################### -# strip a prefix from the beginning of a string if needed -def remove_prefix(text, prefix): - if (len(prefix) > 0) and text.startswith(prefix): - return text[len(prefix) :] - else: - return text - - -################################################################################################### -# nice human-readable file sizes -def sizeof_fmt(num, suffix='B'): - for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: - if abs(num) < 1024.0: - return "%3.1f%s%s" % (num, unit, suffix) - num /= 1024.0 - return "%.1f%s%s" % (num, 'Yi', suffix) - - -################################################################################################### -# convenient boolean argument parsing -def str2bool(v): - if v.lower() in ('yes', 'true', 't', 'y', '1'): - return True - elif v.lower() in ('no', 'false', 'f', 'n', '0'): - return False - else: - raise argparse.ArgumentTypeError('Boolean value expected.') - - -################################################################################################### -# will it float? -def isfloat(value): - try: - float(value) - return True - except ValueError: - return False - - -################################################################################################### -# check a string or list to see if something is a valid IP address -def isipaddress(value): - result = True - try: - if isinstance(value, list) or isinstance(value, tuple) or isinstance(value, set): - for v in value: - ip = ipaddress.ip_address(v) - else: - ip = ipaddress.ip_address(value) - except: - result = False - return result - - -################################################################################################### -# execute a shell process returning its exit code and output -def run_process(command, stdout=True, stderr=False, stdin=None, timeout=60): - retcode = -1 - output = [] - p = subprocess.run( - [command], input=stdin, universal_newlines=True, capture_output=True, shell=True, timeout=timeout - ) - if p: - retcode = p.returncode - if stderr and p.stderr: - output.extend(p.stderr.splitlines()) - if stdout and p.stdout: - output.extend(p.stdout.splitlines()) - - return retcode, output - - def tag_visible(element): if element.parent.name in ['style', 'script', 'head', 'title', 'meta', '[document]']: return False @@ -209,9 +126,8 @@ def check_socket(host, port): ################################################################################################### # determine a list of available (non-virtual) adapters (Iface's) def get_available_adapters(): - available_adapters = [] - _, all_iface_list = run_process("find /sys/class/net/ -mindepth 1 -maxdepth 1 -type l -printf '%P %l\\n'") + _, all_iface_list = run_subprocess("find /sys/class/net/ -mindepth 1 -maxdepth 1 -type l -printf '%P %l\\n'") available_iface_list = [x.split(" ", 1)[0] for x in all_iface_list if 'virtual' not in x] # for each adapter, determine its MAC address and link speed @@ -245,7 +161,7 @@ def identify_adapter(adapter, duration=NIC_BLINK_SECONDS, background=False): stderr=subprocess.DEVNULL, ) else: - retCode, _ = run_process( + retCode, _ = run_subprocess( f"/sbin/ethtool --identify {adapter} {duration}", stdout=False, stderr=False, timeout=duration * 2 ) return retCode == 0 diff --git a/shared/bin/suricata_config_populate.py b/shared/bin/suricata_config_populate.py index d5d30406f..aee403487 100755 --- a/shared/bin/suricata_config_populate.py +++ b/shared/bin/suricata_config_populate.py @@ -27,6 +27,8 @@ from shutil import move as MoveFile, copyfile as CopyFile from subprocess import PIPE, Popen +from malcolm_utils import val2bool + ################################################################################################### args = None script_return_code = 0 @@ -38,129 +40,6 @@ YAML_VERSION = (1, 1) BACKUP_FILES_MAX = 10 -################################################################################################### -def val2bool(v): - try: - if v is None: - return False - elif isinstance(v, bool): - return v - elif isinstance(v, str): - if v.lower() in ("yes", "true", "t", "y"): - return True - elif v.lower() in ("no", "false", "f", "n"): - return False - else: - raise ValueError(f'Boolean value expected (got {v})') - else: - raise ValueError(f'Boolean value expected (got {v})') - except: - # just pitch it back and let the caller worry about it - return v - - -################################################################################################### -@contextlib.contextmanager -def pushd(directory): - prevDir = os.getcwd() - os.chdir(directory) - try: - yield - finally: - os.chdir(prevDir) - - -################################################################################################### -def get_iterable(x): - if isinstance(x, Iterable) and not isinstance(x, str): - return x - else: - return (x,) - - -################################################################################################### -def deep_get(d, keys, default=None): - k = get_iterable(keys) - if d is None: - return default - if not keys: - return d - return deep_get(d.get(k[0]), k[1:], default) - - -################################################################################################### -def deep_set(d, keys, value, deleteIfNone=False): - k = get_iterable(keys) - for key in k[:-1]: - if (key not in d) or (not isinstance(d[key], dict)): - d[key] = dict() - d = d[key] - d[k[-1]] = value - if (deleteIfNone == True) and (value is None): - d.pop(k[-1], None) - - -################################################################################################### -# run command with arguments and return its exit code, stdout, and stderr -def check_output_input(*popenargs, **kwargs): - - if 'stdout' in kwargs: - raise ValueError('stdout argument not allowed, it will be overridden') - - if 'stderr' in kwargs: - raise ValueError('stderr argument not allowed, it will be overridden') - - if 'input' in kwargs and kwargs['input']: - if 'stdin' in kwargs: - raise ValueError('stdin and input arguments may not both be used') - inputdata = kwargs['input'] - kwargs['stdin'] = PIPE - else: - inputdata = None - kwargs.pop('input', None) - - process = Popen(*popenargs, stdout=PIPE, stderr=PIPE, **kwargs) - try: - output, errput = process.communicate(input=inputdata) - except: - process.kill() - process.wait() - raise - - retcode = process.poll() - - return retcode, output, errput - - -################################################################################################### -# run command with arguments and return its exit code and output -def run_process(command, stdout=True, stderr=True, stdin=None, cwd=None, env=None): - - retcode = -1 - output = [] - - try: - # run the command - retcode, cmdout, cmderr = check_output_input(command, input=stdin.encode() if stdin else None, cwd=cwd, env=env) - - # split the output on newlines to return a list - if stderr and (len(cmderr) > 0): - output.extend(cmderr.decode(sys.getdefaultencoding()).split('\n')) - if stdout and (len(cmdout) > 0): - output.extend(cmdout.decode(sys.getdefaultencoding()).split('\n')) - - except (FileNotFoundError, OSError, IOError) as e: - if stderr: - output.append("Command {} not found or unable to execute".format(command)) - - logging.debug( - "{}{} returned {}: {}".format( - command, "({})".format(stdin[:80] + bool(stdin[80:]) * '...' if stdin else ""), retcode, output - ) - ) - - return retcode, output - ################################################################################################### # run command with arguments and return its exit code and output @@ -641,6 +520,7 @@ def ObjToYamlStrLines(obj, options=None): } ) + ################################################################################################### def GetRuleSources(requireRulesExist=False): global DEFAULT_VARS @@ -844,7 +724,6 @@ def main(): # while we're here, configure the eve-log section of outputs if name == 'eve-log': - # enable community-id for easier cross-referencing and pcap-file for # tying back to the original PCAP filename cfg['outputs'][outputIdx][name]['community-id'] = True @@ -852,7 +731,6 @@ def main(): # configure the various different output types belonging to eve-log if 'types' in cfg['outputs'][outputIdx][name]: - remainingTypes = set(list(PROTOCOL_CONFIGS.keys())) for dumperIdx in reversed(range(len(cfg['outputs'][outputIdx][name]['types']))): @@ -1218,7 +1096,9 @@ def main(): '-l', tmpLogDir, '-T', - ] + ], + debug=args.verbose > logging.DEBUG, + logger=logging, ) logging.info(f'suricata configuration test returned {script_return_code}') if script_return_code != 0: diff --git a/shared/bin/suricata_update_config_populate.py b/shared/bin/suricata_update_config_populate.py index b51afb146..cb72553a4 100755 --- a/shared/bin/suricata_update_config_populate.py +++ b/shared/bin/suricata_update_config_populate.py @@ -27,6 +27,8 @@ from shutil import move as MoveFile, copyfile as CopyFile from subprocess import PIPE, Popen +from malcolm_utils import val2bool + ################################################################################################### args = None script_return_code = 0 @@ -38,67 +40,6 @@ YAML_VERSION = (1, 1) BACKUP_FILES_MAX = 10 -################################################################################################### -def val2bool(v): - try: - if v is None: - return False - elif isinstance(v, bool): - return v - elif isinstance(v, str): - if v.lower() in ("yes", "true", "t", "y"): - return True - elif v.lower() in ("no", "false", "f", "n"): - return False - else: - raise ValueError(f'Boolean value expected (got {v})') - else: - raise ValueError(f'Boolean value expected (got {v})') - except: - # just pitch it back and let the caller worry about it - return v - - -################################################################################################### -@contextlib.contextmanager -def pushd(directory): - prevDir = os.getcwd() - os.chdir(directory) - try: - yield - finally: - os.chdir(prevDir) - - -################################################################################################### -def get_iterable(x): - if isinstance(x, Iterable) and not isinstance(x, str): - return x - else: - return (x,) - - -################################################################################################### -def deep_get(d, keys, default=None): - k = get_iterable(keys) - if d is None: - return default - if not keys: - return d - return deep_get(d.get(k[0]), k[1:], default) - - -################################################################################################### -def deep_set(d, keys, value, deleteIfNone=False): - k = get_iterable(keys) - for key in k[:-1]: - if (key not in d) or (not isinstance(d[key], dict)): - d[key] = dict() - d = d[key] - d[k[-1]] = value - if (deleteIfNone == True) and (value is None): - d.pop(k[-1], None) - ################################################################################################### # run command with arguments and return its exit code and output diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py new file mode 100644 index 000000000..080cdce03 --- /dev/null +++ b/shared/bin/watch_common.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import time + +from malcolm_utils import AtomicInt, ContextLockedOrderedDict + +from watchdog.events import ( + FileSystemEventHandler, + FileSystemEvent, + FileSystemMovedEvent, + FileMovedEvent, + DirMovedEvent, + FileModifiedEvent, + DirModifiedEvent, + FileCreatedEvent, + FileClosedEvent, + FileOpenedEvent, + DirCreatedEvent, + FileDeletedEvent, + DirDeletedEvent, +) + +from multiprocessing.pool import ThreadPool +from watchdog.utils import WatchdogShutdown +from watchdog.observers import Observer +from watchdog.observers.polling import PollingObserver + + +################################################################################################### +class FileOperationEventHandler(FileSystemEventHandler): + def __init__( + self, + logger, + polling, + *args, + **kwargs, + ): + super().__init__(*args, **kwargs) + self.polling = polling + # items at the first (idx=0) of this OrderedDict are the + # oldest, items at the last (idx=len-1) are the newest + self.deck = ContextLockedOrderedDict() + self.logger = logger + self.updateTime() + + def done(self): + return True + + def updateTime(self): + self.nowTime = int(time.time()) + + def on_any_event(self, event): + if not event.is_directory: + self.updateTime() + if self.logger: + self.logger.debug(f"{self.nowTime}: {event.event_type} {event.src_path}") + + def on_created(self, event): + self.on_modified(event) + + def on_modified(self, event): + # we only care about "created" and "modified" events if polling, + # otherwise "on_closed" will take care of us + if not event.is_directory and self.polling: + with self.deck as d: + d[event.src_path] = self.nowTime + d.move_to_end(event.src_path, last=True) + + def on_moved(self, event): + if not event.is_directory: + if isinstance(event, FileSystemMovedEvent) and same_file_or_dir( + os.path.dirname(event.src_path), os.path.dirname(event.dest_path) + ): + # a file was simply renamed in the watched directory (not moved + # from some other directory) so just update the filename + with self.deck as d: + d.pop(event.src_path, self.nowTime) + d[event.dest_path] = self.nowTime + d.move_to_end(event.dest_path, last=True) + else: + # the file was moved from somewhere else, treat it as a create + self.on_created(event) + + def on_closed(self, event): + # on_closed is only going to come from inotify events, not polling + # so we know we're good to go. set its time to expire immediately in the worker + if not event.is_directory: + with self.deck as d: + d[event.src_path] = 0 + d.move_to_end(event.src_path, last=False) + + def on_deleted(self, event): + # if a file is deleted I guess we don't need to track it any more + if not event.is_directory: + with self.deck as d: + d.pop(event.src_path, self.nowTime) + + +################################################################################################### +def ProcessFileEventWorker(workerArgs): + handler, observer, fileProcessor, assumeClosedSec, workerThreadCount, shutDown, logger = ( + workerArgs[0], + workerArgs[1], + workerArgs[2], + workerArgs[3], + workerArgs[4], + workerArgs[5], + workerArgs[6], + ) + + with workerThreadCount as workerId: + if logger is not None: + logger.debug(f"[{workerId}]:started") + + while (not shutDown[0]) and observer.is_alive(): + time.sleep(1) + nowTime = int(time.time()) + with handler.deck as d: + for fileName, eventTime in list(d.items()): + if nowTime < eventTime + assumeClosedSec: + # we can break because the list is ordered + break + else: + del d[fileName] + if fileProcessor is not None: + fileProcessor(fileName) + logger.debug(f"processed {fileName} at {(nowTime-eventTime) if (eventTime > 0) else 0} seconds") + + time.sleep(1) + if logger is not None: + logger.debug(f"[{workerId}]: finished") + + +def WatchAndProcessDirectory( + directories, + polling, + fileProcessor, + assumeClosedSec, + shuttingDown, + logger, +): + observer = PollingObserver() if polling else Observer() + handler = FileOperationEventHandler( + logger=logger, + polling=polling, + ) + for directory in directories: + if logger: + logger.debug(f"Scheduling {directory}") + observer.schedule(handler, directory, recursive=True) + + observer.start() + try: + workerThreadCount = AtomicInt(value=0) + workerThreads = ThreadPool( + 1, + ProcessFileEventWorker( + [ + handler, + observer, + fileProcessor, + assumeClosedSec, + workerThreadCount, + shuttingDown, + logger, + ], + ), + ) + + while (not shuttingDown[0]) and observer.is_alive(): + observer.join(1) + + if shuttingDown[0]: + raise WatchdogShutdown() + + except WatchdogShutdown as wdshut: + observer.unschedule_all() + + finally: + observer.stop() + observer.join() + + while workerThreadCount.value() > 0: + time.sleep(1) diff --git a/shared/bin/zeek_carve_logger.py b/shared/bin/zeek_carve_logger.py index 56b188f5b..9077c39cf 100755 --- a/shared/bin/zeek_carve_logger.py +++ b/shared/bin/zeek_carve_logger.py @@ -24,8 +24,12 @@ from collections import defaultdict from contextlib import nullcontext from datetime import datetime + from zeek_carve_utils import * +import malcolm_utils +from malcolm_utils import eprint, str2bool, AtomicInt, same_file_or_dir + ################################################################################################### debug = False verboseDebug = False @@ -37,6 +41,7 @@ origPath = os.getcwd() shuttingDown = False + ################################################################################################### # handle sigint/sigterm and set a global shutdown variable def shutdown_handler(signum, frame): @@ -60,15 +65,6 @@ def debug_toggle_handler(signum, frame): debugToggled = True -################################################################################################### -# -def same_file_or_dir(path1, path2): - try: - return os.path.samefile(path1, path2) - except: - return False - - ################################################################################################### # main def main(): @@ -235,7 +231,6 @@ def main(): print(f'#types\t{BroSignatureLine.signature_types_line()}', file=broSigFile, end='\n') while not shuttingDown: - if pdbFlagged: pdbFlagged = False breakpoint() @@ -251,7 +246,6 @@ def main(): eprint(f"{scriptName}:\t🕑\t(recv)") if isinstance(scanResult, dict): - # register/deregister scanners if FILE_SCAN_RESULT_SCANNER in scanResult: scanner = scanResult[FILE_SCAN_RESULT_SCANNER].lower() @@ -279,7 +273,6 @@ def main(): FILE_SCAN_RESULT_DESCRIPTION, ) ): - triggered = scanResult[FILE_SCAN_RESULT_HITS] > 0 fileName = scanResult[FILE_SCAN_RESULT_FILE] fileNameBase = os.path.basename(fileName) @@ -318,19 +311,16 @@ def main(): # finally, what to do with the file itself if os.path.isfile(fileName): - # once all of the scanners have had their turn... if fileScanCount >= len(scanners): fileScanCounts.pop(fileNameBase, None) fileScanHits.pop(fileNameBase, None) if (fileScanHitCount > 0) and (args.preserveMode != PRESERVE_NONE): - # move triggering file to quarantine if not same_file_or_dir( fileName, os.path.join(quarantineDir, fileNameBase) ): # unless it's somehow already there - try: shutil.move(fileName, quarantineDir) if debug: @@ -344,7 +334,6 @@ def main(): if not same_file_or_dir( quarantineDir, os.path.dirname(fileName) ): # don't move or delete if it's somehow already quarantined - if args.preserveMode == PRESERVE_ALL: # move non-triggering file to preserved directory try: diff --git a/shared/bin/zeek_carve_scanner.py b/shared/bin/zeek_carve_scanner.py index ae92ff304..aab0a3b73 100755 --- a/shared/bin/zeek_carve_scanner.py +++ b/shared/bin/zeek_carve_scanner.py @@ -20,9 +20,13 @@ import time import zmq -from zeek_carve_utils import * from multiprocessing.pool import ThreadPool +from zeek_carve_utils import * +import malcolm_utils +from malcolm_utils import eprint, str2bool, AtomicInt + + ################################################################################################### debug = False verboseDebug = False @@ -35,6 +39,7 @@ shuttingDown = False scanWorkersCount = AtomicInt(value=0) + ################################################################################################### # handle sigint/sigterm and set a global shutdown variable def shutdown_handler(signum, frame): @@ -71,7 +76,6 @@ def locate_file(fileInfo): fileName = None if fileName is not None: - if os.path.isfile(fileName): return fileName @@ -103,7 +107,6 @@ def scanFileWorker(checkConnInfo, carvedFileSub): try: if isinstance(checkConnInfo, FileScanProvider): - # initialize ZeroMQ context and socket(s) to send scan results context = zmq.Context() @@ -121,7 +124,6 @@ def scanFileWorker(checkConnInfo, carvedFileSub): # loop forever, or until we're told to shut down while not shuttingDown: - # "register" this scanner with the logger while (not scannerRegistered) and (not shuttingDown): try: @@ -153,7 +155,6 @@ def scanFileWorker(checkConnInfo, carvedFileSub): fileName = locate_file(fileInfo) if (fileName is not None) and os.path.isfile(fileName): - # file exists, submit for scanning if debug: eprint(f"{scriptName}[{scanWorkerId}]:\t🔎\t{json.dumps(fileInfo)}") @@ -190,13 +191,11 @@ def scanFileWorker(checkConnInfo, carvedFileSub): # todo: maximum time we wait for a single file to be scanned? while (not requestComplete) and (not shuttingDown): - # wait a moment then check to see if the scan is complete time.sleep(scan.provider.check_interval()) response = scan.provider.check_result(scan.submissionResponse) if isinstance(response, AnalyzerResult): - # whether the scan has completed requestComplete = response.finished diff --git a/shared/bin/zeek_carve_utils.py b/shared/bin/zeek_carve_utils.py index 28e8b3af0..bfa87acae 100644 --- a/shared/bin/zeek_carve_utils.py +++ b/shared/bin/zeek_carve_utils.py @@ -4,7 +4,6 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. import clamd -import hashlib import json import os import re @@ -25,6 +24,8 @@ from threading import get_ident from threading import Lock +from malcolm_utils import eprint, sha256sum, run_process + ################################################################################################### VENTILATOR_PORT = 5987 SINK_PORT = 5988 @@ -235,44 +236,6 @@ def __init__(self, source=None, fid=None, uid=None, time=None, ext=None): self.ext = ext -################################################################################################### -# convenient boolean argument parsing -def str2bool(v): - if v.lower() in ('yes', 'true', 't', 'y', '1'): - return True - elif v.lower() in ('no', 'false', 'f', 'n', '0'): - return False - else: - raise argparse.ArgumentTypeError('Boolean value expected.') - - -################################################################################################### -# print to stderr -def eprint(*args, **kwargs): - print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), *args, file=sys.stderr, **kwargs) - - -################################################################################################### -# calculate a sha256 hash of a file -def sha256sum(filename): - h = hashlib.sha256() - b = bytearray(64 * 1024) - mv = memoryview(b) - with open(filename, 'rb', buffering=0) as f: - for n in iter(lambda: f.readinto(mv), 0): - h.update(mv[:n]) - return h.hexdigest() - - -################################################################################################### -# recursive dictionary key search -def dictsearch(d, target): - val = filter( - None, [[b] if a == target else dictsearch(b, target) if isinstance(b, dict) else None for a, b in d.items()] - ) - return [i for b in val for i in b] - - ################################################################################################### # filespec to various fields as per the extractor zeek script (/opt/zeek/share/zeek/site/extractor.zeek) # source-fuid-uid-time.ext @@ -310,95 +273,6 @@ def extracted_filespec_to_fields(filespec): return result -################################################################################################### -# open a file and close it, updating its access time -def touch(filename): - open(filename, 'a').close() - os.utime(filename, None) - - -################################################################################################### -# run command with arguments and return its exit code, stdout, and stderr -def check_output_input(*popenargs, **kwargs): - if 'stdout' in kwargs: - raise ValueError('stdout argument not allowed, it will be overridden') - - if 'stderr' in kwargs: - raise ValueError('stderr argument not allowed, it will be overridden') - - if 'input' in kwargs and kwargs['input']: - if 'stdin' in kwargs: - raise ValueError('stdin and input arguments may not both be used') - inputdata = kwargs['input'] - kwargs['stdin'] = PIPE - else: - inputdata = None - kwargs.pop('input', None) - - process = Popen(*popenargs, stdout=PIPE, stderr=PIPE, **kwargs) - try: - output, errput = process.communicate(input=inputdata) - except: - process.kill() - process.wait() - raise - - retcode = process.poll() - - return retcode, output, errput - - -################################################################################################### -# run command with arguments and return its exit code and output -def run_process(command, stdout=True, stderr=True, stdin=None, cwd=None, env=None, debug=False): - retcode = -1 - output = [] - - try: - # run the command - retcode, cmdout, cmderr = check_output_input(command, input=stdin.encode() if stdin else None, cwd=cwd, env=env) - - # split the output on newlines to return a list - if stderr and (len(cmderr) > 0): - output.extend(cmderr.decode(sys.getdefaultencoding()).split('\n')) - if stdout and (len(cmdout) > 0): - output.extend(cmdout.decode(sys.getdefaultencoding()).split('\n')) - - except (FileNotFoundError, OSError, IOError) as e: - if stderr: - output.append("Command {} not found or unable to execute".format(command)) - - if debug: - eprint( - "{}{} returned {}: {}".format( - command, "({})".format(stdin[:80] + bool(stdin[80:]) * '...' if stdin else ""), retcode, output - ) - ) - - return retcode, output - - -################################################################################################### -class AtomicInt: - def __init__(self, value=0): - self.val = RawValue('i', value) - self.lock = Lock() - - def increment(self): - with self.lock: - self.val.value += 1 - return self.val.value - - def decrement(self): - with self.lock: - self.val.value -= 1 - return self.val.value - - def value(self): - with self.lock: - return self.val.value - - ################################################################################################### class CarvedFileSubscriberThreaded: # --------------------------------------------------------------------------------- @@ -429,7 +303,10 @@ def __init__( self.newFilesSocket.setsockopt(zmq.SUBSCRIBE, bytes(topic, encoding='ascii')) self.newFilesSocket.RCVTIMEO = rcvTimeout if self.debug: - eprint(f"{self.scriptName}:\tbound to ventilator at {port}") + eprint( + f"{self.scriptName}:\tbound to ventilator at {port}", + timestamp=True, + ) # --------------------------------------------------------------------------------- def Pull(self, scanWorkerId=0): @@ -445,7 +322,8 @@ def Pull(self, scanWorkerId=0): if self.verboseDebug: eprint( - f"{self.scriptName}[{scanWorkerId}]:\t{'📨' if (FILE_SCAN_RESULT_FILE in fileinfo) else '🕑'}\t{fileinfo[FILE_SCAN_RESULT_FILE] if (FILE_SCAN_RESULT_FILE in fileinfo) else '(recv)'}" + f"{self.scriptName}[{scanWorkerId}]:\t{'📨' if (FILE_SCAN_RESULT_FILE in fileinfo) else '🕑'}\t{fileinfo[FILE_SCAN_RESULT_FILE] if (FILE_SCAN_RESULT_FILE in fileinfo) else '(recv)'}", + timestamp=True, ) return fileinfo @@ -673,7 +551,10 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo while (not allowed) and (not clamavResult.finished): if not connected: if self.verboseDebug: - eprint(f"{get_ident()}: ClamAV attempting connection") + eprint( + f"{get_ident()}: ClamAV attempting connection", + timestamp=True, + ) clamAv = ( clamd.ClamdUnixSocket(path=self.socketFileName) if self.socketFileName is not None @@ -683,11 +564,17 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo clamAv.ping() connected = True if self.verboseDebug: - eprint(f"{get_ident()}: ClamAV connected!") + eprint( + f"{get_ident()}: ClamAV connected!", + timestamp=True, + ) except Exception as e: connected = False if self.debug: - eprint(f"{get_ident()}: ClamAV connection failed: {str(e)}") + eprint( + f"{get_ident()}: ClamAV connection failed: {str(e)}", + timestamp=True, + ) if connected: # first make sure we haven't exceeded rate limits @@ -700,17 +587,26 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo if connected and allowed: try: if self.verboseDebug: - eprint(f'{get_ident()} ClamAV scanning: {fileName}') + eprint( + f'{get_ident()} ClamAV scanning: {fileName}', + timestamp=True, + ) clamavResult.result = clamAv.scan(fileName) if self.verboseDebug: - eprint(f'{get_ident()} ClamAV scan result: {clamavResult.result}') + eprint( + f'{get_ident()} ClamAV scan result: {clamavResult.result}', + timestamp=True, + ) clamavResult.success = clamavResult.result is not None clamavResult.finished = True except Exception as e: if clamavResult.result is None: clamavResult.result = str(e) if self.debug: - eprint(f'{get_ident()} ClamAV scan error: {clamavResult.result}') + eprint( + f'{get_ident()} ClamAV scan error: {clamavResult.result}', + timestamp=True, + ) finally: self.scanningFilesCount.decrement() @@ -794,11 +690,20 @@ def __init__(self, debug=False, verboseDebug=False, rulesDirs=[], reqLimit=None) self.ruleFilespecs[filename] = filename except yara.SyntaxError as e: if self.debug: - eprint(f'{get_ident()} Ignored Yara compile error in {filename}: {e}') + eprint( + f'{get_ident()} Ignored Yara compile error in {filename}: {e}', + timestamp=True, + ) if self.verboseDebug: - eprint(f"{get_ident()}: Initializing Yara with {len(self.ruleFilespecs)} rules files: {self.ruleFilespecs}") + eprint( + f"{get_ident()}: Initializing Yara with {len(self.ruleFilespecs)} rules files: {self.ruleFilespecs}", + timestamp=True, + ) elif self.debug: - eprint(f"{get_ident()}: Initializing Yara with {len(self.ruleFilespecs)} rules files") + eprint( + f"{get_ident()}: Initializing Yara with {len(self.ruleFilespecs)} rules files", + timestamp=True, + ) self.compiledRules = yara.compile(filepaths=self.ruleFilespecs) @staticmethod @@ -834,10 +739,16 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo if allowed: try: if self.verboseDebug: - eprint(f'{get_ident()} Yara scanning: {fileName}') + eprint( + f'{get_ident()} Yara scanning: {fileName}', + timestamp=True, + ) yaraResult.result = self.compiledRules.match(fileName, timeout=YARA_RUN_TIMEOUT_SEC) if self.verboseDebug: - eprint(f'{get_ident()} Yara scan result: {yaraResult.result}') + eprint( + f'{get_ident()} Yara scan result: {yaraResult.result}', + timestamp=True, + ) yaraResult.success = yaraResult.result is not None yaraResult.finished = True except Exception as e: @@ -846,7 +757,10 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo yaraResult.success = False yaraResult.finished = True if self.debug: - eprint(f'{get_ident()} Yara scan error: {yaraResult.result}') + eprint( + f'{get_ident()} Yara scan error: {yaraResult.result}', + timestamp=True, + ) finally: self.scanningFilesCount.decrement() @@ -950,7 +864,10 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo if allowed: try: if self.verboseDebug: - eprint(f'{get_ident()} Capa scanning: {fileName}') + eprint( + f'{get_ident()} Capa scanning: {fileName}', + timestamp=True, + ) if self.rulesDir is not None: cmd = [ @@ -997,7 +914,10 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo capaResult.result = {"error": str(capaErr)} if self.verboseDebug: - eprint(f'{get_ident()} Capa scan result: {capaResult.result}') + eprint( + f'{get_ident()} Capa scan result: {capaResult.result}', + timestamp=True, + ) capaResult.success = capaResult.result is not None capaResult.finished = True @@ -1005,7 +925,10 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo if capaResult.result is None: capaResult.result = str(e) if self.debug: - eprint(f'{get_ident()} Capa scan error: {capaResult.result}') + eprint( + f'{get_ident()} Capa scan error: {capaResult.result}', + timestamp=True, + ) finally: self.scanningFilesCount.decrement() diff --git a/shared/bin/zeek_carve_watcher.py b/shared/bin/zeek_carve_watcher.py index c1cf3a70c..70504f40f 100755 --- a/shared/bin/zeek_carve_watcher.py +++ b/shared/bin/zeek_carve_watcher.py @@ -24,6 +24,8 @@ from zeek_carve_utils import * +from malcolm_utils import touch + ################################################################################################### MINIMUM_CHECKED_FILE_SIZE_DEFAULT = 64 MAXIMUM_CHECKED_FILE_SIZE_DEFAULT = 134217728 @@ -38,10 +40,10 @@ origPath = os.getcwd() shuttingDown = False + ################################################################################################### # watch files written to and moved to this directory class EventWatcher(pyinotify.ProcessEvent): - # notify on files written in-place then closed (IN_CLOSE_WRITE), and moved into this directory (IN_MOVED_TO) _methods = ["IN_CLOSE_WRITE", "IN_MOVED_TO"] @@ -70,10 +72,8 @@ def __init__(self): ################################################################################################### # set up event processor to append processed events from to the event queue def event_process_generator(cls, method): - # actual method called when we are notified of a file def _method_name(self, event): - global args global debug global verboseDebug @@ -82,10 +82,8 @@ def _method_name(self, event): eprint(f"{scriptName}:\t👓\t{event.pathname}") if (not event.dir) and os.path.isfile(event.pathname): - fileSize = os.path.getsize(event.pathname) if args.minBytes <= fileSize <= args.maxBytes: - fileType = magic.from_file(event.pathname, mime=True) if (pathlib.Path(event.pathname).suffix != CAPA_VIV_SUFFIX) and (fileType != CAPA_VIV_MIME): # the entity is a right-sized file, is not a capa .viv cache file, and it exists, so send it to get scanned diff --git a/shared/bin/zeek_carved_http_server.py b/shared/bin/zeek_carved_http_server.py index 93380e59b..7ed255a5d 100755 --- a/shared/bin/zeek_carved_http_server.py +++ b/shared/bin/zeek_carved_http_server.py @@ -15,9 +15,8 @@ from http.server import HTTPServer, SimpleHTTPRequestHandler from Crypto.Cipher import AES -KEY_SIZE = 32 -OPENSSL_ENC_MAGIC = b'Salted__' -PKCS5_SALT_LEN = 8 + +from malcolm_utils import str2bool, eprint, EVP_KEY_SIZE, PKCS5_SALT_LEN, OPENSSL_ENC_MAGIC, EVP_BytesToKey ################################################################################################### args = None @@ -26,64 +25,10 @@ script_path = os.path.dirname(os.path.realpath(__file__)) orig_path = os.getcwd() -################################################################################################### -# print to stderr -def eprint(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - sys.stderr.flush() - - -################################################################################################### -# convenient boolean argument parsing -def str2bool(v): - if v.lower() in ('yes', 'true', 't', 'y', '1'): - return True - elif v.lower() in ('no', 'false', 'f', 'n', '0'): - return False - else: - raise argparse.ArgumentTypeError('Boolean value expected.') - - -################################################################################################### -# EVP_BytesToKey -# -# reference: https://github.com/openssl/openssl/blob/6f0ac0e2f27d9240516edb9a23b7863e7ad02898/crypto/evp/evp_key.c#L74 -# https://gist.github.com/chrono-meter/d122cbefc6f6248a0af554995f072460 -def EVP_BytesToKey(key_length: int, iv_length: int, md, salt: bytes, data: bytes, count: int = 1) -> (bytes, bytes): - assert data - assert salt == b'' or len(salt) == PKCS5_SALT_LEN - - md_buf = b'' - key = b'' - iv = b'' - addmd = 0 - - while key_length > len(key) or iv_length > len(iv): - c = md() - if addmd: - c.update(md_buf) - addmd += 1 - c.update(data) - c.update(salt) - md_buf = c.digest() - for i in range(1, count): - md_buf = md(md_buf) - - md_buf2 = md_buf - - if key_length > len(key): - key, md_buf2 = key + md_buf2[: key_length - len(key)], md_buf2[key_length - len(key) :] - - if iv_length > len(iv): - iv = iv + md_buf2[: iv_length - len(iv)] - - return key, iv - ################################################################################################### # class HTTPHandler(SimpleHTTPRequestHandler): - # return full path based on server base path and requested path def translate_path(self, path): path = SimpleHTTPRequestHandler.translate_path(self, path) @@ -110,7 +55,7 @@ def do_GET(self): self.send_header('Content-Disposition', f'attachment; filename={os.path.basename(fullpath)}.encrypted') self.end_headers() salt = os.urandom(PKCS5_SALT_LEN) - key, iv = EVP_BytesToKey(KEY_SIZE, AES.block_size, hashlib.sha256, salt, args.key.encode('utf-8')) + key, iv = EVP_BytesToKey(EVP_KEY_SIZE, AES.block_size, hashlib.sha256, salt, args.key.encode('utf-8')) cipher = AES.new(key, AES.MODE_CBC, iv) encrypted = b"" encrypted += OPENSSL_ENC_MAGIC diff --git a/shared/bin/zeek_intel_from_threat_feed.py b/shared/bin/zeek_intel_from_threat_feed.py index f69d99c2f..3ab73ce13 100755 --- a/shared/bin/zeek_intel_from_threat_feed.py +++ b/shared/bin/zeek_intel_from_threat_feed.py @@ -13,12 +13,14 @@ import logging import os import sys +import malcolm_utils import zeek_threat_feed_utils ################################################################################################### script_name = os.path.basename(__file__) script_path = os.path.dirname(os.path.realpath(__file__)) + ################################################################################################### # main def main(): @@ -154,7 +156,7 @@ def main(): elif '://' in infileArg: # download from URL and read input from remote file - with zeek_threat_feed_utils.temporary_filename(suffix='.txt') as tmpFileName: + with malcolm_utils.temporary_filename(suffix='.txt') as tmpFileName: dlFileName = zeek_threat_feed_utils.download_to_file( infileArg, local_filename=tmpFileName, @@ -177,7 +179,7 @@ def main(): # we'll queue and then process all of the input arguments in workers inputQueue = deque() inputQueue.extend(args.input) - workerThreadCount = zeek_threat_feed_utils.AtomicInt(value=0) + workerThreadCount = malcolm_utils.AtomicInt(value=0) workerThreads = ThreadPool( args.threads, zeek_threat_feed_utils.ProcessThreatInputWorker, diff --git a/shared/bin/zeek_threat_feed_utils.py b/shared/bin/zeek_threat_feed_utils.py index 954ad25a8..664581a08 100644 --- a/shared/bin/zeek_threat_feed_utils.py +++ b/shared/bin/zeek_threat_feed_utils.py @@ -6,7 +6,6 @@ # - BSD 3-Clause license: https://github.com/tenzir/threatbus/blob/master/COPYING # - Zeek Plugin: https://github.com/tenzir/threatbus/blob/master/COPYING -from base64 import b64decode from bs4 import BeautifulSoup from collections import defaultdict from collections.abc import Iterable @@ -27,7 +26,6 @@ from taxii2client.v21 import as_pages as TaxiiAsPages_v21 from taxii2client.v21 import Collection as TaxiiCollection_v21 from taxii2client.v21 import Server as TaxiiServer_v21 -from tempfile import NamedTemporaryFile from threading import Lock from time import sleep, mktime from typing import Tuple, Union @@ -37,6 +35,7 @@ import re import requests +from malcolm_utils import base64_decode_if_prefixed, LoadStrIfJson, LoadFileIfJson # keys for dict returned by map_stix_indicator_to_zeek for Zeek intel file fields ZEEK_INTEL_INDICATOR = 'indicator' @@ -113,38 +112,6 @@ } -def base64_decode_if_prefixed(s: str): - if s.startswith('base64:'): - return b64decode(s[7:]).decode('utf-8') - else: - return s - - -def LoadStrIfJson(jsonStr): - try: - return json.loads(jsonStr) - except ValueError as e: - return None - - -def LoadFileIfJson(fileHandle): - try: - return json.load(fileHandle) - except ValueError as e: - return None - - -@contextmanager -def temporary_filename(suffix=None): - try: - f = tempfile.NamedTemporaryFile(suffix=suffix, delete=False) - tmp_name = f.name - f.close() - yield tmp_name - finally: - os.unlink(tmp_name) - - # get URL directory listing def get_url_paths_from_response(response_text, parent_url='', ext=''): soup = BeautifulSoup(response_text, 'html.parser') @@ -225,7 +192,6 @@ def is_stix_point_equality_ioc(indicator_type: type, pattern_str: str, logger=No """ try: if pattern := stix_pattern_from_str(indicator_type, pattern_str): - # InspectionListener https://github.com/oasis-open/cti-pattern-validator/blob/e926d0a14adf88de08acb908a51db1f453c13647/stix2patterns/v21/inspector.py#L5 # E.g., pattern = "[domain-name:value = 'evil.com']" # => il = pattern_data(comparisons={'domain-name': [(['value'], '=', "'evil.com'")]}, observation_ops=set(), qualifiers=set()) @@ -273,7 +239,6 @@ def split_stix_object_path_and_value( for comparison in list(il.comparisons.keys()): for element in il.comparisons[comparison]: if isinstance(element, Iterable) and (len(element) == 3) and (element[1] in ('=', '==')): - # construct object path name, e.g.: # file:hashes.'SHA-1' # software:name @@ -334,7 +299,6 @@ def map_stix_indicator_to_zeek( results = [] for object_path, ioc_value in split_stix_object_path_and_value(type(indicator), indicator.pattern, logger): - # get matching Zeek intel type if not (zeek_type := STIX_ZEEK_INTEL_TYPE_MAP.get(object_path, None)): if logger is not None: @@ -420,7 +384,6 @@ def map_misp_attribute_to_zeek( # process type/value pairs for zeek_type, attribute_value in valTypePairs: - if zeek_type == "URL": # remove leading protocol, if any parsed = urlparse(attribute_value) @@ -457,32 +420,6 @@ def map_misp_attribute_to_zeek( return results -class AtomicInt: - def __init__(self, value=0): - self.val = RawValue('i', value) - self.lock = Lock() - - def increment(self): - with self.lock: - self.val.value += 1 - return self.val.value - - def decrement(self): - with self.lock: - self.val.value -= 1 - return self.val.value - - def value(self): - with self.lock: - return self.val.value - - def __enter__(self): - return self.increment() - - def __exit__(self, type, value, traceback): - return self.decrement() - - class FeedParserZeekPrinter(object): lock = None fields = [] @@ -538,7 +475,6 @@ def ProcessSTIX( # parse the STIX and process all "Indicator" objects for obj in STIXParse(toParse, allow_custom=True).objects: if type(obj).__name__ == "Indicator": - # map indicator object to Zeek value(s) if ((self.since is None) or (obj.created >= self.since) or (obj.modified >= self.since)) and ( vals := map_stix_indicator_to_zeek(indicator=obj, source=source, logger=self.logger) @@ -591,7 +527,6 @@ def ProcessMISP( certainty = None for attribute in event.attributes: - # map event attribute to Zeek value(s) if ( ((not hasattr(attribute, 'deleted')) or (attribute.deleted == False)) @@ -620,7 +555,6 @@ def ProcessMISP( def ProcessThreatInputWorker(threatInputWorkerArgs): - inputQueue, zeekPrinter, since, workerThreadCount, logger = ( threatInputWorkerArgs[0], threatInputWorkerArgs[1], @@ -641,9 +575,7 @@ def ProcessThreatInputWorker(threatInputWorkerArgs): sleep(1) else: try: - with open(inarg) if ((inarg is not None) and os.path.isfile(inarg)) else nullcontext() as infile: - if infile: ################################################################################## # JSON FILE (STIX or MISP) @@ -686,7 +618,6 @@ def ProcessThreatInputWorker(threatInputWorkerArgs): mispAuthKey = mispConnInfo[1] with requests.Session() as mispSession: - if mispAuthKey is not None: mispSession.headers.update({'Authorization': mispAuthKey}) @@ -849,7 +780,6 @@ def ProcessThreatInputWorker(threatInputWorkerArgs): else TaxiiCollection_v20(info['url'], user=taxiiUsername, password=taxiiPassword) ) try: - # loop over paginated results for envelope in ( TaxiiAsPages_v21( From 02a6d1e3392bd20ef387625ba69d93daaff0ae5e Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 31 Mar 2023 16:00:10 -0600 Subject: [PATCH 075/239] work in progress for some python refactoring --- shared/bin/malcolm_utils.py | 333 +++++++++++++++++++----------------- 1 file changed, 174 insertions(+), 159 deletions(-) diff --git a/shared/bin/malcolm_utils.py b/shared/bin/malcolm_utils.py index 36915da96..8c32fbcfc 100644 --- a/shared/bin/malcolm_utils.py +++ b/shared/bin/malcolm_utils.py @@ -15,47 +15,43 @@ from tempfile import NamedTemporaryFile from Crypto.Cipher import AES -################################################################################################### -# EVP_BytesToKey -# -# reference: https://github.com/openssl/openssl/blob/6f0ac0e2f27d9240516edb9a23b7863e7ad02898/crypto/evp/evp_key.c#L74 -# https://gist.github.com/chrono-meter/d122cbefc6f6248a0af554995f072460 -EVP_KEY_SIZE = 32 -OPENSSL_ENC_MAGIC = b'Salted__' -PKCS5_SALT_LEN = 8 +################################################################################################### +# urlencode each character of a string +def aggressive_url_encode(string): + return "".join("%{0:0>2}".format(format(ord(char), "x")) for char in string) -def EVP_BytesToKey(key_length: int, iv_length: int, md, salt: bytes, data: bytes, count: int = 1) -> (bytes, bytes): - assert data - assert salt == b'' or len(salt) == PKCS5_SALT_LEN - md_buf = b'' - key = b'' - iv = b'' - addmd = 0 +################################################################################################### +# atomic integer class and context manager +class AtomicInt: + def __init__(self, value=0): + self.val = RawValue('i', value) + self.lock = Lock() - while key_length > len(key) or iv_length > len(iv): - c = md() - if addmd: - c.update(md_buf) - addmd += 1 - c.update(data) - c.update(salt) - md_buf = c.digest() - for i in range(1, count): - md_buf = md(md_buf) + def increment(self): + with self.lock: + self.val.value += 1 + return self.val.value - md_buf2 = md_buf + def decrement(self): + with self.lock: + self.val.value -= 1 + return self.val.value - if key_length > len(key): - key, md_buf2 = key + md_buf2[: key_length - len(key)], md_buf2[key_length - len(key) :] + def value(self): + with self.lock: + return self.val.value - if iv_length > len(iv): - iv = iv + md_buf2[: iv_length - len(iv)] + def __enter__(self): + return self.increment() - return key, iv + def __exit__(self, type, value, traceback): + return self.decrement() +################################################################################################### +# if a string starts with 'base64:', decode it, otherwise return it as-is def base64_decode_if_prefixed(s: str): if s.startswith('base64:'): return b64decode(s[7:]).decode('utf-8') @@ -63,51 +59,24 @@ def base64_decode_if_prefixed(s: str): return s -def LoadStrIfJson(jsonStr): - try: - return json.loads(jsonStr) - except ValueError as e: - return None - - -def LoadFileIfJson(fileHandle): - try: - return json.load(fileHandle) - except ValueError as e: - return None - - -@contextlib.contextmanager -def temporary_filename(suffix=None): - try: - f = tempfile.NamedTemporaryFile(suffix=suffix, delete=False) - tmp_name = f.name - f.close() - yield tmp_name - finally: - os.unlink(tmp_name) - - ################################################################################################### -@contextlib.contextmanager -def pushd(directory): - prevDir = os.getcwd() - os.chdir(directory) - try: - yield - finally: - os.chdir(prevDir) +# an OrderedDict that locks itself and unlocks itself as a context manager +class ContextLockedOrderedDict(OrderedDict): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.lock = Lock() + def __enter__(self): + self.lock.acquire() + return self -################################################################################################### -def get_iterable(x): - if isinstance(x, Iterable) and not isinstance(x, str): - return x - else: - return (x,) + def __exit__(self, type, value, traceback): + self.lock.release() + return self ################################################################################################### +# convenience routine for deep-getting a value from a dictionary def deep_get(d, keys, default=None): k = get_iterable(keys) if d is None: @@ -118,6 +87,7 @@ def deep_get(d, keys, default=None): ################################################################################################### +# convenience routine for setting-getting a value into a dictionary def deep_set(d, keys, value, deleteIfNone=False): k = get_iterable(keys) for key in k[:-1]: @@ -130,10 +100,12 @@ def deep_set(d, keys, value, deleteIfNone=False): ################################################################################################### -# open a file and close it, updating its access time -def touch(filename): - open(filename, 'a').close() - os.utime(filename, None) +# recursive dictionary key search +def dictsearch(d, target): + val = filter( + None, [[b] if a == target else dictsearch(b, target) if isinstance(b, dict) else None for a, b in d.items()] + ) + return [i for b in val for i in b] ################################################################################################### @@ -148,60 +120,53 @@ def eprint(*args, **kwargs): ################################################################################################### -# urlencode each character of a string -def aggressive_url_encode(string): - return "".join("%{0:0>2}".format(format(ord(char), "x")) for char in string) +# EVP_BytesToKey +# +# reference: https://github.com/openssl/openssl/blob/6f0ac0e2f27d9240516edb9a23b7863e7ad02898/crypto/evp/evp_key.c#L74 +# https://gist.github.com/chrono-meter/d122cbefc6f6248a0af554995f072460 +EVP_KEY_SIZE = 32 +OPENSSL_ENC_MAGIC = b'Salted__' +PKCS5_SALT_LEN = 8 -################################################################################################### -# strip a prefix from the beginning of a string if needed -def remove_prefix(text, prefix): - if (len(prefix) > 0) and text.startswith(prefix): - return text[len(prefix) :] - else: - return text +def EVP_BytesToKey(key_length: int, iv_length: int, md, salt: bytes, data: bytes, count: int = 1) -> (bytes, bytes): + assert data + assert salt == b'' or len(salt) == PKCS5_SALT_LEN + md_buf = b'' + key = b'' + iv = b'' + addmd = 0 -################################################################################################### -# nice human-readable file sizes -def sizeof_fmt(num, suffix='B'): - for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: - if abs(num) < 1024.0: - return "%3.1f%s%s" % (num, unit, suffix) - num /= 1024.0 - return "%.1f%s%s" % (num, 'Yi', suffix) + while key_length > len(key) or iv_length > len(iv): + c = md() + if addmd: + c.update(md_buf) + addmd += 1 + c.update(data) + c.update(salt) + md_buf = c.digest() + for i in range(1, count): + md_buf = md(md_buf) + md_buf2 = md_buf -################################################################################################### -# convenient boolean argument parsing -def str2bool(v): - if v.lower() in ('yes', 'true', 't', 'y', '1'): - return True - elif v.lower() in ('no', 'false', 'f', 'n', '0'): - return False - else: - raise ValueError('Boolean value expected.') + if key_length > len(key): + key, md_buf2 = key + md_buf2[: key_length - len(key)], md_buf2[key_length - len(key) :] + + if iv_length > len(iv): + iv = iv + md_buf2[: iv_length - len(iv)] + + return key, iv ################################################################################################### -def val2bool(v): - try: - if v is None: - return False - elif isinstance(v, bool): - return v - elif isinstance(v, str): - if v.lower() in ("yes", "true", "t", "y"): - return True - elif v.lower() in ("no", "false", "f", "n"): - return False - else: - raise ValueError(f'Boolean value expected (got {v})') - else: - raise ValueError(f'Boolean value expected (got {v})') - except: - # just pitch it back and let the caller worry about it - return v +# return just about any object as an iterable +def get_iterable(x): + if isinstance(x, Iterable) and not isinstance(x, str): + return x + else: + return (x,) ################################################################################################### @@ -230,6 +195,47 @@ def isipaddress(value): ################################################################################################### +# attempt to decode a string as JSON, returning the object if it decodes and None otherwise +def LoadStrIfJson(jsonStr): + try: + return json.loads(jsonStr) + except ValueError as e: + return None + + +################################################################################################### +# attempt to decode a file (given by handle) as JSON, returning the object if it decodes and +# None otherwise +def LoadFileIfJson(fileHandle): + try: + return json.load(fileHandle) + except ValueError as e: + return None + + +################################################################################################### +# a context manager for entering a directory and leaving it upon leaving the context +@contextlib.contextmanager +def pushd(directory): + prevDir = os.getcwd() + os.chdir(directory) + try: + yield + finally: + os.chdir(prevDir) + + +################################################################################################### +# strip a prefix from the beginning of a string if needed +def remove_prefix(text, prefix): + if (len(prefix) > 0) and text.startswith(prefix): + return text[len(prefix) :] + else: + return text + + +################################################################################################### +# return true if os.path.samefile, also False on exception def same_file_or_dir(path1, path2): try: return os.path.samefile(path1, path2) @@ -250,56 +256,65 @@ def sha256sum(filename): ################################################################################################### -# recursive dictionary key search -def dictsearch(d, target): - val = filter( - None, [[b] if a == target else dictsearch(b, target) if isinstance(b, dict) else None for a, b in d.items()] - ) - return [i for b in val for i in b] +# nice human-readable file sizes +def sizeof_fmt(num, suffix='B'): + for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: + if abs(num) < 1024.0: + return "%3.1f%s%s" % (num, unit, suffix) + num /= 1024.0 + return "%.1f%s%s" % (num, 'Yi', suffix) ################################################################################################### -# atomic integer class and context manager -class AtomicInt: - def __init__(self, value=0): - self.val = RawValue('i', value) - self.lock = Lock() - - def increment(self): - with self.lock: - self.val.value += 1 - return self.val.value - - def decrement(self): - with self.lock: - self.val.value -= 1 - return self.val.value - - def value(self): - with self.lock: - return self.val.value +# convenient boolean argument parsing +def str2bool(v): + if v.lower() in ('yes', 'true', 't', 'y', '1'): + return True + elif v.lower() in ('no', 'false', 'f', 'n', '0'): + return False + else: + raise ValueError('Boolean value expected.') - def __enter__(self): - return self.increment() - def __exit__(self, type, value, traceback): - return self.decrement() +################################################################################################### +# a context manager returning a temporary filename which is deleted upon leaving the context +@contextlib.contextmanager +def temporary_filename(suffix=None): + try: + f = tempfile.NamedTemporaryFile(suffix=suffix, delete=False) + tmp_name = f.name + f.close() + yield tmp_name + finally: + os.unlink(tmp_name) ################################################################################################### -# atomic integer class and context manager -class ContextLockedOrderedDict(OrderedDict): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.lock = Lock() +# open a file and close it, updating its access time +def touch(filename): + open(filename, 'a').close() + os.utime(filename, None) - def __enter__(self): - self.lock.acquire() - return self - def __exit__(self, type, value, traceback): - self.lock.release() - return self +################################################################################################### +def val2bool(v): + try: + if v is None: + return False + elif isinstance(v, bool): + return v + elif isinstance(v, str): + if v.lower() in ("yes", "true", "t", "y"): + return True + elif v.lower() in ("no", "false", "f", "n"): + return False + else: + raise ValueError(f'Boolean value expected (got {v})') + else: + raise ValueError(f'Boolean value expected (got {v})') + except: + # just pitch it back and let the caller worry about it + return v ################################################################################################### From e35edd840a2be152db145c24ee6cf0302bd0f258 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 3 Apr 2023 11:56:08 -0600 Subject: [PATCH 076/239] major refactoring work and checking flake output --- .dockerignore | 1 + Dockerfiles/api.Dockerfile | 6 +- Dockerfiles/arkime.Dockerfile | 5 +- Dockerfiles/dashboards-helper.Dockerfile | 2 +- Dockerfiles/dashboards.Dockerfile | 2 +- Dockerfiles/file-monitor.Dockerfile | 1 + Dockerfiles/filebeat.Dockerfile | 2 +- Dockerfiles/logstash.Dockerfile | 2 +- Dockerfiles/pcap-monitor.Dockerfile | 2 +- Dockerfiles/suricata.Dockerfile | 1 + Dockerfiles/zeek.Dockerfile | 1 + api/project/__init__.py | 4 +- arkime/scripts/docker_entrypoint.sh | 2 +- dashboards/scripts/docker_entrypoint.sh | 2 +- dashboards/scripts/index-refresh.py | 10 +- docs/ubuntu-install-example.md | 2 +- logstash/scripts/logstash-start.sh | 4 +- malcolm-iso/build.sh | 1 + scripts/control.py | 23 +- scripts/install.py | 26 +- scripts/malcolm_appliance_packager.sh | 2 + scripts/malcolm_common.py | 303 +----------------- {shared/bin => scripts}/malcolm_utils.py | 235 ++++++++++++-- shared/bin/configure-capture.py | 31 +- shared/bin/configure-interfaces.py | 34 +- shared/bin/ics-oui-parse.py | 8 +- shared/bin/opensearch_index_size_prune.py | 13 +- shared/bin/opensearch_read_only.py | 9 +- shared/bin/pcap_processor.py | 22 +- shared/bin/pcap_watcher.py | 21 +- shared/bin/sensor-capture-disk-config.py | 44 ++- shared/bin/sensorcommon.py | 20 +- shared/bin/suricata_config_populate.py | 12 +- shared/bin/suricata_update_config_populate.py | 3 +- shared/bin/watch_common.py | 6 +- shared/bin/zeek_carve_logger.py | 21 +- shared/bin/zeek_carve_scanner.py | 39 ++- shared/bin/zeek_carve_utils.py | 22 +- shared/bin/zeek_carve_watcher.py | 15 +- shared/bin/zeek_carved_http_server.py | 8 +- shared/bin/zeek_intel_from_threat_feed.py | 7 +- shared/bin/zeek_threat_feed_utils.py | 11 +- 42 files changed, 486 insertions(+), 499 deletions(-) rename {shared/bin => scripts}/malcolm_utils.py (65%) diff --git a/.dockerignore b/.dockerignore index 284ba36f7..bd42311d6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -32,6 +32,7 @@ pcap _site scripts !scripts/malcolm_common.py +!scripts/malcolm_utils.py zeek-logs suricata-logs netbox/netbox/media diff --git a/Dockerfiles/api.Dockerfile b/Dockerfiles/api.Dockerfile index 894b0ad60..4018b2931 100644 --- a/Dockerfiles/api.Dockerfile +++ b/Dockerfiles/api.Dockerfile @@ -12,11 +12,11 @@ RUN apt-get update -q \ && python3 -m pip install flake8 COPY ./api /usr/src/app/ -COPY scripts/malcolm_common.py /usr/src/app/ +COPY scripts/malcolm_utils.py /usr/src/app/ WORKDIR /usr/src/app RUN python3 -m pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt \ - && flake8 --ignore=E501,F401,W503 + && flake8 --ignore=E203,E501,F401,W503 FROM python:3-slim @@ -70,7 +70,7 @@ WORKDIR "${APP_HOME}" COPY --from=builder /usr/src/app/wheels /wheels COPY --from=builder /usr/src/app/requirements.txt . COPY ./api "${APP_HOME}" -COPY scripts/malcolm_common.py "${APP_HOME}"/ +COPY scripts/malcolm_utils.py "${APP_HOME}"/ COPY shared/bin/opensearch_status.sh "${APP_HOME}"/ COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index 1b0232b8c..b9026370d 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -168,7 +168,7 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l wget \ tini \ tar gzip unzip cpio bzip2 lzma xz-utils p7zip-full unrar zlib1g && \ - pip3 install --no-cache-dir beautifulsoup4 pyzmq && \ + pip3 install --no-cache-dir beautifulsoup4 pyzmq watchdog && \ ln -sfr $ARKIME_DIR/bin/npm /usr/local/bin/npm && \ ln -sfr $ARKIME_DIR/bin/node /usr/local/bin/node && \ ln -sfr $ARKIME_DIR/bin/npx /usr/local/bin/npx && \ @@ -184,7 +184,8 @@ COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic ADD arkime/scripts /opt/ ADD shared/bin/pcap_processor.py /opt/ ADD shared/bin/pcap_utils.py /opt/ -ADD scripts/malcolm_common.py /opt/ +ADD scripts/malcolm_utils.py /opt/ +ADD shared/bin/watch_common.py /opt/ ADD shared/bin/opensearch_status.sh /opt/ ADD shared/bin/self_signed_key_gen.sh /usr/local/bin/ ADD arkime/etc $ARKIME_DIR/etc/ diff --git a/Dockerfiles/dashboards-helper.Dockerfile b/Dockerfiles/dashboards-helper.Dockerfile index caf4a4eba..6b542570d 100644 --- a/Dockerfiles/dashboards-helper.Dockerfile +++ b/Dockerfiles/dashboards-helper.Dockerfile @@ -69,7 +69,7 @@ COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic COPY --chmod=755 shared/bin/opensearch_status.sh /data/ COPY --chmod=755 shared/bin/opensearch_index_size_prune.py /data/ COPY --chmod=755 shared/bin/opensearch_read_only.py /data/ -ADD scripts/malcolm_common.py /data/ +ADD scripts/malcolm_utils.py /data/ RUN apk update --no-cache && \ apk upgrade --no-cache && \ diff --git a/Dockerfiles/dashboards.Dockerfile b/Dockerfiles/dashboards.Dockerfile index 8db8f05a5..0c32d83aa 100644 --- a/Dockerfiles/dashboards.Dockerfile +++ b/Dockerfiles/dashboards.Dockerfile @@ -137,7 +137,7 @@ COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic COPY --chmod=755 dashboards/scripts/docker_entrypoint.sh /usr/local/bin/ ADD dashboards/opensearch_dashboards.yml /usr/share/opensearch-dashboards/config/opensearch_dashboards.orig.yml ADD dashboards/scripts/docker_entrypoint.sh /usr/local/bin/ -ADD scripts/malcolm_common.py /usr/local/bin/ +ADD scripts/malcolm_utils.py /usr/local/bin/ # Yeah, I know about https://opensearch.org/docs/latest/dashboards/branding ... but I can't figure out a way # to specify the entries in the opensearch_dashboards.yml such that they are valid BOTH from the diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index eb1f03543..861341022 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -205,6 +205,7 @@ COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic ADD shared/bin/zeek_carve*.py /usr/local/bin/ +ADD scripts/malcolm_utils.py /usr/local/bin/ ADD file-monitor/supervisord.conf /etc/supervisord.conf ADD file-monitor/docker-entrypoint.sh /docker-entrypoint.sh ADD file-monitor/*update.sh /usr/local/bin/ diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index d3ea91d10..302d67438 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -109,7 +109,7 @@ ADD filebeat/filebeat.yml /usr/share/filebeat/filebeat.yml ADD filebeat/filebeat-nginx.yml /usr/share/filebeat-nginx/filebeat-nginx.yml ADD filebeat/filebeat-tcp.yml /usr/share/filebeat-tcp/filebeat-tcp.yml ADD filebeat/scripts /usr/local/bin/ -ADD scripts/malcolm_common.py /usr/local/bin/ +ADD scripts/malcolm_utils.py /usr/local/bin/ ADD shared/bin/opensearch_status.sh /usr/local/bin/ ADD filebeat/supervisord.conf /etc/supervisord.conf RUN for INPUT in nginx tcp; do \ diff --git a/Dockerfiles/logstash.Dockerfile b/Dockerfiles/logstash.Dockerfile index 598a74789..bb4bbbc96 100644 --- a/Dockerfiles/logstash.Dockerfile +++ b/Dockerfiles/logstash.Dockerfile @@ -84,7 +84,7 @@ ADD logstash/pipelines/ /usr/share/logstash/malcolm-pipelines/ ADD logstash/patterns/ /usr/share/logstash/malcolm-patterns/ ADD logstash/ruby/ /usr/share/logstash/malcolm-ruby/ ADD logstash/scripts /usr/local/bin/ -ADD scripts/malcolm_common.py /usr/local/bin/ +ADD scripts/malcolm_utils.py /usr/local/bin/ ADD logstash/supervisord.conf /etc/supervisord.conf RUN bash -c "chmod --silent 755 /usr/local/bin/*.sh /usr/local/bin/*.py || true" && \ diff --git a/Dockerfiles/pcap-monitor.Dockerfile b/Dockerfiles/pcap-monitor.Dockerfile index 153569de0..151309e82 100644 --- a/Dockerfiles/pcap-monitor.Dockerfile +++ b/Dockerfiles/pcap-monitor.Dockerfile @@ -71,7 +71,7 @@ ADD pcap-monitor/supervisord.conf /etc/supervisord.conf ADD pcap-monitor/scripts/ /usr/local/bin/ ADD shared/bin/pcap_watcher.py /usr/local/bin/ ADD shared/bin/pcap_utils.py /usr/local/bin/ -ADD scripts/malcolm_common.py /usr/local/bin/ +ADD scripts/malcolm_utils.py /usr/local/bin/ EXPOSE 30441 diff --git a/Dockerfiles/suricata.Dockerfile b/Dockerfiles/suricata.Dockerfile index 648446ceb..17cef6e09 100644 --- a/Dockerfiles/suricata.Dockerfile +++ b/Dockerfiles/suricata.Dockerfile @@ -132,6 +132,7 @@ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic COPY --chmod=755 shared/bin/nic-capture-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/pcap_processor.py /usr/local/bin/ +COPY --chmod=644 scripts/malcolm_utils.py /usr/local/bin/ COPY --chmod=755 shared/bin/suricata_config_populate.py /usr/local/bin/ COPY --chmod=755 suricata/scripts/docker_entrypoint.sh /usr/local/bin/ COPY --chmod=755 suricata/scripts/eve-clean-logs.sh /usr/local/bin/ diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index 6255b42a8..4ab2f3262 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -154,6 +154,7 @@ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic ADD shared/bin/pcap_processor.py /usr/local/bin/ ADD shared/bin/pcap_utils.py /usr/local/bin/ +ADD scripts/malcolm_utils.py /usr/local/bin/ ADD shared/bin/zeek*threat*.py ${ZEEK_DIR}/bin/ ADD shared/pcaps /tmp/pcaps ADD zeek/supervisord.conf /etc/supervisord.conf diff --git a/api/project/__init__.py b/api/project/__init__.py index 417a1a82c..0036bbe9d 100644 --- a/api/project/__init__.py +++ b/api/project/__init__.py @@ -1,6 +1,6 @@ import dateparser import json -import malcolm_common +import malcolm_utils import opensearchpy import os import pytz @@ -161,7 +161,7 @@ opensearchLocal = (app.config["OPENSEARCH_LOCAL"] == "true") or (opensearchUrl == 'http://opensearch:9200') opensearchSslVerify = app.config["OPENSEARCH_SSL_CERTIFICATE_VERIFICATION"] == "true" opensearchCreds = ( - malcolm_common.ParseCurlFile(app.config["OPENSEARCH_CREDS_CONFIG_FILE"]) + malcolm_utils.ParseCurlFile(app.config["OPENSEARCH_CREDS_CONFIG_FILE"]) if (not opensearchLocal) else defaultdict(lambda: None) ) diff --git a/arkime/scripts/docker_entrypoint.sh b/arkime/scripts/docker_entrypoint.sh index 77e5a684e..a154c1a17 100755 --- a/arkime/scripts/docker_entrypoint.sh +++ b/arkime/scripts/docker_entrypoint.sh @@ -19,7 +19,7 @@ if [[ "$OPENSEARCH_LOCAL" == "false" ]] && [[ -r "$OPENSEARCH_CREDS_CONFIG_FILE" # get the new username/password from the curl file (I already wrote python code to do this, so sue me) pushd "$(dirname $(realpath -e "${BASH_SOURCE[0]}"))" >/dev/null 2>&1 - NEW_USER_PASSWORD="$(python3 -c "import malcolm_common; result=malcolm_common.ParseCurlFile('$OPENSEARCH_CREDS_CONFIG_FILE'); print(result['user']+'|'+result['password']);")" + NEW_USER_PASSWORD="$(python3 -c "import malcolm_utils; result=malcolm_utils.ParseCurlFile('$OPENSEARCH_CREDS_CONFIG_FILE'); print(result['user']+'|'+result['password']);")" NEW_USER="$(echo "$NEW_USER_PASSWORD" | cut -d'|' -f1)" NEW_PASSWORD="$(urlencodeall "$(echo "$NEW_USER_PASSWORD" | cut -d'|' -f2-)")" popd >/dev/null 2>&1 diff --git a/dashboards/scripts/docker_entrypoint.sh b/dashboards/scripts/docker_entrypoint.sh index ff9ee00f5..de78e2737 100755 --- a/dashboards/scripts/docker_entrypoint.sh +++ b/dashboards/scripts/docker_entrypoint.sh @@ -16,7 +16,7 @@ if [[ -f "$ORIG_YML" ]]; then OPENSSL_PASSWORD= if [[ "$OPENSEARCH_LOCAL" == "false" ]] && [[ -r "$OPENSEARCH_CREDS_CONFIG_FILE" ]]; then pushd "$(dirname $(realpath -e "${BASH_SOURCE[0]}"))" >/dev/null 2>&1 - NEW_USER_PASSWORD="$(python3 -c "import malcolm_common; result=malcolm_common.ParseCurlFile('$OPENSEARCH_CREDS_CONFIG_FILE'); print(result['user']+'|'+result['password']);")" + NEW_USER_PASSWORD="$(python3 -c "import malcolm_utils; result=malcolm_utils.ParseCurlFile('$OPENSEARCH_CREDS_CONFIG_FILE'); print(result['user']+'|'+result['password']);")" OPENSSL_USER="$(echo "$NEW_USER_PASSWORD" | cut -d'|' -f1)" OPENSSL_PASSWORD="$(echo "$NEW_USER_PASSWORD" | cut -d'|' -f2-)" popd >/dev/null 2>&1 diff --git a/dashboards/scripts/index-refresh.py b/dashboards/scripts/index-refresh.py index 7e5c84134..06a6c9911 100755 --- a/dashboards/scripts/index-refresh.py +++ b/dashboards/scripts/index-refresh.py @@ -3,7 +3,7 @@ import argparse import json -import malcolm_common +import malcolm_utils import re import requests import os @@ -29,6 +29,7 @@ origPath = os.getcwd() urllib3.disable_warnings() + ################################################################################################### # print to stderr def eprint(*args, **kwargs): @@ -148,7 +149,7 @@ def main(): args.opensearchIsLocal = args.opensearchIsLocal or (args.opensearchUrl == 'http://opensearch:9200') opensearchCreds = ( - malcolm_common.ParseCurlFile(args.opensearchCurlRcFile) + malcolm_utils.ParseCurlFile(args.opensearchCurlRcFile) if (not args.opensearchIsLocal) else defaultdict(lambda: None) ) @@ -199,7 +200,6 @@ def main(): eprint('Index ID for {} is {}'.format(args.index, indexId)) if indexId is not None: - # get the current fields list getFieldsResponse = requests.get( '{}/{}'.format(args.dashboardsUrl, GET_FIELDS_URI), @@ -214,7 +214,6 @@ def main(): # get the fields from the template, if specified, and merge those into the fields list if args.template is not None: try: - # request template from OpenSearch and pull the mappings/properties (field list) out getTemplateResponse = requests.get( '{}/{}/{}'.format(args.opensearchUrl, OS_GET_INDEX_TEMPLATE_URI, args.template), @@ -225,7 +224,6 @@ def main(): getTemplateResponseJson = getTemplateResponse.json() if 'index_templates' in getTemplateResponseJson: for template in getTemplateResponseJson['index_templates']: - templateFields = template['index_template']['template']['mappings']['properties'] # also include fields from component templates into templateFields before processing @@ -259,7 +257,6 @@ def main(): and ('type' in templateFields[field]) and (templateFields[field]['type'] in mergeFieldTypes) ): - # create field dict in same format as those returned by GET_FIELDS_URI above mergedFieldInfo = {} mergedFieldInfo['name'] = field @@ -321,7 +318,6 @@ def main(): fieldFormatMap = {} for field in getFieldsList: if field['name'][:1].isalpha(): - # for Arkime to query by database field name, see arkime issue/PR 1461/1463 valQuote = '"' if field['type'] == 'string' else '' valDbPrefix = '' if field['name'].startswith('zeek') else 'db:' diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index d0bde1c94..b63e62551 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -10,7 +10,7 @@ To install Malcolm from the latest Malcolm release, browse to the [Malcolm relea ``` user@host:~$ cd Downloads/ user@host:~/Downloads$ ls -malcolm_common.py install.py malcolm_20190611_095410_ce2d8de.tar.gz +malcolm_common.py malcolm_utils.py install.py malcolm_20190611_095410_ce2d8de.tar.gz ``` If you are obtaining Malcolm using `git` instead, run the following command to clone Malcolm into a local working copy: diff --git a/logstash/scripts/logstash-start.sh b/logstash/scripts/logstash-start.sh index 94e38deb1..b80196d7f 100755 --- a/logstash/scripts/logstash-start.sh +++ b/logstash/scripts/logstash-start.sh @@ -84,7 +84,7 @@ OPENSSL_USER= OPENSSL_PASSWORD= if [[ "$OPENSEARCH_LOCAL" == "false" ]] && [[ -r "$OPENSEARCH_CREDS_CONFIG_FILE" ]]; then pushd "$(dirname $(realpath -e "${BASH_SOURCE[0]}"))" >/dev/null 2>&1 - NEW_USER_PASSWORD="$(python3 -c "import malcolm_common; result=malcolm_common.ParseCurlFile('$OPENSEARCH_CREDS_CONFIG_FILE'); print(result['user']+'|'+result['password']);")" + NEW_USER_PASSWORD="$(python3 -c "import malcolm_utils; result=malcolm_utils.ParseCurlFile('$OPENSEARCH_CREDS_CONFIG_FILE'); print(result['user']+'|'+result['password']);")" OPENSSL_USER="$(echo "$NEW_USER_PASSWORD" | cut -d'|' -f1)" OPENSSL_PASSWORD="$(echo "$NEW_USER_PASSWORD" | cut -d'|' -f2-)" popd >/dev/null 2>&1 @@ -94,7 +94,7 @@ OPENSSL_SECONDARY_USER= OPENSSL_SECONDARY_PASSWORD= if [[ "$OPENSEARCH_SECONDARY" == "true" ]] && [[ -r "$OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE" ]]; then pushd "$(dirname $(realpath -e "${BASH_SOURCE[0]}"))" >/dev/null 2>&1 - NEW_SECONDARY_USER_PASSWORD="$(python3 -c "import malcolm_common; result=malcolm_common.ParseCurlFile('$OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE'); print(result['user']+'|'+result['password']);")" + NEW_SECONDARY_USER_PASSWORD="$(python3 -c "import malcolm_utils; result=malcolm_utils.ParseCurlFile('$OPENSEARCH_SECONDARY_CREDS_CONFIG_FILE'); print(result['user']+'|'+result['password']);")" OPENSSL_SECONDARY_USER="$(echo "$NEW_SECONDARY_USER_PASSWORD" | cut -d'|' -f1)" OPENSSL_SECONDARY_PASSWORD="$(echo "$NEW_SECONDARY_USER_PASSWORD" | cut -d'|' -f2-)" popd >/dev/null 2>&1 diff --git a/malcolm-iso/build.sh b/malcolm-iso/build.sh index 8671bc72a..dc78f5876 100755 --- a/malcolm-iso/build.sh +++ b/malcolm-iso/build.sh @@ -140,6 +140,7 @@ if [ -d "$WORKDIR" ]; then ln -s ./control.py wipe popd >/dev/null 2>&1 cp ./scripts/malcolm_common.py "$MALCOLM_DEST_DIR/scripts/" + cp ./scripts/malcolm_utils.py "$MALCOLM_DEST_DIR/scripts/" cp ./logstash/certs/*.conf "$MALCOLM_DEST_DIR/logstash/certs/" cp ./logstash/maps/malcolm_severity.yaml "$MALCOLM_DEST_DIR/logstash/maps/" cp -r ./netbox/config/ "$MALCOLM_DEST_DIR/netbox/" diff --git a/scripts/control.py b/scripts/control.py index b4581c894..abae273f4 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -22,6 +22,17 @@ import time from malcolm_common import * +from malcolm_utils import ( + eprint, + EscapeForCurl, + EscapeAnsi, + LoadStrIfJson, + ParseCurlFile, + RemoveEmptyFolders, + run_process, + same_file_or_dir, + which, +) from base64 import b64encode from collections import defaultdict, namedtuple from subprocess import PIPE, DEVNULL, Popen, TimeoutExpired @@ -892,8 +903,8 @@ def authSetup(wipe=False): filebeatPath = os.path.join(MalcolmPath, os.path.join('filebeat', 'certs')) txRxScript = None - if (pyPlatform != PLATFORM_WINDOWS) and Which("croc"): - txRxScript = 'tx-rx-secure.sh' if Which('tx-rx-secure.sh') else None + if (pyPlatform != PLATFORM_WINDOWS) and which("croc"): + txRxScript = 'tx-rx-secure.sh' if which('tx-rx-secure.sh') else None if not txRxScript: txRxScript = os.path.join( MalcolmPath, os.path.join('shared', os.path.join('bin', os.path.join('tx-rx-secure.sh'))) @@ -1740,10 +1751,10 @@ def main(): osEnv['TMPDIR'] = MalcolmTmpPath # make sure docker/docker-compose is available - dockerBin = 'docker.exe' if ((pyPlatform == PLATFORM_WINDOWS) and Which('docker.exe')) else 'docker' - if (pyPlatform == PLATFORM_WINDOWS) and Which('docker-compose.exe'): + dockerBin = 'docker.exe' if ((pyPlatform == PLATFORM_WINDOWS) and which('docker.exe')) else 'docker' + if (pyPlatform == PLATFORM_WINDOWS) and which('docker-compose.exe'): dockerComposeBin = 'docker-compose.exe' - elif Which('docker-compose'): + elif which('docker-compose'): dockerComposeBin = 'docker-compose' elif os.path.isfile('/usr/libexec/docker/cli-plugins/docker-compose'): dockerComposeBin = '/usr/libexec/docker/cli-plugins/docker-compose' @@ -1767,7 +1778,7 @@ def main(): dockerComposeYaml = yamlImported.safe_load(cf) # identify openssl binary - opensslBin = 'openssl.exe' if ((pyPlatform == PLATFORM_WINDOWS) and Which('openssl.exe')) else 'openssl' + opensslBin = 'openssl.exe' if ((pyPlatform == PLATFORM_WINDOWS) and which('openssl.exe')) else 'openssl' # if executed via a symlink, figure out what was intended via the symlink name if os.path.islink(os.path.join(ScriptPath, ScriptName)): diff --git a/scripts/install.py b/scripts/install.py index 579987783..20c870f52 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -29,6 +29,8 @@ from collections import defaultdict, namedtuple from malcolm_common import * +import malcolm_utils +from malcolm_utils import eprint, str2bool, run_process, same_file_or_dir, touch, which ################################################################################################### DOCKER_COMPOSE_INSTALL_VERSION = "2.14.2" @@ -180,7 +182,7 @@ def __init__(self, debug=False, configOnly=False): self.requiredPackages = [] self.pipCmd = 'pip3' - if not Which(self.pipCmd, debug=self.debug): + if not which(self.pipCmd, debug=self.debug): self.pipCmd = 'pip' self.tempDirName = tempfile.mkdtemp() @@ -1252,7 +1254,7 @@ def tweak_malcolm_runtime( # now, go through and modify the values in the .env files for val in EnvValues: try: - Touch(val.envFile) + touch(val.envFile) except Exception as e: pass @@ -1624,7 +1626,7 @@ def tweak_malcolm_runtime( os.chown(composeFile, origUid, origGuid) try: - Touch(MalcolmCfgRunOnceFile) + touch(MalcolmCfgRunOnceFile) except Exception as e: pass @@ -1748,24 +1750,24 @@ def __init__(self, debug=False, configOnly=False): raise Exception(f'{ScriptName} must be run as root, or {self.sudoCmd} must be available') # determine command to use to query if a package is installed - if Which('dpkg', debug=self.debug): + if which('dpkg', debug=self.debug): os.environ["DEBIAN_FRONTEND"] = "noninteractive" self.checkPackageCmds.append(['dpkg', '-s']) - elif Which('rpm', debug=self.debug): + elif which('rpm', debug=self.debug): self.checkPackageCmds.append(['rpm', '-q']) - elif Which('dnf', debug=self.debug): + elif which('dnf', debug=self.debug): self.checkPackageCmds.append(['dnf', 'list', 'installed']) - elif Which('yum', debug=self.debug): + elif which('yum', debug=self.debug): self.checkPackageCmds.append(['yum', 'list', 'installed']) # determine command to install a package from the distro's repos - if Which('apt-get', debug=self.debug): + if which('apt-get', debug=self.debug): self.installPackageCmds.append(['apt-get', 'install', '-y', '-qq']) - elif Which('apt', debug=self.debug): + elif which('apt', debug=self.debug): self.installPackageCmds.append(['apt', 'install', '-y', '-qq']) - elif Which('dnf', debug=self.debug): + elif which('dnf', debug=self.debug): self.installPackageCmds.append(['dnf', '-y', 'install', '--nobest']) - elif Which('yum', debug=self.debug): + elif which('yum', debug=self.debug): self.installPackageCmds.append(['yum', '-y', 'install']) # determine total system memory @@ -1996,7 +1998,7 @@ def install_docker_compose(self): result = False dockerComposeCmd = 'docker-compose' - if not Which(dockerComposeCmd, debug=self.debug): + if not which(dockerComposeCmd, debug=self.debug): if os.path.isfile('/usr/libexec/docker/cli-plugins/docker-compose'): dockerComposeCmd = '/usr/libexec/docker/cli-plugins/docker-compose' elif os.path.isfile('/usr/local/bin/docker-compose'): diff --git a/scripts/malcolm_appliance_packager.sh b/scripts/malcolm_appliance_packager.sh index e46cb68b3..260e54d8a 100755 --- a/scripts/malcolm_appliance_packager.sh +++ b/scripts/malcolm_appliance_packager.sh @@ -96,6 +96,7 @@ if mkdir "$DESTDIR"; then cp $VERBOSE ./scripts/install.py "$DESTDIR/scripts/" cp $VERBOSE ./scripts/control.py "$DESTDIR/scripts/" cp $VERBOSE ./scripts/malcolm_common.py "$DESTDIR/scripts/" + cp $VERBOSE ./scripts/malcolm_utils.py "$DESTDIR/scripts/" cp $VERBOSE ./README.md "$DESTDIR/" cp $VERBOSE ./logstash/certs/*.conf "$DESTDIR/logstash/certs/" cp $VERBOSE ./logstash/maps/malcolm_severity.yaml "$DESTDIR/logstash/maps/" @@ -120,6 +121,7 @@ if mkdir "$DESTDIR"; then README="$RUN_PATH/$(basename $DESTDIR).README.txt" cp $VERBOSE "$SCRIPT_PATH/install.py" "$RUN_PATH/" cp $VERBOSE "$SCRIPT_PATH/malcolm_common.py" "$RUN_PATH/" + cp $VERBOSE "$SCRIPT_PATH/malcolm_utils.py" "$RUN_PATH/" tar -czf $VERBOSE "$DESTNAME" "./$(basename $DESTDIR)/" echo "Packaged Malcolm to \"$DESTNAME\"" diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index 168e2978f..8b9c50c9d 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -3,33 +3,24 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. -import argparse -import contextlib import getpass import importlib -import json import os import platform import re import string import sys -import time +import malcolm_utils +from malcolm_utils import eprint, str2bool, run_process + +from collections import defaultdict, namedtuple from enum import IntFlag, auto try: from pwd import getpwuid except ImportError: getpwuid = None -from subprocess import PIPE, STDOUT, Popen, CalledProcessError - - -from collections import defaultdict, namedtuple - -try: - from collections.abc import Iterable -except ImportError: - from collections import Iterable try: from dialog import Dialog @@ -90,77 +81,6 @@ class UserInterfaceMode(IntFlag): HOMEBREW_INSTALL_URLS = defaultdict(lambda: 'https://brew.sh/') -################################################################################################### -# chdir to directory as context manager, returning automatically -@contextlib.contextmanager -def pushd(directory): - prevDir = os.getcwd() - os.chdir(directory) - try: - yield - finally: - os.chdir(prevDir) - - -################################################################################################### -# print to stderr -def eprint(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - - -################################################################################################### -def EscapeAnsi(line): - ansiEscape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]') - return ansiEscape.sub('', line) - - -################################################################################################### -def EscapeForCurl(s): - return s.translate( - str.maketrans( - { - '"': r'\"', - "\\": r"\\", - "\t": r"\t", - "\n": r"\n", - "\r": r"\r", - "\v": r"\v", - } - ) - ) - - -################################################################################################### -def custom_make_translation(text, translation): - regex = re.compile('|'.join(map(re.escape, translation))) - return regex.sub(lambda match: translation[match.group(0)], text) - - -################################################################################################## -def UnescapeForCurl(s): - return custom_make_translation( - s, - { - r'\"': '"', - r"\t": "\t", - r"\n": "\n", - r"\r": "\r", - r"\v": "\v", - r"\\": "\\", - }, - ) - - -################################################################################################### -# if the object is an iterable, return it, otherwise return a tuple with it as a single element. -# useful if you want to user either a scalar or an array in a loop, etc. -def GetIterable(x): - if isinstance(x, Iterable) and not isinstance(x, str): - return x - else: - return (x,) - - ################################################################################################## def ReplaceBindMountLocation(line, location, linePrefix): if os.path.isdir(location): @@ -209,58 +129,6 @@ def GetUidGidFromComposeFile(composeFile): return uidGidDict -################################################################################################### -def same_file_or_dir(path1, path2): - try: - return os.path.samefile(path1, path2) - except Exception: - return False - - -################################################################################################### -# parse a curl-formatted config file, with special handling for user:password and URL -# see https://everything.curl.dev/cmdline/configfile -# e.g.: -# -# given .opensearch.primary.curlrc containing: -# - -# user: "sikari:changethis" -# insecure -# - -# -# ParseCurlFile('.opensearch.primary.curlrc') returns: -# { -# 'user': 'sikari', -# 'password': 'changethis', -# 'insecure': '' -# } -def ParseCurlFile(curlCfgFileName): - result = defaultdict(lambda: None) - if os.path.isfile(curlCfgFileName): - itemRegEx = re.compile(r'^([^\s:=]+)((\s*[:=]?\s*)(.*))?$') - with open(curlCfgFileName, 'r') as f: - allLines = [x.strip().lstrip('-') for x in f.readlines() if not x.startswith('#')] - for line in allLines: - found = itemRegEx.match(line) - if found is not None: - key = found.group(1) - value = UnescapeForCurl(found.group(4).lstrip('"').rstrip('"')) - if (key == 'user') and (':' in value): - splitVal = value.split(':', 1) - result[key] = splitVal[0] - if len(splitVal) > 1: - result['password'] = splitVal[1] - else: - result[key] = value - - return result - - -################################################################################################### -def contains_whitespace(s): - return True in [c in s for c in string.whitespace] - - ################################################################################################### # attempt to clear the screen def ClearScreen(): @@ -602,22 +470,6 @@ def DisplayProgramBox( return reply -################################################################################################### -# convenient boolean argument parsing -def str2bool(v): - if isinstance(v, bool): - return v - elif isinstance(v, str): - if v.lower() in ("yes", "true", "t", "y", "1"): - return True - elif v.lower() in ("no", "false", "f", "n", "0"): - return False - else: - raise ValueError("Boolean value expected") - else: - raise ValueError("Boolean value expected") - - ################################################################################################### # Dies if $value isn't positive. NoneType is also acceptable def posInt(value): @@ -626,122 +478,11 @@ def posInt(value): ivalue = int(value) if ivalue <= 0: - raise argparse.ArgumentTypeError("{} is an invalid positive int value".format(value)) + raise ValueError("{} is an invalid positive int value".format(value)) return ivalue -################################################################################################### -# determine if a program/script exists and is executable in the system path -def Which(cmd, debug=False): - result = any(os.access(os.path.join(path, cmd), os.X_OK) for path in os.environ["PATH"].split(os.pathsep)) - if debug: - eprint(f"Which {cmd} returned {result}") - return result - - -################################################################################################### -# nice human-readable file sizes -def SizeHumanFormat(num, suffix='B'): - for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: - if abs(num) < 1024.0: - return f"{num:3.1f}{unit}{suffix}" - num /= 1024.0 - return f"{num:.1f}{'Yi'}{suffix}" - - -################################################################################################### -# is this string valid json? if so, load and return it -def LoadStrIfJson(jsonStr): - try: - return json.loads(jsonStr) - except ValueError: - return None - - -################################################################################################### -# safe deep get for a dictionary -# -# Example: -# d = {'meta': {'status': 'OK', 'status_code': 200}} -# DeepGet(d, ['meta', 'status_code']) # => 200 -# DeepGet(d, ['garbage', 'status_code']) # => None -# DeepGet(d, ['meta', 'garbage'], default='-') # => '-' -def DeepGet(d, keys, default=None): - assert type(keys) is list - if d is None: - return default - if not keys: - return d - return DeepGet(d.get(keys[0]), keys[1:], default) - - -################################################################################################### -# run command with arguments and return its exit code, stdout, and stderr -def check_output_input(*popenargs, **kwargs): - if 'stdout' in kwargs: - raise ValueError('stdout argument not allowed, it will be overridden') - - if 'stderr' in kwargs: - raise ValueError('stderr argument not allowed, it will be overridden') - - if 'input' in kwargs and kwargs['input']: - if 'stdin' in kwargs: - raise ValueError('stdin and input arguments may not both be used') - inputdata = kwargs['input'] - kwargs['stdin'] = PIPE - else: - inputdata = None - kwargs.pop('input', None) - - process = Popen(*popenargs, stdout=PIPE, stderr=PIPE, **kwargs) - try: - output, errput = process.communicate(inputdata) - except Exception: - process.kill() - process.wait() - raise - - retcode = process.poll() - - return retcode, output, errput - - -################################################################################################### -# run command with arguments and return its exit code, stdout, and stderr -def run_process( - command, stdout=True, stderr=True, stdin=None, retry=0, retrySleepSec=5, cwd=None, env=None, debug=False -): - retcode = -1 - output = [] - - try: - # run the command - retcode, cmdout, cmderr = check_output_input( - command, input=stdin.encode() if stdin else stdin, cwd=cwd, env=env - ) - - # split the output on newlines to return a list - if stderr and (len(cmderr) > 0): - output.extend(cmderr.decode(sys.getdefaultencoding()).split('\n')) - if stdout and (len(cmdout) > 0): - output.extend(cmdout.decode(sys.getdefaultencoding()).split('\n')) - - except (FileNotFoundError, OSError, IOError): - if stderr: - output.append(f"Command {command} not found or unable to execute") - - if debug: - eprint(f"{command}({stdin[:80] + bool(stdin[80:]) * '...' if stdin else ''}) returned {retcode}: {output}") - - if (retcode != 0) and retry and (retry > 0): - # sleep then retry - time.sleep(retrySleepSec) - return run_process(command, stdout, stderr, stdin, retry - 1, retrySleepSec, cwd, env, debug) - else: - return retcode, output - - ################################################################################################### # attempt dynamic imports, prompting for install via pip if possible DynImports = defaultdict(lambda: None) @@ -766,12 +507,12 @@ def DoDynamicImport(importName, pipPkgName, interactive=False, debug=False): pyPlatform = platform.system() pyExec = sys.executable pipCmd = "pip3" - if not Which(pipCmd, debug=debug): + if not malcolm_utils.which(pipCmd, debug=debug): pipCmd = "pip" eprint(f"The {pipPkgName} module is required under Python {platform.python_version()} ({pyExec})") - if interactive and Which(pipCmd, debug=debug): + if interactive and malcolm_utils.which(pipCmd, debug=debug): if YesOrNo(f"Importing the {pipPkgName} module failed. Attempt to install via {pipCmd}?"): installCmd = None @@ -852,34 +593,6 @@ def DownloadToFile(url, local_filename, debug=False): fSize = os.path.getsize(local_filename) if debug: eprint( - f"Download of {url} to {local_filename} {'succeeded' if fExists else 'failed'} ({SizeHumanFormat(fSize)})" + f"Download of {url} to {local_filename} {'succeeded' if fExists else 'failed'} ({malcolm_utils.sizeof_fmt(fSize)})" ) return fExists and (fSize > 0) - - -################################################################################################### -# recursively remove empty subfolders -def RemoveEmptyFolders(path, removeRoot=True): - if not os.path.isdir(path): - return - - files = os.listdir(path) - if len(files): - for f in files: - fullpath = os.path.join(path, f) - if os.path.isdir(fullpath): - RemoveEmptyFolders(fullpath) - - files = os.listdir(path) - if len(files) == 0 and removeRoot: - try: - os.rmdir(path) - except Exception: - pass - - -################################################################################################### -# open a file and close it, updating its access time -def Touch(filename): - open(filename, 'a').close() - os.utime(filename, None) diff --git a/shared/bin/malcolm_utils.py b/scripts/malcolm_utils.py similarity index 65% rename from shared/bin/malcolm_utils.py rename to scripts/malcolm_utils.py index 8c32fbcfc..6fde1fa83 100644 --- a/shared/bin/malcolm_utils.py +++ b/scripts/malcolm_utils.py @@ -1,25 +1,40 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import os -import json -import hashlib +# Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. + import contextlib +import datetime +import hashlib +import ipaddress +import json +import os +import re +import socket +import string import subprocess import sys +import tempfile +import time + -from collections import OrderedDict -from multiprocessing import RawValue -from threading import Lock from base64 import b64decode +from multiprocessing import RawValue +from subprocess import PIPE, STDOUT, Popen, CalledProcessError from tempfile import NamedTemporaryFile -from Crypto.Cipher import AES +from threading import Lock + +try: + from collections.abc import Iterable +except ImportError: + from collections import Iterable +from collections import defaultdict, namedtuple, OrderedDict ################################################################################################### # urlencode each character of a string -def aggressive_url_encode(string): - return "".join("%{0:0>2}".format(format(ord(char), "x")) for char in string) +def aggressive_url_encode(val): + return "".join("%{0:0>2}".format(format(ord(char), "x")) for char in val) ################################################################################################### @@ -59,6 +74,22 @@ def base64_decode_if_prefixed(s: str): return s +################################################################################################### +# test if a remote port is open +def check_socket(host, port): + with contextlib.closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock: + sock.settimeout(10) + if sock.connect_ex((host, port)) == 0: + return True + else: + return False + + +################################################################################################### +def contains_whitespace(s): + return True in [c in s for c in string.whitespace] + + ################################################################################################### # an OrderedDict that locks itself and unlocks itself as a context manager class ContextLockedOrderedDict(OrderedDict): @@ -76,7 +107,19 @@ def __exit__(self, type, value, traceback): ################################################################################################### -# convenience routine for deep-getting a value from a dictionary +def custom_make_translation(text, translation): + regex = re.compile('|'.join(map(re.escape, translation))) + return regex.sub(lambda match: translation[match.group(0)], text) + + +################################################################################################### +# safe deep get for a dictionary +# +# Example: +# d = {'meta': {'status': 'OK', 'status_code': 200}} +# DeepGet(d, ['meta', 'status_code']) # => 200 +# DeepGet(d, ['garbage', 'status_code']) # => None +# DeepGet(d, ['meta', 'garbage'], default='-') # => '-' def deep_get(d, keys, default=None): k = get_iterable(keys) if d is None: @@ -95,7 +138,7 @@ def deep_set(d, keys, value, deleteIfNone=False): d[key] = dict() d = d[key] d[k[-1]] = value - if (deleteIfNone == True) and (value is None): + if deleteIfNone and (value is None): d.pop(k[-1], None) @@ -119,6 +162,42 @@ def eprint(*args, **kwargs): sys.stderr.flush() +################################################################################################### +def EscapeAnsi(line): + ansiEscape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]') + return ansiEscape.sub('', line) + + +################################################################################################### +def EscapeForCurl(s): + return s.translate( + str.maketrans( + { + '"': r'\"', + "\\": r"\\", + "\t": r"\t", + "\n": r"\n", + "\r": r"\r", + "\v": r"\v", + } + ) + ) + + +def UnescapeForCurl(s): + return custom_make_translation( + s, + { + r'\"': '"', + r"\t": "\t", + r"\n": "\n", + r"\r": "\r", + r"\v": "\v", + r"\\": "\\", + }, + ) + + ################################################################################################### # EVP_BytesToKey # @@ -161,7 +240,8 @@ def EVP_BytesToKey(key_length: int, iv_length: int, md, salt: bytes, data: bytes ################################################################################################### -# return just about any object as an iterable +# if the object is an iterable, return it, otherwise return a tuple with it as a single element. +# useful if you want to user either a scalar or an array in a loop, etc. def get_iterable(x): if isinstance(x, Iterable) and not isinstance(x, str): return x @@ -186,10 +266,10 @@ def isipaddress(value): try: if isinstance(value, list) or isinstance(value, tuple) or isinstance(value, set): for v in value: - ip = ipaddress.ip_address(v) + ipaddress.ip_address(v) else: - ip = ipaddress.ip_address(value) - except: + ipaddress.ip_address(value) + except Exception: result = False return result @@ -199,7 +279,7 @@ def isipaddress(value): def LoadStrIfJson(jsonStr): try: return json.loads(jsonStr) - except ValueError as e: + except ValueError: return None @@ -209,10 +289,49 @@ def LoadStrIfJson(jsonStr): def LoadFileIfJson(fileHandle): try: return json.load(fileHandle) - except ValueError as e: + except ValueError: return None +################################################################################################### +# parse a curl-formatted config file, with special handling for user:password and URL +# see https://everything.curl.dev/cmdline/configfile +# e.g.: +# +# given .opensearch.primary.curlrc containing: +# - +# user: "sikari:changethis" +# insecure +# - +# +# ParseCurlFile('.opensearch.primary.curlrc') returns: +# { +# 'user': 'sikari', +# 'password': 'changethis', +# 'insecure': '' +# } +def ParseCurlFile(curlCfgFileName): + result = defaultdict(lambda: None) + if os.path.isfile(curlCfgFileName): + itemRegEx = re.compile(r'^([^\s:=]+)((\s*[:=]?\s*)(.*))?$') + with open(curlCfgFileName, 'r') as f: + allLines = [x.strip().lstrip('-') for x in f.readlines() if not x.startswith('#')] + for line in allLines: + found = itemRegEx.match(line) + if found is not None: + key = found.group(1) + value = UnescapeForCurl(found.group(4).lstrip('"').rstrip('"')) + if (key == 'user') and (':' in value): + splitVal = value.split(':', 1) + result[key] = splitVal[0] + if len(splitVal) > 1: + result['password'] = splitVal[1] + else: + result[key] = value + + return result + + ################################################################################################### # a context manager for entering a directory and leaving it upon leaving the context @contextlib.contextmanager @@ -225,6 +344,27 @@ def pushd(directory): os.chdir(prevDir) +################################################################################################### +# recursively remove empty subfolders +def RemoveEmptyFolders(path, removeRoot=True): + if not os.path.isdir(path): + return + + files = os.listdir(path) + if len(files): + for f in files: + fullpath = os.path.join(path, f) + if os.path.isdir(fullpath): + RemoveEmptyFolders(fullpath) + + files = os.listdir(path) + if len(files) == 0 and removeRoot: + try: + os.rmdir(path) + except Exception: + pass + + ################################################################################################### # strip a prefix from the beginning of a string if needed def remove_prefix(text, prefix): @@ -260,20 +400,25 @@ def sha256sum(filename): def sizeof_fmt(num, suffix='B'): for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: if abs(num) < 1024.0: - return "%3.1f%s%s" % (num, unit, suffix) + return f"{num:3.1f}{unit}{suffix}" num /= 1024.0 - return "%.1f%s%s" % (num, 'Yi', suffix) + return f"{num:.1f}{'Yi'}{suffix}" ################################################################################################### # convenient boolean argument parsing def str2bool(v): - if v.lower() in ('yes', 'true', 't', 'y', '1'): - return True - elif v.lower() in ('no', 'false', 'f', 'n', '0'): - return False + if isinstance(v, bool): + return v + elif isinstance(v, str): + if v.lower() in ("yes", "true", "t", "y", "1"): + return True + elif v.lower() in ("no", "false", "f", "n", "0"): + return False + else: + raise ValueError("Boolean value expected") else: - raise ValueError('Boolean value expected.') + raise ValueError("Boolean value expected") ################################################################################################### @@ -312,11 +457,20 @@ def val2bool(v): raise ValueError(f'Boolean value expected (got {v})') else: raise ValueError(f'Boolean value expected (got {v})') - except: + except Exception: # just pitch it back and let the caller worry about it return v +################################################################################################### +# determine if a program/script exists and is executable in the system path +def which(cmd, debug=False): + result = any(os.access(os.path.join(path, cmd), os.X_OK) for path in os.environ["PATH"].split(os.pathsep)) + if debug: + eprint(f"which {cmd} returned {result}") + return result + + ################################################################################################### # run command with arguments and return its exit code, stdout, and stderr def check_output_input(*popenargs, **kwargs): @@ -338,7 +492,7 @@ def check_output_input(*popenargs, **kwargs): process = Popen(*popenargs, stdout=PIPE, stderr=PIPE, **kwargs) try: output, errput = process.communicate(inputdata) - except: + except Exception: process.kill() process.wait() raise @@ -350,13 +504,29 @@ def check_output_input(*popenargs, **kwargs): ################################################################################################### # run command with arguments and return its exit code and output -def run_process(command, stdout=True, stderr=True, stdin=None, cwd=None, env=None, debug=False, logger=None): +def run_process( + command, + stdout=True, + stderr=True, + stdin=None, + retry=0, + retrySleepSec=5, + cwd=None, + env=None, + debug=False, + logger=None, +): retcode = -1 output = [] try: # run the command - retcode, cmdout, cmderr = check_output_input(command, input=stdin.encode() if stdin else None, cwd=cwd, env=env) + retcode, cmdout, cmderr = check_output_input( + command, + input=stdin.encode() if stdin else None, + cwd=cwd, + env=env, + ) # split the output on newlines to return a list if stderr and (len(cmderr) > 0): @@ -364,7 +534,7 @@ def run_process(command, stdout=True, stderr=True, stdin=None, cwd=None, env=Non if stdout and (len(cmdout) > 0): output.extend(cmdout.decode(sys.getdefaultencoding()).split('\n')) - except (FileNotFoundError, OSError, IOError) as e: + except (FileNotFoundError, OSError, IOError): if stderr: output.append("Command {} not found or unable to execute".format(command)) @@ -377,7 +547,12 @@ def run_process(command, stdout=True, stderr=True, stdin=None, cwd=None, env=Non else: eprint(dbgStr) - return retcode, output + if (retcode != 0) and retry and (retry > 0): + # sleep then retry + time.sleep(retrySleepSec) + return run_process(command, stdout, stderr, stdin, retry - 1, retrySleepSec, cwd, env, debug, logger) + else: + return retcode, output ################################################################################################### diff --git a/shared/bin/configure-capture.py b/shared/bin/configure-capture.py index f0273f32e..e890c9c3e 100755 --- a/shared/bin/configure-capture.py +++ b/shared/bin/configure-capture.py @@ -16,9 +16,16 @@ from subprocess import PIPE, STDOUT, Popen, CalledProcessError -from zeek_carve_utils import * -from sensorcommon import * -from malcolm_common import run_process +from zeek_carve_utils import PRESERVE_NONE, PRESERVE_QUARANTINED, PRESERVE_ALL +from sensorcommon import ( + CancelledError, + clearquit, + get_available_adapters, + identify_adapter, + NIC_BLINK_SECONDS, + test_connection, +) +from malcolm_utils import run_process, remove_prefix, aggressive_url_encode, isipaddress, check_socket class Constants: @@ -244,7 +251,7 @@ def input_opensearch_connection_info( break # HTTP/HTTPS authentication - code, http_username = d.inputbox(f"OpenSearch HTTP/HTTPS server username", init=default_username) + code, http_username = d.inputbox("OpenSearch HTTP/HTTPS server username", init=default_username) if (code == Dialog.CANCEL) or (code == Dialog.ESC): raise CancelledError return_dict[Constants.BEAT_HTTP_USERNAME] = http_username.strip() @@ -252,13 +259,13 @@ def input_opensearch_connection_info( # make them enter the password twice while True: code, http_password = d.passwordbox( - f"OpenSearch HTTP/HTTPS server password", insecure=True, init=default_password + "OpenSearch HTTP/HTTPS server password", insecure=True, init=default_password ) if (code == Dialog.CANCEL) or (code == Dialog.ESC): raise CancelledError code, http_password2 = d.passwordbox( - f"OpenSearch HTTP/HTTPS server password (again)", + "OpenSearch HTTP/HTTPS server password (again)", insecure=True, init=default_password if (http_password == default_password) else "", ) @@ -315,7 +322,7 @@ def main(): try: with open(Constants.DEV_IDENTIFIER_FILE, 'r') as f: installation = f.readline().strip() - except: + except Exception: pass if installation not in Constants.DEV_VALID: print(Constants.MSG_ERR_DEV_INVALID) @@ -381,7 +388,7 @@ def main(): raise CancelledError if mode == Constants.MSG_CONFIG_MODE_AUTOSTART: - ##### sensor autostart services configuration ####################################################################################### + # sensor autostart services configuration ############################################################################################# while True: # select processes for autostart (except for the file scan ones, handle those with the file scanning stuff) @@ -434,7 +441,7 @@ def main(): code = d.msgbox(text=Constants.MSG_CONFIG_AUTOSTART_SUCCESS) elif mode == Constants.MSG_CONFIG_MODE_CAPTURE: - ##### sensor capture configuration ################################################################################################## + # sensor capture configuration ######################################################################################################## # determine a list of available (non-virtual) adapters available_adapters = get_available_adapters() @@ -794,7 +801,7 @@ def main(): ) elif mode == Constants.MSG_CONFIG_MODE_FORWARD: - ##### sensor forwarding (beats) configuration ######################################################################### + # sensor forwarding (beats) configuration ############################################################################# # only display MSG_CONFIG_TXRX if we have appropriate executable and script txRxScript = '/opt/sensor/sensor_ctl/tx-rx-secure.sh' @@ -1220,7 +1227,7 @@ def main(): break with Popen( - [txRxScript, '-s', tx_ip, '-r', rx_token, '-o', BEAT_LS_CERT_DIR_DEFAULT], + [txRxScript, '-s', tx_ip, '-r', rx_token, '-o', Constants.BEAT_LS_CERT_DIR_DEFAULT], stdout=PIPE, stderr=STDOUT, bufsize=0, @@ -1240,7 +1247,7 @@ def main(): # we're here without a valid forwarding type selection?!? raise Exception(Constants.MSG_MESSAGE_ERROR.format(Constants.MSG_INVALID_FORWARDING_TYPE)) - except CancelledError as c: + except CancelledError: # d.msgbox(text=Constants.MSG_CANCEL_ERROR) # just start over continue diff --git a/shared/bin/configure-interfaces.py b/shared/bin/configure-interfaces.py index e144d82df..de9a344b4 100755 --- a/shared/bin/configure-interfaces.py +++ b/shared/bin/configure-interfaces.py @@ -13,9 +13,23 @@ import re from dialog import Dialog from debinterface.interfaces import Interfaces -from sensorcommon import * -from malcolm_utils import run_process +from sensorcommon import ( + CancelledError, + clearquit, + get_available_adapters, + identify_adapter, + NIC_BLINK_SECONDS, +) +from malcolm_utils import ( + eprint, + run_process, + remove_prefix, + aggressive_url_encode, + isipaddress, + check_socket, + test_connection, +) class Constants: @@ -149,7 +163,7 @@ def write_and_display_results(interfaces, selected_iface): else: start_text = Constants.MSG_NETWORK_START_ERROR - code = d.msgbox(stop_text + "\n".join(stop_results) + "\n\n. . .\n\n" + start_text + "\n".join(start_results)) + d.msgbox(stop_text + "\n".join(stop_results) + "\n\n. . .\n\n" + start_text + "\n".join(start_results)) ################################################################################################### @@ -168,7 +182,7 @@ def main(): try: with open(Constants.DEV_IDENTIFIER_FILE, 'r') as f: installation = f.readline().strip() - except: + except Exception: pass if installation == Constants.DEV_SENSOR: modeChoices = [Constants.MSG_CONFIG_INTERFACE, Constants.MSG_CONFIG_HOST, Constants.MSG_CONFIG_TIME_SYNC] @@ -197,7 +211,7 @@ def main(): raise CancelledError if config_mode == Constants.MSG_CONFIG_HOST[0]: - ##### system hostname configuration ################################################################################################## + # system hostname configuration ###################################################################################################### # get current host/identification information ecode, host_get_output = run_process('hostnamectl', stderr=True) @@ -217,7 +231,7 @@ def main(): if (code == Dialog.CANCEL) or (code == Dialog.ESC): raise CancelledError elif len(new_hostname) <= 0: - code = d.msgbox(text=Constants.MSG_MESSAGE_ERROR.format(f'Invalid hostname specified')) + code = d.msgbox(text=Constants.MSG_MESSAGE_ERROR.format('Invalid hostname specified')) else: break @@ -254,7 +268,7 @@ def main(): ) elif config_mode == Constants.MSG_CONFIG_TIME_SYNC[0]: - ##### time synchronization configuration############################################################################################## + # time synchronization configuration################################################################################################## time_sync_mode = '' code = Dialog.OK while (len(time_sync_mode) == 0) and (code == Dialog.OK): @@ -315,7 +329,7 @@ def main(): # get polling interval code, htpdate_interval = d.rangebox( - f"Time synchronization polling interval (minutes)", width=60, min=1, max=60, init=15 + "Time synchronization polling interval (minutes)", width=60, min=1, max=60, init=15 ) if code == Dialog.CANCEL or code == Dialog.ESC: raise CancelledError @@ -387,7 +401,7 @@ def main(): raise CancelledError else: - ##### interface IP address configuration ############################################################################################# + # interface IP address configuration ################################################################################################# # read configuration from /etc/network/interfaces.d/sensor (or the default /etc/network/interfaces if for some reason it doesn't exist) interfaces_path = ( @@ -553,7 +567,7 @@ def main(): write_and_display_results(interfaces, selected_iface) break - except CancelledError as c: + except CancelledError: # d.msgbox(text=Constants.MSG_CANCEL_ERROR) # just start over continue diff --git a/shared/bin/ics-oui-parse.py b/shared/bin/ics-oui-parse.py index 08477fb18..88bd984d6 100755 --- a/shared/bin/ics-oui-parse.py +++ b/shared/bin/ics-oui-parse.py @@ -11,7 +11,8 @@ import ruamel.yaml as yaml except ImportError: import yaml -from netaddr import * + +from netaddr import EUI from operator import itemgetter import malcolm_utils @@ -86,14 +87,13 @@ def main(): macPadHigh = macPad + padded_mac_high[-(len(padded_mac_high) - len(macPad)) :] macLow = EUI(macPadLow.replace(':', '-')) macHigh = EUI(macPadHigh.replace(':', '-')) - eui64 = int(macHigh.eui64()) - int(macLow.eui64()) companies.append( { 'name': oui['companyName'], 'low': str(macLow), - #'low': int(re.sub("[.:-]", "", str(macLow)), 16), + # 'low': int(re.sub("[.:-]", "", str(macLow)), 16), 'high': str(macHigh), - #'high': int(re.sub("[.:-]", "", str(macHigh)), 16), + # 'high': int(re.sub("[.:-]", "", str(macHigh)), 16), } ) diff --git a/shared/bin/opensearch_index_size_prune.py b/shared/bin/opensearch_index_size_prune.py index 4b136c2b3..16d63933d 100755 --- a/shared/bin/opensearch_index_size_prune.py +++ b/shared/bin/opensearch_index_size_prune.py @@ -4,7 +4,6 @@ import argparse import humanfriendly import json -import malcolm_common import re import requests import os @@ -15,7 +14,7 @@ from requests.auth import HTTPBasicAuth import malcolm_utils -from malcolm_utils import eprint, str2bool +from malcolm_utils import eprint, str2bool, ParseCurlFile ################################################################################################### debug = False @@ -134,7 +133,7 @@ def main(): try: parser.error = parser.exit args = parser.parse_args() - except Exception as e: + except Exception: parser.print_help() exit(2) @@ -152,9 +151,7 @@ def main(): args.opensearchIsLocal = args.opensearchIsLocal or (args.opensearchUrl == 'http://opensearch:9200') opensearchCreds = ( - malcolm_common.ParseCurlFile(args.opensearchCurlRcFile) - if (not args.opensearchIsLocal) - else defaultdict(lambda: None) + ParseCurlFile(args.opensearchCurlRcFile) if (not args.opensearchIsLocal) else defaultdict(lambda: None) ) if not args.opensearchUrl: if args.opensearchIsLocal: @@ -236,10 +233,10 @@ def main(): # ... # ] if len(esDiskUsageStats) != 1: - raise Exception(f'Unable to determine node, please specify --node if using a percentage limit') + raise Exception('Unable to determine node, please specify --node if using a percentage limit') elif 'disk.total' not in esDiskUsageStats[0]: raise Exception( - f'Unable to determine disk.total for {esDiskUsageStats[0]["node"] if "node" in esDiskUsageStats[0] else node}' + f'Unable to determine disk.total for {esDiskUsageStats[0]["node"] if "node" in esDiskUsageStats[0] else "node"}' ) limitMegabytes = int(float(esDiskUsageStats[0]['disk.total']) * (float(limitPercent) / 100.0)) // 1000000 diff --git a/shared/bin/opensearch_read_only.py b/shared/bin/opensearch_read_only.py index e4d86e276..35c9f210f 100755 --- a/shared/bin/opensearch_read_only.py +++ b/shared/bin/opensearch_read_only.py @@ -6,7 +6,6 @@ import argparse import json import requests -import malcolm_common import os import sys import urllib3 @@ -15,7 +14,7 @@ from requests.auth import HTTPBasicAuth import malcolm_utils -from malcolm_utils import eprint, str2bool +from malcolm_utils import eprint, str2bool, ParseCurlFile ################################################################################################### debug = False @@ -120,7 +119,7 @@ def main(): try: parser.error = parser.exit args = parser.parse_args() - except Exception as e: + except Exception: parser.print_help() exit(2) @@ -134,9 +133,7 @@ def main(): args.opensearchIsLocal = args.opensearchIsLocal or (args.opensearchUrl == 'http://opensearch:9200') opensearchCreds = ( - malcolm_common.ParseCurlFile(args.opensearchCurlRcFile) - if (not args.opensearchIsLocal) - else defaultdict(lambda: None) + ParseCurlFile(args.opensearchCurlRcFile) if (not args.opensearchIsLocal) else defaultdict(lambda: None) ) if not args.opensearchUrl: if args.opensearchIsLocal: diff --git a/shared/bin/pcap_processor.py b/shared/bin/pcap_processor.py index 1637e8037..a171cfe6b 100755 --- a/shared/bin/pcap_processor.py +++ b/shared/bin/pcap_processor.py @@ -21,9 +21,19 @@ import time import zmq -from pcap_utils import * +from pcap_utils import ( + FILE_INFO_DICT_NAME, + FILE_INFO_DICT_NODE, + FILE_INFO_DICT_SIZE, + FILE_INFO_DICT_TAGS, + FILE_INFO_FILE_MIME, + FILE_INFO_FILE_TYPE, + PCAP_MIME_TYPES, + PCAP_TOPIC_PORT, + tags_from_filename, +) import malcolm_utils -from malcolm_utils import eprint, str2bool, AtomicInt +from malcolm_utils import eprint, str2bool, AtomicInt, run_process from multiprocessing.pool import ThreadPool from collections import deque @@ -730,7 +740,7 @@ def main(): # start worker threads which will pull filenames/tags to be processed by capture if processingMode == PCAP_PROCESSING_MODE_ARKIME: - scannerThreads = ThreadPool( + ThreadPool( args.threads, arkimeCaptureFileWorker, ( @@ -745,7 +755,7 @@ def main(): ), ) elif processingMode == PCAP_PROCESSING_MODE_ZEEK: - scannerThreads = ThreadPool( + ThreadPool( args.threads, zeekFileWorker, ( @@ -762,7 +772,7 @@ def main(): ), ) elif processingMode == PCAP_PROCESSING_MODE_SURICATA: - scannerThreads = ThreadPool( + ThreadPool( args.threads, suricataFileWorker, ( @@ -788,7 +798,7 @@ def main(): # accept a file info dict from new_files_socket as json try: fileInfo = json.loads(new_files_socket.recv_string()) - except zmq.Again as timeout: + except zmq.Again: # no file received due to timeout, we'll go around and try again if verboseDebug: eprint(f"{scriptName}:\t🕑\t(recv)") diff --git a/shared/bin/pcap_watcher.py b/shared/bin/pcap_watcher.py index 0b3062fe3..f7eb13ceb 100755 --- a/shared/bin/pcap_watcher.py +++ b/shared/bin/pcap_watcher.py @@ -14,7 +14,6 @@ import json import logging import magic -import malcolm_common import os import pathlib import pyinotify @@ -23,9 +22,19 @@ import time import zmq -from pcap_utils import * +from pcap_utils import ( + FILE_INFO_DICT_NAME, + FILE_INFO_DICT_NODE, + FILE_INFO_DICT_SIZE, + FILE_INFO_DICT_TAGS, + FILE_INFO_FILE_MIME, + FILE_INFO_FILE_TYPE, + PCAP_MIME_TYPES, + PCAP_TOPIC_PORT, + tags_from_filename, +) import malcolm_utils -from malcolm_utils import eprint, str2bool +from malcolm_utils import eprint, str2bool, ParseCurlFile, remove_prefix, touch from collections import defaultdict @@ -234,7 +243,7 @@ def _method_name(self, event): self.topic_socket.send_string(json.dumps(fileInfo)) if debug: eprint(f"{scriptName}:\t📫\t{fileInfo}") - except zmq.Again as timeout: + except zmq.Again: if verboseDebug: eprint(f"{scriptName}:\t🕑\t{event.pathname}") @@ -446,9 +455,7 @@ def main(): args.opensearchIsLocal = args.opensearchIsLocal or (args.opensearchUrl == 'http://opensearch:9200') opensearchCreds = ( - malcolm_common.ParseCurlFile(args.opensearchCurlRcFile) - if (not args.opensearchIsLocal) - else defaultdict(lambda: None) + ParseCurlFile(args.opensearchCurlRcFile) if (not args.opensearchIsLocal) else defaultdict(lambda: None) ) if not args.opensearchUrl: if args.opensearchIsLocal: diff --git a/shared/bin/sensor-capture-disk-config.py b/shared/bin/sensor-capture-disk-config.py index afa77cf78..b5f82f7e0 100755 --- a/shared/bin/sensor-capture-disk-config.py +++ b/shared/bin/sensor-capture-disk-config.py @@ -17,9 +17,10 @@ import argparse import fileinput from collections import defaultdict -from sensorcommon import * from fstab import Fstab +from malcolm_utils import remove_prefix, str2bool, sizeof_fmt, run_process, eprint + MINIMUM_CAPTURE_DEVICE_BYTES = 100 * 1024 * 1024 * 1024 # 100GiB CAPTURE_MOUNT_ROOT_PATH = "/capture" CAPTURE_MOUNT_PCAP_DIR = "pcap" @@ -37,6 +38,7 @@ debug = False + ################################################################################################### # used to map output of lsblk class PartitionInfo: @@ -74,6 +76,7 @@ def CreateMapperDeviceName(device): ################################################################################################### + ################################################################################################### # determine if a device (eg., sda) is an internal (True) or removable (False) device def IsInternalDevice(name): @@ -124,7 +127,6 @@ def GetDeviceSize(device): # main ################################################################################################### def main(): - # to parse fdisk output, look for partitions after partitions header line fdisk_pars_begin_pattern = re.compile(r'^Device\s+Start\s+End\s+Sectors\s+Size\s+Type\s*$') # to parse partitions from fdisk output after parted creates partition table @@ -194,9 +196,9 @@ def main(): # unmount existing mounts if requested if args.umount and (not args.dryrun): - if (not args.interactive) or YesOrNo(f'Unmount any mounted capture path(s)?'): + if (not args.interactive) or YesOrNo('Unmount any mounted capture path(s)?'): if debug: - eprint(f"Attempting unmount of capture path(s)...") + eprint("Attempting unmount of capture path(s)...") run_process(f"umount {os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_PCAP_DIR)}") run_process(f"umount {os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_ZEEK_DIR)}") run_process(f"umount {CAPTURE_MOUNT_ROOT_PATH}") @@ -212,7 +214,7 @@ def main(): if debug: for line in cryptOut: eprint(f"\t{line}") - _, reloadOut = run_process(f"systemctl daemon-reload") + _, reloadOut = run_process("systemctl daemon-reload") # check existing mounts, if the capture path(s) are already mounted, then abort with open('/proc/mounts', 'r') as f: @@ -225,7 +227,7 @@ def main(): f"It appears there is already a device mounted under {CAPTURE_MOUNT_ROOT_PATH} at {mountPoint}." ) eprint( - f"If you wish to continue, you may run this script with the '-u|--umount' option to umount first." + "If you wish to continue, you may run this script with the '-u|--umount' option to umount first." ) eprint() parser.print_help() @@ -234,7 +236,7 @@ def main(): # get physical disks, partitions, device maps, and any mountpoints and UUID associated allDisks = defaultdict(list) if debug: - eprint(f"Block devices:") + eprint("Block devices:") for device in GetInternalDevices(): ecode, deviceTree = run_process( f'/bin/lsblk -o name,uuid,mountpoint --paths --noheadings /dev/{device}', stdout=True, stderr=False @@ -308,7 +310,6 @@ def main(): eprint(f"Device candidates: {[(x, sizeof_fmt(GetDeviceSize(x))) for x in candidateDevs]}") if len(candidateDevs) > 0: - if args.encrypt: # create keyfile (will be on the encrypted system drive, and used to automatically unlock the encrypted capture drives) with open(CAPTURE_CRYPT_KEYFILE, 'wb') as f: @@ -318,7 +319,6 @@ def main(): # partition/format each candidate device for device in candidateDevs: - # we only need at most two drives (one for pcap, one for zeek), or at least one if len(formattedDevs) >= 2: break @@ -326,7 +326,6 @@ def main(): if (not args.interactive) or YesOrNo( f'Partition and format {device}{" (dry-run)" if args.dryrun else ""}?' ): - if args.dryrun: eprint(f"Partitioning {device} (dry run only)...") eprint( @@ -366,7 +365,6 @@ def main(): pars.append(match.group('device')) if len(pars) == 1: - parDev = pars[0] parUuid = str(uuid.uuid4()) parMapperDev = None @@ -386,7 +384,7 @@ def main(): else: print(line) - _, reloadOut = run_process(f"systemctl daemon-reload") + _, reloadOut = run_process("systemctl daemon-reload") # for good measure, run luksErase in case it was a previous luks volume if debug: @@ -401,7 +399,7 @@ def main(): for line in cryptOut: eprint(f"\t{line}") - _, reloadOut = run_process(f"systemctl daemon-reload") + _, reloadOut = run_process("systemctl daemon-reload") # luks volume creation @@ -418,7 +416,6 @@ def main(): for line in cryptOut: eprint(f"\t{line}") if ecode == 0: - # open the luks volume in /dev/mapper/ if debug: eprint(f"Running crypsetup luksOpen on {device}...") @@ -468,7 +465,9 @@ def main(): ) else: - eprint(f"Error {ecode} formatting {formatPath}, giving up on {device}") + eprint( + f"Error {ecode} formatting {parMapperDev if args.encrypt else parDev}, giving up on {device}" + ) else: eprint( @@ -494,7 +493,7 @@ def main(): run_process(f"umount {os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_ZEEK_DIR)}") run_process(f"umount {CAPTURE_MOUNT_ROOT_PATH}") - _, reloadOut = run_process(f"systemctl daemon-reload") + _, reloadOut = run_process("systemctl daemon-reload") # clean out any previous fstab entries that might be interfering from previous configurations if Fstab.remove_by_mountpoint(os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_PCAP_DIR), path=FSTAB_FILE): @@ -512,7 +511,7 @@ def main(): eprint(f"Removed previous {CAPTURE_MOUNT_ROOT_PATH} mount from {FSTAB_FILE}") # reload tab files with systemctl - _, reloadOut = run_process(f"systemctl daemon-reload") + _, reloadOut = run_process("systemctl daemon-reload") # get the GID of the group of the user(s) that will be doing the capture try: @@ -521,7 +520,7 @@ def main(): netdevGuid = int(guidGetOut[0].split(':')[2]) else: netdevGuid = -1 - except: + except Exception: netdevGuid = -1 # rmdir any mount directories that might be interfering from previous configurations @@ -558,7 +557,7 @@ def main(): entry = Fstab.add( device=f"{par.mapper}", mountpoint=par.mount, - options=f"defaults,inode64,noatime,rw,auto,user,x-systemd.device-timeout=600s", + options="defaults,inode64,noatime,rw,auto,user,x-systemd.device-timeout=600s", fs_passno=2, filesystem='xfs', path=FSTAB_FILE, @@ -567,7 +566,7 @@ def main(): entry = Fstab.add( device=f"UUID={par.uuid}", mountpoint=par.mount, - options=f"defaults,inode64,noatime,rw,auto,user,x-systemd.device-timeout=600s", + options="defaults,inode64,noatime,rw,auto,user,x-systemd.device-timeout=600s", fs_passno=2, filesystem='xfs', path=FSTAB_FILE, @@ -575,11 +574,10 @@ def main(): eprint(f'Added "{entry}" to {FSTAB_FILE} for {par.partition}') # reload tab files with systemctl - _, reloadOut = run_process(f"systemctl daemon-reload") + _, reloadOut = run_process("systemctl daemon-reload") # mount the partitions and create a directory with user permissions for par in formattedDevs: - ecode, mountOut = run_process(f"mount {par.mount}") if ecode == 0: if debug: @@ -629,7 +627,7 @@ def main(): eprint(f"Error {ecode} mounting {par.partition}") else: - eprint(f"Could not find any unmounted devices greater than 100GB, giving up") + eprint("Could not find any unmounted devices greater than 100GB, giving up") if __name__ == '__main__': diff --git a/shared/bin/sensorcommon.py b/shared/bin/sensorcommon.py index 693462126..19d97f3ff 100644 --- a/shared/bin/sensorcommon.py +++ b/shared/bin/sensorcommon.py @@ -11,14 +11,13 @@ import ssl import sys import urllib.request +import subprocess from base64 import b64encode from bs4 import BeautifulSoup from bs4.element import Comment -from contextlib import closing from http.client import HTTPSConnection, HTTPConnection -from multiprocessing import RawValue -from threading import Lock +from subprocess import PIPE, STDOUT, Popen, CalledProcessError from malcolm_utils import run_subprocess @@ -112,17 +111,6 @@ def test_connection( return status, message, output -################################################################################################### -# test if a remote port is open -def check_socket(host, port): - with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock: - sock.settimeout(10) - if sock.connect_ex((host, port)) == 0: - return True - else: - return False - - ################################################################################################### # determine a list of available (non-virtual) adapters (Iface's) def get_available_adapters(): @@ -137,12 +125,12 @@ def get_available_adapters(): try: with open(f"/sys/class/net/{adapter}/address", 'r') as f: mac_address = f.readline().strip() - except: + except Exception: pass try: with open(f"/sys/class/net/{adapter}/speed", 'r') as f: speed = f.readline().strip() - except: + except Exception: pass description = f"{mac_address} ({speed} Mbits/sec)" iface = Iface(adapter, description) diff --git a/shared/bin/suricata_config_populate.py b/shared/bin/suricata_config_populate.py index aee403487..85977b525 100755 --- a/shared/bin/suricata_config_populate.py +++ b/shared/bin/suricata_config_populate.py @@ -27,7 +27,7 @@ from shutil import move as MoveFile, copyfile as CopyFile from subprocess import PIPE, Popen -from malcolm_utils import val2bool +from malcolm_utils import val2bool, deep_set, pushd, run_process ################################################################################################### args = None @@ -52,7 +52,7 @@ def __call__(self, repr, data): ################################################################################################### def ObjToYamlStrLines(obj, options=None): outputStr = None - if options == None: + if options is None: options = {} yaml = YAML() @@ -527,7 +527,7 @@ def GetRuleSources(requireRulesExist=False): ruleSources = [] - if val2bool(DEFAULT_VARS['CUSTOM_RULES_ONLY']) == False: + if not val2bool(DEFAULT_VARS['CUSTOM_RULES_ONLY']): ruleSources.append('suricata.rules') customRuleFiles = ( @@ -536,7 +536,7 @@ def GetRuleSources(requireRulesExist=False): else [] ) - if (DEFAULT_VARS['CUSTOM_RULES_DIR'] is not None) and ((requireRulesExist == False) or (len(customRuleFiles) > 0)): + if (DEFAULT_VARS['CUSTOM_RULES_DIR'] is not None) and ((not requireRulesExist) or (len(customRuleFiles) > 0)): ruleSources.append(os.path.join(DEFAULT_VARS['CUSTOM_RULES_DIR'], '*.rules')) return ruleSources @@ -1127,11 +1127,11 @@ def main(): if DEFAULT_VARS['RUN_DIR'] is not None and os.path.isdir(os.path.join(DEFAULT_VARS['RUN_DIR'])): try: os.remove(os.path.join(DEFAULT_VARS['RUN_DIR'], 'suricata.pid')) - except: + except Exception: pass try: os.remove(os.path.join(DEFAULT_VARS['RUN_DIR'], 'suricata-command.socket')) - except: + except Exception: pass diff --git a/shared/bin/suricata_update_config_populate.py b/shared/bin/suricata_update_config_populate.py index cb72553a4..6dda3c649 100755 --- a/shared/bin/suricata_update_config_populate.py +++ b/shared/bin/suricata_update_config_populate.py @@ -52,7 +52,7 @@ def __call__(self, repr, data): ################################################################################################### def ObjToYamlStrLines(obj, options=None): outputStr = None - if options == None: + if options is None: options = {} yaml = YAML() @@ -162,7 +162,6 @@ def main(): args.output if args.output else args.input if args.inplace else inFileParts[0] + "_new" + inFileParts[1] ) - argsOrigVerbose = args.verbose args.verbose = logging.CRITICAL - (10 * args.verbose) if args.verbose > 0 else 0 logging.basicConfig( level=args.verbose, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py index 080cdce03..6971e2fab 100644 --- a/shared/bin/watch_common.py +++ b/shared/bin/watch_common.py @@ -4,7 +4,7 @@ import os import time -from malcolm_utils import AtomicInt, ContextLockedOrderedDict +from malcolm_utils import AtomicInt, ContextLockedOrderedDict, same_file_or_dir from watchdog.events import ( FileSystemEventHandler, @@ -154,7 +154,7 @@ def WatchAndProcessDirectory( observer.start() try: workerThreadCount = AtomicInt(value=0) - workerThreads = ThreadPool( + ThreadPool( 1, ProcessFileEventWorker( [ @@ -175,7 +175,7 @@ def WatchAndProcessDirectory( if shuttingDown[0]: raise WatchdogShutdown() - except WatchdogShutdown as wdshut: + except WatchdogShutdown: observer.unschedule_all() finally: diff --git a/shared/bin/zeek_carve_logger.py b/shared/bin/zeek_carve_logger.py index 9077c39cf..8e6c5dc21 100755 --- a/shared/bin/zeek_carve_logger.py +++ b/shared/bin/zeek_carve_logger.py @@ -10,7 +10,6 @@ ################################################################################################### import argparse -import datetime import json import os import pathlib @@ -25,7 +24,23 @@ from contextlib import nullcontext from datetime import datetime -from zeek_carve_utils import * +from zeek_carve_utils import ( + BroSignatureLine, + extracted_filespec_to_fields, + FILE_SCAN_RESULT_DESCRIPTION, + FILE_SCAN_RESULT_ENGINES, + FILE_SCAN_RESULT_FILE, + FILE_SCAN_RESULT_HITS, + FILE_SCAN_RESULT_MESSAGE, + FILE_SCAN_RESULT_SCANNER, + PRESERVE_ALL, + PRESERVE_NONE, + PRESERVE_PRESERVED_DIR_NAME, + PRESERVE_QUARANTINED, + PRESERVE_QUARANTINED_DIR_NAME, + SINK_PORT, + ZEEK_SIGNATURE_NOTICE, +) import malcolm_utils from malcolm_utils import eprint, str2bool, AtomicInt, same_file_or_dir @@ -240,7 +255,7 @@ def main(): scanResult = json.loads(scanned_files_socket.recv_string()) if debug: eprint(f"{scriptName}:\t📨\t{scanResult}") - except zmq.Again as timeout: + except zmq.Again: scanResult = None if verboseDebug: eprint(f"{scriptName}:\t🕑\t(recv)") diff --git a/shared/bin/zeek_carve_scanner.py b/shared/bin/zeek_carve_scanner.py index aab0a3b73..3d4e61a48 100755 --- a/shared/bin/zeek_carve_scanner.py +++ b/shared/bin/zeek_carve_scanner.py @@ -22,7 +22,36 @@ from multiprocessing.pool import ThreadPool -from zeek_carve_utils import * +from zeek_carve_utils import ( + AnalyzerResult, + AnalyzerScan, + BroSignatureLine, + CapaScan, + CarvedFileSubscriberThreaded, + ClamAVScan, + extracted_filespec_to_fields, + FILE_SCAN_RESULT_DESCRIPTION, + FILE_SCAN_RESULT_ENGINES, + FILE_SCAN_RESULT_FILE, + FILE_SCAN_RESULT_FILE_SIZE, + FILE_SCAN_RESULT_HITS, + FILE_SCAN_RESULT_MESSAGE, + FILE_SCAN_RESULT_SCANNER, + FILE_SCAN_RESULT_FILE_TYPE, + FileScanProvider, + PRESERVE_ALL, + PRESERVE_NONE, + PRESERVE_PRESERVED_DIR_NAME, + PRESERVE_QUARANTINED, + PRESERVE_QUARANTINED_DIR_NAME, + SINK_PORT, + VENTILATOR_PORT, + VirusTotalSearch, + YARA_CUSTOM_RULES_DIR, + YARA_RULES_DIR, + YaraScan, + ZEEK_SIGNATURE_NOTICE, +) import malcolm_utils from malcolm_utils import eprint, str2bool, AtomicInt @@ -134,7 +163,7 @@ def scanFileWorker(checkConnInfo, carvedFileSub): if debug: eprint(f"{scriptName}[{scanWorkerId}]:\t🇷\t{checkConnInfo.scanner_name()}") - except zmq.Again as timeout: + except zmq.Again: # todo: what to do here? if verboseDebug: eprint(f"{scriptName}[{scanWorkerId}]:\t🕑\t{checkConnInfo.scanner_name()} 🇷") @@ -231,7 +260,7 @@ def scanFileWorker(checkConnInfo, carvedFileSub): if debug: eprint(f"{scriptName}[{scanWorkerId}]:\t✅\t{fileName}") - except zmq.Again as timeout: + except zmq.Again: # todo: what to do here? if verboseDebug: eprint(f"{scriptName}[{scanWorkerId}]:\t🕑\t{fileName}") @@ -249,7 +278,7 @@ def scanFileWorker(checkConnInfo, carvedFileSub): scannerRegistered = False if debug: eprint(f"{scriptName}[{scanWorkerId}]:\t🙃\t{checkConnInfo.scanner_name()}") - except zmq.Again as timeout: + except zmq.Again: # todo: what to do here? if verboseDebug: eprint(f"{scriptName}[{scanWorkerId}]:\t🕑\t{checkConnInfo.scanner_name()} 🙃") @@ -438,7 +467,7 @@ def main(): ) # start scanner threads which will pull filenames to be scanned and send the results to the logger - scannerThreads = ThreadPool(checkConnInfo.max_requests(), scanFileWorker, ([checkConnInfo, carvedFileSub])) + ThreadPool(checkConnInfo.max_requests(), scanFileWorker, ([checkConnInfo, carvedFileSub])) while not shuttingDown: if pdbFlagged: pdbFlagged = False diff --git a/shared/bin/zeek_carve_utils.py b/shared/bin/zeek_carve_utils.py index bfa87acae..ebde15b34 100644 --- a/shared/bin/zeek_carve_utils.py +++ b/shared/bin/zeek_carve_utils.py @@ -24,7 +24,7 @@ from threading import get_ident from threading import Lock -from malcolm_utils import eprint, sha256sum, run_process +from malcolm_utils import eprint, sha256sum, run_process, AtomicInt, dictsearch ################################################################################################### VENTILATOR_PORT = 5987 @@ -316,7 +316,7 @@ def Pull(self, scanWorkerId=0): # accept a fileinfo dict from newFilesSocket try: fileinfo.update(json.loads(self.newFilesSocket.recv_string())) - except zmq.Again as timeout: + except zmq.Again: # no file received due to timeout, return empty dict. which means no file available pass @@ -423,7 +423,7 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo if allowed: try: response = requests.get(VTOT_URL, params={'apikey': self.apiKey, 'resource': sha256sum(fileName)}) - except requests.exceptions.RequestException as e: + except requests.exceptions.RequestException: # things are bad return None @@ -444,7 +444,7 @@ def check_result(self, submissionResponse): if submissionResponse is not None: try: result.success = submissionResponse.ok - except: + except Exception: pass try: @@ -488,7 +488,7 @@ def format(fileName, response): scans = { engine: resp['scans'][engine] for engine in resp['scans'] - if ('detected' in resp['scans'][engine]) and (resp['scans'][engine]['detected'] == True) + if ('detected' in resp['scans'][engine]) and (resp['scans'][engine]['detected']) } hits = defaultdict(list) for k, v in scans.items(): @@ -549,6 +549,8 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo # while limit only repeats if block=True while (not allowed) and (not clamavResult.finished): + nowTime = int(time.time()) + if not connected: if self.verboseDebug: eprint( @@ -684,9 +686,8 @@ def __init__(self, debug=False, verboseDebug=False, rulesDirs=[], reqLimit=None) if file.startswith(".") or file.startswith("~") or file.startswith("_"): continue filename = os.path.join(root, file) - extension = os.path.splitext(file)[1].lower() try: - testCompile = yara.compile(filename) + yara.compile(filename) self.ruleFilespecs[filename] = filename except yara.SyntaxError as e: if self.debug: @@ -722,13 +723,14 @@ def check_interval(): def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeout=YARA_SUBMIT_TIMEOUT_SEC): yaraResult = AnalyzerResult() allowed = False - matches = [] # timeout only applies if block=True timeoutTime = int(time.time()) + timeout # while limit only repeats if block=True while (not allowed) and (not yaraResult.finished): + nowTime = int(time.time()) + # first make sure we haven't exceeded rate limits if self.scanningFilesCount.increment() <= self.reqLimit: # we've got fewer than the allowed requests open, so we're good to go! @@ -854,6 +856,8 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo # while limit only repeats if block=True while (not allowed) and (not capaResult.finished): + nowTime = int(time.time()) + # first make sure we haven't exceeded rate limits if self.scanningFilesCount.increment() <= self.reqLimit: # we've got fewer than the allowed requests open, so we're good to go! @@ -935,7 +939,7 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo try: if os.path.isfile(fileName + CAPA_VIV_SUFFIX): os.remove(fileName + CAPA_VIV_SUFFIX) - except Exception as fe: + except Exception: pass elif block and (nowTime < timeoutTime): diff --git a/shared/bin/zeek_carve_watcher.py b/shared/bin/zeek_carve_watcher.py index 70504f40f..c60bbb6e4 100755 --- a/shared/bin/zeek_carve_watcher.py +++ b/shared/bin/zeek_carve_watcher.py @@ -22,9 +22,16 @@ import time import zmq -from zeek_carve_utils import * +from zeek_carve_utils import ( + CAPA_VIV_MIME, + CAPA_VIV_SUFFIX, + FILE_SCAN_RESULT_FILE, + FILE_SCAN_RESULT_FILE_SIZE, + FILE_SCAN_RESULT_FILE_TYPE, + VENTILATOR_PORT, +) -from malcolm_utils import touch +from malcolm_utils import touch, eprint, str2bool ################################################################################################### MINIMUM_CHECKED_FILE_SIZE_DEFAULT = 64 @@ -101,7 +108,7 @@ def _method_name(self, event): self.ventilator_socket.send_string(fileInfo) if debug: eprint(f"{scriptName}:\t📫\t{event.pathname}") - except zmq.Again as timeout: + except zmq.Again: if verboseDebug: eprint(f"{scriptName}:\t🕑\t{event.pathname}") @@ -268,7 +275,7 @@ def main(): else: preexistingDir = False if debug: - eprint(f'{scriptname}: creating "{args.baseDir}" to monitor') + eprint(f'{scriptName}: creating "{args.baseDir}" to monitor') pathlib.Path(args.baseDir).mkdir(parents=False, exist_ok=True) # if recursion was requested, get list of directories to monitor diff --git a/shared/bin/zeek_carved_http_server.py b/shared/bin/zeek_carved_http_server.py index 7ed255a5d..8b385c9de 100755 --- a/shared/bin/zeek_carved_http_server.py +++ b/shared/bin/zeek_carved_http_server.py @@ -149,7 +149,13 @@ def main(): help=f"Encrypt files with aes-256-cbc ({defaultEncrypt})", ) parser.add_argument( - '-k', '--key', dest='key', help=f"File encryption key", metavar='', type=str, default=defaultKey + '-k', + '--key', + dest='key', + help="File encryption key", + metavar='', + type=str, + default=defaultKey, ) try: parser.error = parser.exit diff --git a/shared/bin/zeek_intel_from_threat_feed.py b/shared/bin/zeek_intel_from_threat_feed.py index 3ab73ce13..214f35b48 100755 --- a/shared/bin/zeek_intel_from_threat_feed.py +++ b/shared/bin/zeek_intel_from_threat_feed.py @@ -13,9 +13,11 @@ import logging import os import sys -import malcolm_utils import zeek_threat_feed_utils +import malcolm_utils +from malcolm_utils import nullcontext + ################################################################################################### script_name = os.path.basename(__file__) script_path = os.path.dirname(os.path.realpath(__file__)) @@ -180,7 +182,7 @@ def main(): inputQueue = deque() inputQueue.extend(args.input) workerThreadCount = malcolm_utils.AtomicInt(value=0) - workerThreads = ThreadPool( + ThreadPool( args.threads, zeek_threat_feed_utils.ProcessThreatInputWorker, ( @@ -188,6 +190,7 @@ def main(): inputQueue, zeekPrinter, since, + defaultNow, workerThreadCount, logging, ], diff --git a/shared/bin/zeek_threat_feed_utils.py b/shared/bin/zeek_threat_feed_utils.py index 664581a08..e5446a6aa 100644 --- a/shared/bin/zeek_threat_feed_utils.py +++ b/shared/bin/zeek_threat_feed_utils.py @@ -487,7 +487,7 @@ def ProcessSTIX( except STIXError as ve: if self.logger is not None: - self.logger.warning(f"{type(ve).__name__} parsing '{infile}': {ve}") + self.logger.warning(f"{type(ve).__name__}: {ve}") def ProcessMISP( self, @@ -520,7 +520,7 @@ def ProcessMISP( certaintyTags = [x.name.replace('"', '') for x in event.Tag if x.name.startswith('osint:certainty')] try: certainty = float(certaintyTags[0].split('=')[-1]) if len(certaintyTags) > 0 else None - except ValueError as ve: + except ValueError: certainty = None else: tags = [] @@ -529,7 +529,7 @@ def ProcessMISP( for attribute in event.attributes: # map event attribute to Zeek value(s) if ( - ((not hasattr(attribute, 'deleted')) or (attribute.deleted == False)) + ((not hasattr(attribute, 'deleted')) or (not attribute.deleted)) and ((self.since is None) or (event.timestamp >= self.since) or (attribute.timestamp >= self.since)) and ( vals := map_misp_attribute_to_zeek( @@ -555,12 +555,13 @@ def ProcessMISP( def ProcessThreatInputWorker(threatInputWorkerArgs): - inputQueue, zeekPrinter, since, workerThreadCount, logger = ( + inputQueue, zeekPrinter, since, defaultNow, workerThreadCount, logger = ( threatInputWorkerArgs[0], threatInputWorkerArgs[1], threatInputWorkerArgs[2], threatInputWorkerArgs[3], threatInputWorkerArgs[4], + threatInputWorkerArgs[5], ) with workerThreadCount as workerId: @@ -592,7 +593,7 @@ def ProcessThreatInputWorker(threatInputWorkerArgs): # TODO: is this always the case? anything other than "Event", or multiple objects? # MISP input file zeekPrinter.ProcessMISP( - mispJson, + infileJson, source=[os.path.splitext(os.path.basename(inarg))[0]], ) From 4ec1dcfee5db4e6dc416cbfd1215afb6c6098d18 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 3 Apr 2023 12:25:15 -0600 Subject: [PATCH 077/239] major refactoring work and checking flake output --- arkime/scripts/bs4_remove_div.py | 5 +- ...ilebeat-clean-zeeklogs-processed-folder.py | 11 +-- logstash/scripts/ja3_build_list.py | 1 + netbox/scripts/netbox_init.py | 16 ++-- netbox/scripts/netbox_library_import.py | 48 +++++------ scripts/control.py | 73 +++++++++++------ scripts/install.py | 80 +++++++++++++------ scripts/malcolm_common.py | 4 +- scripts/zeek_script_to_malcolm_boilerplate.py | 1 + .../interface/sensor_interface/routes.py | 7 +- .../sensor_interface/sysquery/__init__.py | 1 - .../sensor_interface/sysquery/sys_service.py | 4 +- 12 files changed, 151 insertions(+), 100 deletions(-) diff --git a/arkime/scripts/bs4_remove_div.py b/arkime/scripts/bs4_remove_div.py index dee992ec7..261fcc439 100755 --- a/arkime/scripts/bs4_remove_div.py +++ b/arkime/scripts/bs4_remove_div.py @@ -16,15 +16,12 @@ origPath = os.getcwd() ################################################################################################### -if not PY3: - if hasattr(__builtins__, 'raw_input'): - input = raw_input - try: FileNotFoundError except NameError: FileNotFoundError = IOError + ################################################################################################### # print to stderr def eprint(*args, **kwargs): diff --git a/filebeat/scripts/filebeat-clean-zeeklogs-processed-folder.py b/filebeat/scripts/filebeat-clean-zeeklogs-processed-folder.py index 448fc045c..736e7c0e7 100755 --- a/filebeat/scripts/filebeat-clean-zeeklogs-processed-folder.py +++ b/filebeat/scripts/filebeat-clean-zeeklogs-processed-folder.py @@ -43,7 +43,6 @@ def silentRemove(filename): def checkFile(filename, filebeatReg=None, checkLogs=True, checkArchives=True): - try: # first check to see if it's in the filebeat registry if filebeatReg is not None: @@ -70,16 +69,15 @@ def checkFile(filename, filebeatReg=None, checkLogs=True, checkArchives=True): fuserProcess.communicate() fuserExitCode = fuserProcess.wait() if fuserExitCode != 0: - # the file is not in use, let's check it's mtime/ctime logTime = max(os.path.getctime(filename), os.path.getmtime(filename)) lastUseTime = nowTime - logTime # get the file type fileType = magic.from_file(filename, mime=True) - if (checkLogs == True) and (cleanLogSeconds > 0) and (fileType == logMimeType): + if (checkLogs is True) and (cleanLogSeconds > 0) and (fileType == logMimeType): cleanSeconds = cleanLogSeconds - elif (checkArchives == True) and (cleanZipSeconds > 0) and archiveMimeTypeRegex.match(fileType) is not None: + elif (checkArchives is True) and (cleanZipSeconds > 0) and archiveMimeTypeRegex.match(fileType) is not None: cleanSeconds = cleanZipSeconds else: # not a file we're going to be messing with @@ -90,7 +88,7 @@ def checkFile(filename, filebeatReg=None, checkLogs=True, checkArchives=True): print('removing old file "{}" ({}, used {} seconds ago)'.format(filename, fileType, lastUseTime)) silentRemove(filename) - except FileNotFoundError as fnf: + except FileNotFoundError: # file's already gone, oh well pass @@ -99,7 +97,6 @@ def checkFile(filename, filebeatReg=None, checkLogs=True, checkArchives=True): def pruneFiles(): - if (cleanLogSeconds <= 0) and (cleanZipSeconds <= 0): # disabled, don't do anything return @@ -143,7 +140,7 @@ def pruneFiles(): candidateDirs.sort(reverse=True) candidateDirs.sort(key=len, reverse=True) candidateDirsAndTimes = zip(candidateDirs, [os.path.getmtime(dirToRm) for dirToRm in candidateDirs]) - for (dirToRm, dirTime) in candidateDirsAndTimes: + for dirToRm, dirTime in candidateDirsAndTimes: dirAge = nowTime - dirTime if dirAge >= cleanDirSeconds: try: diff --git a/logstash/scripts/ja3_build_list.py b/logstash/scripts/ja3_build_list.py index 42ec77f71..2ff1ab966 100755 --- a/logstash/scripts/ja3_build_list.py +++ b/logstash/scripts/ja3_build_list.py @@ -31,6 +31,7 @@ except NameError: FileNotFoundError = IOError + ################################################################################################### # print to stderr def eprint(*args, **kwargs): diff --git a/netbox/scripts/netbox_init.py b/netbox/scripts/netbox_init.py index 88f27b068..1f04b14b2 100755 --- a/netbox/scripts/netbox_init.py +++ b/netbox/scripts/netbox_init.py @@ -26,6 +26,7 @@ script_path = os.path.dirname(os.path.realpath(__file__)) orig_path = os.getcwd() + ################################################################################################### def get_iterable(x): if isinstance(x, Iterable) and not isinstance(x, str): @@ -36,7 +37,7 @@ def get_iterable(x): def is_ip_address(x): try: - ip = ipaddress.ip_address(x) + ipaddress.ip_address(x) return True except Exception: return False @@ -44,7 +45,7 @@ def is_ip_address(x): def is_ip_v4_address(x): try: - ip = ipaddress.IPv4Address(x) + ipaddress.IPv4Address(x) return True except Exception: return False @@ -52,7 +53,7 @@ def is_ip_v4_address(x): def is_ip_v6_address(x): try: - ip = ipaddress.IPv6Address(x) + ipaddress.IPv6Address(x) return True except Exception: return False @@ -60,7 +61,7 @@ def is_ip_v6_address(x): def is_ip_network(x): try: - ip = ipaddress.ip_network(x) + ipaddress.ip_network(x) return True except Exception: return False @@ -274,14 +275,14 @@ def main(): # wait for a good connection while args.wait: try: - sitesConnTest = [x.name for x in nb.dcim.sites.all()] + [x.name for x in nb.dcim.sites.all()] break except Exception as e: logging.info(f"{type(e).__name__}: {e}") logging.debug("retrying in a few seconds...") time.sleep(5) - ###### GROUPS ################################################################################################ + # GROUPS ##################################################################################################### DEFAULT_GROUP_NAMES = ( args.staffGroupName, args.defaultGroupName, @@ -303,7 +304,7 @@ def main(): except Exception as e: logging.error(f"{type(e).__name__} processing groups: {e}") - ####### PERMISSIONS ########################################################################################### + # PERMISSIONS ################################################################################################## DEFAULT_PERMISSIONS = { f'{args.staffGroupName}_permission': { 'name': f'{args.staffGroupName}_permission', @@ -531,7 +532,6 @@ def main(): with open(args.netMapFileName) as f: netMapJson = json.load(f) if netMapJson is not None: - # create new VRFs vrfPreExisting = {x.name: x for x in nb.ipam.vrfs.all()} logging.debug(f"VRFs (before): { {k:v.id for k, v in vrfPreExisting.items()} }") diff --git a/netbox/scripts/netbox_library_import.py b/netbox/scripts/netbox_library_import.py index 2e3a1abff..034ec0306 100644 --- a/netbox/scripts/netbox_library_import.py +++ b/netbox/scripts/netbox_library_import.py @@ -19,14 +19,13 @@ def slugFormat(name): - return re.sub('\W+', '-', name.lower()) + return re.sub(r'\W+', '-', name.lower()) YAML_EXTENSIONS = ['yml', 'yaml'] def getFiles(library_dir, vendors=None): - files = [] discoveredVendors = [] base_path = os.path.join(library_dir, 'device-types', '') @@ -91,7 +90,7 @@ def readYAMl(files, **kwargs): with open(file, 'r') as stream: try: data = yaml.safe_load(stream) - except yaml.YAMLError as exc: + except yaml.YAMLError: continue manufacturer = data['manufacturer'] data['manufacturer'] = {} @@ -107,7 +106,6 @@ def readYAMl(files, **kwargs): def read_yaml_modules(files, **kwargs): - slugs = kwargs.get('slugs', None) module_types = [] manufacturers = [] @@ -115,7 +113,7 @@ def read_yaml_modules(files, **kwargs): with open(file, 'r') as stream: try: data = yaml.safe_load(stream) - except yaml.YAMLError as exc: + except yaml.YAMLError: continue manufacturer = data['manufacturer'] data['manufacturer'] = {} @@ -147,7 +145,7 @@ def createManufacturers(vendors, nb, counter=None): if counter is not None: for man in manSuccess: counter.update({'manufacturer': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -169,7 +167,7 @@ def createInterfaces(interfaces, deviceType, nb, counter=None): if counter is not None: for intf in ifSuccess: counter.update({'updated': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -191,7 +189,7 @@ def create_module_interfaces(interfaces, module_type, nb, counter=None): if counter is not None: for intf in ifSuccess: counter.update({'module_port_added': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -213,12 +211,11 @@ def createConsolePorts(consoleports, deviceType, nb, counter=None): if counter is not None: for port in cpSuccess: counter.update({'updated': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass def create_module_console_ports(consoleports, module_type, nb, counter=None): - all_consoleports = {str(item): item for item in nb.dcim.console_port_templates.filter(moduletype_id=module_type)} need_consoleports = [] for consoleport in consoleports: @@ -236,7 +233,7 @@ def create_module_console_ports(consoleports, module_type, nb, counter=None): if counter is not None: for port in cpSuccess: counter.update({'module_port_added': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -258,7 +255,7 @@ def createPowerPorts(powerports, deviceType, nb, counter=None): if counter is not None: for pp in ppSuccess: counter.update({'updated': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -280,7 +277,7 @@ def create_module_power_ports(powerports, module_type, nb, counter=None): if counter is not None: for pp in ppSuccess: counter.update({'module_port_added': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -304,7 +301,7 @@ def createConsoleServerPorts(consoleserverports, deviceType, nb, counter=None): if counter is not None: for csp in cspSuccess: counter.update({'updated': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -328,7 +325,7 @@ def create_module_console_server_ports(consoleserverports, module_type, nb, coun if counter is not None: for csp in cspSuccess: counter.update({'module_port_added': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -358,7 +355,7 @@ def createFrontPorts(frontports, deviceType, nb, counter=None): if counter is not None: for fp in fpSuccess: counter.update({'updated': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -388,7 +385,7 @@ def create_module_front_ports(frontports, module_type, nb, counter=None): if counter is not None: for fp in fpSuccess: counter.update({'module_port_added': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -410,7 +407,7 @@ def createRearPorts(rearports, deviceType, nb, counter=None): if counter is not None: for rp in rpSuccess: counter.update({'updated': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -432,7 +429,7 @@ def create_module_rear_ports(rearports, module_type, nb, counter=None): if counter is not None: for rp in rpSuccess: counter.update({'module_port_added': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -454,7 +451,7 @@ def createDeviceBays(devicebays, deviceType, nb, counter=None): if counter is not None: for db in dbSuccess: counter.update({'updated': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -483,7 +480,7 @@ def create_module_bays(module_bays, device_type, nb, counter=None): if counter is not None: for module_bay in module_bay_res: counter.update({'updated': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -513,7 +510,7 @@ def createPowerOutlets(poweroutlets, deviceType, nb, counter=None): if counter is not None: for po in poSuccess: counter.update({'updated': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -556,7 +553,7 @@ def create_module_power_outlets(poweroutlets, module_type, nb, counter=None): if counter is not None: for po in poSuccess: counter.update({'module_port_added': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass @@ -570,7 +567,7 @@ def createDeviceTypes(deviceTypes, nb, counter=None): dt = nb.dcim.device_types.create(deviceType) if counter is not None: counter.update({'added': 1}) - except pynetbox.RequestError as e: + except pynetbox.RequestError: pass if "interfaces" in deviceType: @@ -621,7 +618,7 @@ def create_module_types(module_types, nb, counter=None): module_type_res = nb.dcim.module_types.create(curr_mt) if counter is not None: counter.update({'module_added': 1}) - except pynetbox.RequestError as exce: + except pynetbox.RequestError: pass # module_type_res = all_module_types[curr_mt['manufacturer']['slug']][curr_mt["model"]] @@ -652,7 +649,6 @@ def import_library(nb, library_dir): ) if library_dir is not None and os.path.isdir(library_dir): - files, vendors = getFiles(library_dir) deviceTypes = readYAMl(files) createManufacturers(vendors, nb, counter=cntr) diff --git a/scripts/control.py b/scripts/control.py index abae273f4..3eed49be5 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -21,7 +21,27 @@ import tarfile import time -from malcolm_common import * +from malcolm_common import ( + MalcolmTmpPath, + AskForPassword, + AskForString, + BoundPath, + ChooseOne, + DisplayMessage, + DisplayProgramBox, + GetIterable, + GetUidGidFromComposeFile, + LocalPathForContainerBindMount, + MainDialog, + MalcolmAuthFilesExist, + MalcolmPath, + PLATFORM_WINDOWS, + posInt, + ScriptPath, + YAMLDynamic, + YesOrNo, +) + from malcolm_utils import ( eprint, EscapeForCurl, @@ -32,10 +52,12 @@ run_process, same_file_or_dir, which, + str2bool, + pushd, ) from base64 import b64encode from collections import defaultdict, namedtuple -from subprocess import PIPE, DEVNULL, Popen, TimeoutExpired +from subprocess import PIPE, STDOUT, DEVNULL, Popen, TimeoutExpired from urllib.parse import urlparse try: @@ -71,7 +93,7 @@ def __exit__(self, *args): ColoramaInit() coloramaImported = True -except: +except Exception: coloramaImported = False @@ -338,7 +360,7 @@ def netboxBackup(backupFileName=None): err, results = run_process(dockerCmd, env=osEnv, debug=args.debug, stdout=True, stderr=False) if (err != 0) or (len(results) == 0): - raise Exception(f'Error creating NetBox configuration database backup') + raise Exception('Error creating NetBox configuration database backup') if (backupFileName is None) or (len(backupFileName) == 0): backupFileName = f"malcolm_netbox_backup_{time.strftime('%Y%m%d-%H%M%S')}.gz" @@ -394,20 +416,20 @@ def netboxRestore(backupFileName=None): dockerCmd = dockerCmdBase + ['netbox-postgres', 'createdb', '-U', 'netbox', 'netbox'] err, results = run_process(dockerCmd, env=osEnv, debug=args.debug) if err != 0: - raise Exception(f'Error creating new NetBox database') + raise Exception('Error creating new NetBox database') # load the backed-up psql dump dockerCmd = dockerCmdBase + ['netbox-postgres', 'psql', '-U', 'netbox'] with gzip.open(backupFileName, 'rt') as f: err, results = run_process(dockerCmd, env=osEnv, debug=args.debug, stdin=f.read()) if (err != 0) or (len(results) == 0): - raise Exception(f'Error loading NetBox database') + raise Exception('Error loading NetBox database') # migrations if needed dockerCmd = dockerCmdBase + ['netbox', '/opt/netbox/netbox/manage.py', 'migrate'] err, results = run_process(dockerCmd, env=osEnv, debug=args.debug) if (err != 0) or (len(results) == 0): - raise Exception(f'Error performing NetBox migration') + raise Exception('Error performing NetBox migration') # restore media directory backupFileParts = os.path.splitext(backupFileName) @@ -481,14 +503,14 @@ def logs(): # logs we don't want to eliminate, but we don't want to repeat ad-nauseum # TODO: not implemented yet - dupeRegEx = re.compile( - r""" - .+( - Maybe the destination pipeline is down or stopping - ) - """, - re.VERBOSE | re.IGNORECASE, - ) + # dupeRegEx = re.compile( + # r""" + # .+( + # Maybe the destination pipeline is down or stopping + # ) + # """, + # re.VERBOSE | re.IGNORECASE, + # ) serviceRegEx = re.compile(r'^(?P.+?\|)\s*(?P.*)$') iso8601TimeRegEx = re.compile( @@ -537,7 +559,8 @@ def logs(): outputStr = urlUserPassRegEx.sub(r"\1xxxxxxxx\2", output.decode().strip()) outputStrEscaped = EscapeAnsi(outputStr) if ignoreRegEx.match(outputStrEscaped): - pass ### print(f'!!!!!!!: {outputStr}') + # print(f'!!!!!!!: {outputStr}') + pass elif ( (args.cmdStart or args.cmdRestart) and (not args.cmdLogs) @@ -753,7 +776,7 @@ def stop(wipe=False): if (os.path.isfile(fileSpec) or os.path.islink(fileSpec)) and (not file.startswith('.git')): try: os.remove(fileSpec) - except: + except Exception: pass # delete whole directories if boundPath.relative_dirs: @@ -994,7 +1017,7 @@ def authSetup(wipe=False): try: k, v = line.rstrip().split("=") prevAuthInfo[k] = v.strip('"') - except: + except Exception: pass if len(prevAuthInfo['MALCOLM_USERNAME']) > 0: usernamePrevious = prevAuthInfo['MALCOLM_USERNAME'] @@ -1050,7 +1073,7 @@ def authSetup(wipe=False): try: k, v = line.rstrip().split("=") ldapDefaults[k] = v.strip('"').strip("'") - except: + except Exception: pass ldapProto = ldapDefaults.get("LDAP_PROTO", "ldap://") ldapHost = ldapDefaults.get("LDAP_HOST", "ds.example.com") @@ -1110,7 +1133,7 @@ def authSetup(wipe=False): open(os.path.join(MalcolmPath, os.path.join('htadmin', 'metadata')), 'a').close() DisplayMessage( - f'Additional local accounts can be created at https://localhost:488/ when Malcolm is running', + 'Additional local accounts can be created at https://localhost:488/ when Malcolm is running', ) # generate HTTPS self-signed certificates @@ -1383,7 +1406,7 @@ def authSetup(wipe=False): eprint("Passwords do not match") esSslVerify = YesOrNo( - f'Require SSL certificate validation for OpenSearch communication?', + 'Require SSL certificate validation for OpenSearch communication?', default=(not (('k' in prevCurlContents) or ('insecure' in prevCurlContents))), ) @@ -1395,7 +1418,7 @@ def authSetup(wipe=False): else: try: os.remove(openSearchCredFileName) - except: + except Exception: pass open(openSearchCredFileName, 'a').close() os.chmod(openSearchCredFileName, stat.S_IRUSR | stat.S_IWUSR) @@ -1515,7 +1538,7 @@ def authSetup(wipe=False): elif authItem[0] == 'txfwcerts': DisplayMessage( - f'Run configure-capture on the remote log forwarder, select "Configure Forwarding," then "Receive client SSL files..."', + 'Run configure-capture on the remote log forwarder, select "Configure Forwarding," then "Receive client SSL files..."', ) with pushd(filebeatPath): with Popen( @@ -1735,7 +1758,7 @@ def main(): else: raise else: - raise Exception(f"Could not determine configuration directory containing Malcolm's .env files") + raise Exception("Could not determine configuration directory containing Malcolm's .env files") # create local temporary directory for docker-compose because we may have noexec on /tmp try: @@ -1801,7 +1824,7 @@ def main(): elif ScriptName == "netbox-restore" and ( (not args.netboxRestoreFile) or (not os.path.isfile(args.netboxRestoreFile)) ): - raise Exception(f'NetBox configuration database file must be specified with --netbox-restore') + raise Exception('NetBox configuration database file must be specified with --netbox-restore') # the compose file references various .env files in just about every operation this script does, # so make sure they exist right off the bat diff --git a/scripts/install.py b/scripts/install.py index 20c870f52..abeb1efd7 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -28,9 +28,44 @@ getpwuid = None from collections import defaultdict, namedtuple -from malcolm_common import * -import malcolm_utils -from malcolm_utils import eprint, str2bool, run_process, same_file_or_dir, touch, which +from malcolm_common import ( + AskForString, + ChooseMultiple, + ChooseOne, + DisplayMessage, + DOCKER_COMPOSE_INSTALL_URLS, + dockerComposeUrl, + DOCKER_INSTALL_URLS, + DotEnvDynamic, + DownloadToFile, + HOMEBREW_INSTALL_URLS, + MalcolmCfgRunOnceFile, + MalcolmPath, + PLATFORM_LINUX, + PLATFORM_LINUX_CENTOS, + PLATFORM_LINUX_DEBIAN, + PLATFORM_LINUX_FEDORA, + PLATFORM_LINUX_UBUNTU, + PLATFORM_MAC, + PLATFORM_WINDOWS, + ReplaceBindMountLocation, + RequestsDynamic, + ScriptPath, + UserInputDefaultsBehavior, + UserInterfaceMode, + WindowsInstaller, + YAMLDynamic, + YesOrNo, +) +from malcolm_utils import ( + deep_get, + eprint, + run_process, + same_file_or_dir, + str2bool, + touch, + which, +) ################################################################################################### DOCKER_COMPOSE_INSTALL_VERSION = "2.14.2" @@ -287,7 +322,7 @@ def install_malcolm_files(self, malcolm_install_file): else: try: os.makedirs(installPath) - except: + except Exception: pass if os.path.isdir(installPath): break @@ -363,7 +398,7 @@ def tweak_malcolm_runtime( pgid = str(os.getgid()) if (puid == '0') or (pgid == '0'): raise Exception('it is preferrable not to run Malcolm as root, prompting for UID/GID instead') - except: + except Exception: puid = '1000' pgid = '1000' @@ -539,7 +574,7 @@ def tweak_malcolm_runtime( ldapServerType = None while ldapServerType not in allowedLdapModes: ldapServerType = InstallerChooseOne( - f'Select LDAP server compatibility type', + 'Select LDAP server compatibility type', choices=[(x, '', x == 'winldap') for x in allowedLdapModes], ) ldapStartTLS = InstallerYesOrNo( @@ -555,7 +590,7 @@ def tweak_malcolm_runtime( file=ldapDefaultsFile, ) print(f"LDAP_PORT='{3268 if ldapStartTLS else 3269}'", file=ldapDefaultsFile) - except: + except Exception: pass # directories for data volume mounts (PCAP storage, Zeek log storage, OpenSearch indexes, etc.) @@ -749,11 +784,11 @@ def tweak_malcolm_runtime( default="miscbeat", ) filebeatTcpDropField = InstallerAskForString( - f'Field to drop from events sent to Filebeat TCP listener', + 'Field to drop from events sent to Filebeat TCP listener', default=filebeatTcpSourceField, ) filebeatTcpTag = InstallerAskForString( - f'Tag to apply to messages sent to Filebeat TCP listener', + 'Tag to apply to messages sent to Filebeat TCP listener', default=filebeatTcpTag, ) else: @@ -767,7 +802,6 @@ def tweak_malcolm_runtime( allowedFileCarveModes = ('none', 'known', 'mapped', 'all', 'interesting') allowedFilePreserveModes = ('quarantined', 'all', 'none') - fileCarveModeUser = None fileCarveMode = None filePreserveMode = None vtotApiKey = '0' @@ -1255,7 +1289,7 @@ def tweak_malcolm_runtime( for val in EnvValues: try: touch(val.envFile) - except Exception as e: + except Exception: pass try: @@ -1300,7 +1334,7 @@ def tweak_malcolm_runtime( currentService = None # determine indentation for each compose file section (assumes YML file is consistently indented) - if (currentSection is not None) and (not currentSection in sectionIndents): + if (currentSection is not None) and (currentSection not in sectionIndents): indentMatch = re.search(r'^(\s+)\S+\s*:\s*$', line) if indentMatch is not None: sectionIndents[currentSection] = indentMatch.group(1) @@ -1537,7 +1571,7 @@ def tweak_malcolm_runtime( fr"\g<1>{'0.0.0.0' if nginxSSL and (((not '9200:9200' in line) and (not '5601:5601' in line)) or opensearchOpen) else '127.0.0.1'}:\g<3>", line, ) - if nginxSSL == False: + if nginxSSL is False: if ':443:' in line: line = line.replace(':443:', ':80:') if ':9200:' in line: @@ -1627,7 +1661,7 @@ def tweak_malcolm_runtime( try: touch(MalcolmCfgRunOnceFile) - except Exception as e: + except Exception: pass # if the Malcolm dir is owned by root, see if they want to reassign ownership to a non-root user @@ -1676,17 +1710,17 @@ def __init__(self, debug=False, configOnly=False): try: k, v = line.rstrip().split("=") osInfo[k] = v.strip('"') - except: + except Exception: pass if ('NAME' in osInfo) and (len(osInfo['NAME']) > 0): - distro = osInfo['NAME'].lower().split()[0] + self.distro = osInfo['NAME'].lower().split()[0] if ('VERSION_CODENAME' in osInfo) and (len(osInfo['VERSION_CODENAME']) > 0): - codename = osInfo['VERSION_CODENAME'].lower().split()[0] + self.codename = osInfo['VERSION_CODENAME'].lower().split()[0] if ('VERSION_ID' in osInfo) and (len(osInfo['VERSION_ID']) > 0): - release = osInfo['VERSION_ID'].lower().split()[0] + self.release = osInfo['VERSION_ID'].lower().split()[0] # try lsb_release next if self.distro is None: @@ -1774,7 +1808,7 @@ def __init__(self, debug=False, configOnly=False): try: totalMemBytes = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES') self.totalMemoryGigs = math.ceil(totalMemBytes / (1024.0**3)) - except: + except Exception: self.totalMemoryGigs = 0.0 # determine total system memory a different way if the first way didn't work @@ -1787,7 +1821,7 @@ def __init__(self, debug=False, configOnly=False): # determine total system CPU cores try: self.totalCores = os.sysconf('SC_NPROCESSORS_ONLN') - except: + except Exception: self.totalCores = 0 # determine total system CPU cores a different way if the first way didn't work @@ -2271,7 +2305,7 @@ def __init__(self, debug=False, configOnly=False): try: totalMemBytes = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES') self.totalMemoryGigs = math.ceil(totalMemBytes / (1024.0**3)) - except: + except Exception: self.totalMemoryGigs = 0.0 # determine total system memory a different way if the first way didn't work @@ -2284,7 +2318,7 @@ def __init__(self, debug=False, configOnly=False): # determine total system CPU cores try: self.totalCores = os.sysconf('SC_NPROCESSORS_ONLN') - except: + except Exception: self.totalCores = 0 # determine total system CPU cores a different way if the first way didn't work @@ -2400,7 +2434,7 @@ def install_docker(self): newCpus = InstallerAskForString('Enter Docker CPU cores (e.g., 4, 8, 16)') newMemoryGiB = InstallerAskForString('Enter Docker RAM MiB (e.g., 8, 16, etc.)') - if newCpus or newMemoryMiB: + if newCpus or newMemoryGiB: with open(settingsFile, 'r+') as f: data = json.load(f) if newCpus: diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index 8b9c50c9d..a319bb5f4 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -12,7 +12,7 @@ import sys import malcolm_utils -from malcolm_utils import eprint, str2bool, run_process +from malcolm_utils import eprint, str2bool, run_process, deep_get from collections import defaultdict, namedtuple from enum import IntFlag, auto @@ -95,7 +95,7 @@ def ReplaceBindMountLocation(line, location, linePrefix): def LocalPathForContainerBindMount(service, dockerComposeContents, containerPath, localBasePath=None): localPath = None if service and dockerComposeContents and containerPath: - vols = DeepGet(dockerComposeContents, ['services', service, 'volumes']) + vols = deep_get(dockerComposeContents, ['services', service, 'volumes']) if (vols is not None) and (len(vols) > 0): for vol in vols: volSplit = vol.split(':') diff --git a/scripts/zeek_script_to_malcolm_boilerplate.py b/scripts/zeek_script_to_malcolm_boilerplate.py index d1a9289a0..96cd2939a 100755 --- a/scripts/zeek_script_to_malcolm_boilerplate.py +++ b/scripts/zeek_script_to_malcolm_boilerplate.py @@ -72,6 +72,7 @@ # Used as a predicate to filter nodes during tree traversal. # see https://github.com/zeek/zeekscript/blob/4a3512dd114e2709d6738016176c27a65f3f1492/zeekscript/node.py#L157 + # This Node is a "create_stream" expression, e.g.: # Log::create_stream(ICSNPP_OPCUA_Binary::LOG, [$columns=OPCUA_Binary::Info, $path="opcua-binary"]) def IsCreateStreamExprNode(Node): diff --git a/sensor-iso/interface/sensor_interface/routes.py b/sensor-iso/interface/sensor_interface/routes.py index 3b7bdecd6..2f4c3d66e 100644 --- a/sensor-iso/interface/sensor_interface/routes.py +++ b/sensor-iso/interface/sensor_interface/routes.py @@ -1,6 +1,10 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. -import psutil, time, json, logging, os +import psutil +import time +import json +import logging +import os from .sysquery import sys_service as sys_s from flask import render_template, send_from_directory from flask import Flask @@ -62,7 +66,6 @@ def activate_service(script): @app.route('/update', methods=['GET']) def update_stats(): - req_time = int(time.time()) disk_write_data_start = psutil.disk_io_counters(perdisk=False) diff --git a/sensor-iso/interface/sensor_interface/sysquery/__init__.py b/sensor-iso/interface/sensor_interface/sysquery/__init__.py index 8b1378917..e69de29bb 100644 --- a/sensor-iso/interface/sensor_interface/sysquery/__init__.py +++ b/sensor-iso/interface/sensor_interface/sysquery/__init__.py @@ -1 +0,0 @@ - diff --git a/sensor-iso/interface/sensor_interface/sysquery/sys_service.py b/sensor-iso/interface/sensor_interface/sysquery/sys_service.py index c030e57be..a1a432bf2 100644 --- a/sensor-iso/interface/sensor_interface/sysquery/sys_service.py +++ b/sensor-iso/interface/sensor_interface/sysquery/sys_service.py @@ -1,6 +1,7 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. -import subprocess, json +import subprocess +import json import os # traverse back up the path of the project directory to the scripts location @@ -8,7 +9,6 @@ def service(command): - # TODO implement better error handling command, arguement = command.split(" ") From a4de71c4b3ff6a2b4b8af808904564d269b3ff79 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Mon, 3 Apr 2023 13:09:11 -0600 Subject: [PATCH 078/239] Mount default auth to tmp directory for idaholab/Malcolm#168 --- nginx/scripts/docker_entrypoint.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nginx/scripts/docker_entrypoint.sh b/nginx/scripts/docker_entrypoint.sh index b931e15cf..ceb2cf29e 100755 --- a/nginx/scripts/docker_entrypoint.sh +++ b/nginx/scripts/docker_entrypoint.sh @@ -238,7 +238,8 @@ EOF fi # basic vs. ldap if [[ ! -f /etc/nginx/auth/htpasswd ]]; then - cp /etc/nginx/auth/default/htpasswd /etc/nginx/auth/ + cp /tmp/auth/default/htpasswd /etc/nginx/auth/ + rm -rf /tmp/auth/* fi # start supervisor (which will spawn nginx, stunnel, etc.) or whatever the default command is From 85a4a4751f015482d2e35734a5e3e8f7e598c608 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 3 Apr 2023 13:45:54 -0600 Subject: [PATCH 079/239] refactoring fixes --- scripts/control.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index 3eed49be5..532e1e77b 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -29,7 +29,6 @@ ChooseOne, DisplayMessage, DisplayProgramBox, - GetIterable, GetUidGidFromComposeFile, LocalPathForContainerBindMount, MainDialog, @@ -46,6 +45,7 @@ eprint, EscapeForCurl, EscapeAnsi, + get_iterable, LoadStrIfJson, ParseCurlFile, RemoveEmptyFolders, @@ -780,7 +780,7 @@ def stop(wipe=False): pass # delete whole directories if boundPath.relative_dirs: - for relDir in GetIterable(boundPath.relative_dirs): + for relDir in get_iterable(boundPath.relative_dirs): tmpPath = os.path.join(localPath, relDir) if os.path.isdir(tmpPath): if args.debug: @@ -788,7 +788,7 @@ def stop(wipe=False): shutil.rmtree(tmpPath, ignore_errors=True) # cleanup empty directories if boundPath.clean_empty_dirs: - for cleanDir in GetIterable(boundPath.clean_empty_dirs): + for cleanDir in get_iterable(boundPath.clean_empty_dirs): tmpPath = os.path.join(localPath, cleanDir) if os.path.isdir(tmpPath): if args.debug: @@ -882,7 +882,7 @@ def start(): else: raise if boundPath.relative_dirs: - for relDir in GetIterable(boundPath.relative_dirs): + for relDir in get_iterable(boundPath.relative_dirs): tmpPath = os.path.join(localPath, relDir) try: if args.debug: From 27a6a834aaa39cb17d110c8ccc8cb881ef731188 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 3 Apr 2023 14:14:48 -0600 Subject: [PATCH 080/239] increase number of allowed nested fields in a composable template from 50 to 250 --- dashboards/templates/malcolm_beats_template.json | 3 ++- dashboards/templates/malcolm_template.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dashboards/templates/malcolm_beats_template.json b/dashboards/templates/malcolm_beats_template.json index 4effb4f03..fdbad390e 100644 --- a/dashboards/templates/malcolm_beats_template.json +++ b/dashboards/templates/malcolm_beats_template.json @@ -26,7 +26,8 @@ "template" :{ "settings" : { "index" : { - "mapping.total_fields.limit" : "5000" + "mapping.total_fields.limit" : "5000", + "mapping.nested_fields.limit" : "250" } }, "mappings": { diff --git a/dashboards/templates/malcolm_template.json b/dashboards/templates/malcolm_template.json index 04d6fb0f7..36b8e85ea 100644 --- a/dashboards/templates/malcolm_template.json +++ b/dashboards/templates/malcolm_template.json @@ -30,7 +30,8 @@ "template" :{ "settings" : { "index" : { - "mapping.total_fields.limit" : "5000" + "mapping.total_fields.limit" : "5000", + "mapping.nested_fields.limit" : "250" } }, "mappings": { From 36dba458bf0558f3768c5f07f6a0e9e689aa3a41 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 3 Apr 2023 15:46:40 -0600 Subject: [PATCH 081/239] work in progress for moving inotify to watchdog, see idaholab/Malcolm#168 --- Dockerfiles/pcap-monitor.Dockerfile | 5 + config/upload-common.env.example | 4 + pcap-monitor/supervisord.conf | 2 + .../demo/amazon_linux_2_malcolm_demo_setup.sh | 1 + shared/bin/pcap_watcher.py | 192 +++++++++++------- shared/bin/watch_common.py | 14 +- 6 files changed, 147 insertions(+), 71 deletions(-) diff --git a/Dockerfiles/pcap-monitor.Dockerfile b/Dockerfiles/pcap-monitor.Dockerfile index 151309e82..27f298e64 100644 --- a/Dockerfiles/pcap-monitor.Dockerfile +++ b/Dockerfiles/pcap-monitor.Dockerfile @@ -30,6 +30,8 @@ ARG PCAP_PATH=/pcap ARG PCAP_PIPELINE_DEBUG=false ARG PCAP_PIPELINE_DEBUG_EXTRA=false ARG PCAP_PIPELINE_IGNORE_PREEXISTING=false +ARG PCAP_PIPELINE_POLLING=false +ARG PCAP_PIPELINE_POLLING_ASSUME_CLOSED_SEC=10 ARG PCAP_NODE_NAME=malcolm ARG ZEEK_PATH=/zeek @@ -39,6 +41,8 @@ ENV PCAP_PATH $PCAP_PATH ENV PCAP_PIPELINE_DEBUG $PCAP_PIPELINE_DEBUG ENV PCAP_PIPELINE_DEBUG_EXTRA $PCAP_PIPELINE_DEBUG_EXTRA ENV PCAP_PIPELINE_IGNORE_PREEXISTING $PCAP_PIPELINE_IGNORE_PREEXISTING +ENV PCAP_PIPELINE_POLLING $PCAP_PIPELINE_POLLING +ENV PCAP_PIPELINE_POLLING_ASSUME_CLOSED_SEC $PCAP_PIPELINE_POLLING_ASSUME_CLOSED_SEC ENV PCAP_NODE_NAME $PCAP_NODE_NAME ENV ZEEK_PATH $ZEEK_PATH @@ -71,6 +75,7 @@ ADD pcap-monitor/supervisord.conf /etc/supervisord.conf ADD pcap-monitor/scripts/ /usr/local/bin/ ADD shared/bin/pcap_watcher.py /usr/local/bin/ ADD shared/bin/pcap_utils.py /usr/local/bin/ +ADD shared/bin/watch_common.py /usr/local/bin/ ADD scripts/malcolm_utils.py /usr/local/bin/ EXPOSE 30441 diff --git a/config/upload-common.env.example b/config/upload-common.env.example index edc9d577d..e5891711f 100644 --- a/config/upload-common.env.example +++ b/config/upload-common.env.example @@ -10,6 +10,10 @@ PCAP_PIPELINE_DEBUG=false PCAP_PIPELINE_DEBUG_EXTRA=false # Whether or not PCAP files extant in ./pcap/ will be ignored on startup PCAP_PIPELINE_IGNORE_PREEXISTING=false +# Whether or not to use polling vs. native inotify API to watch for files +PCAP_PIPELINE_POLLING=false +# When polling, seconds of inactivity to assume a file is closed and ready for processing +PCAP_PIPELINE_POLLING_ASSUME_CLOSED_SEC=10 # 'pcap-monitor' to match the name of the container providing the uploaded/captured PCAP file # monitoring service PCAP_MONITOR_HOST=pcap-monitor diff --git a/pcap-monitor/supervisord.conf b/pcap-monitor/supervisord.conf index a48fa7963..a4ad0d9f8 100644 --- a/pcap-monitor/supervisord.conf +++ b/pcap-monitor/supervisord.conf @@ -37,6 +37,8 @@ command=python3 /usr/local/bin/pcap_watcher.py --opensearch-wait --node "%(ENV_PCAP_NODE_NAME)s" --ignore-existing "%(ENV_PCAP_PIPELINE_IGNORE_PREEXISTING)s" + --polling "%(ENV_PCAP_PIPELINE_POLLING)s" + --closed-sec %(ENV_PCAP_PIPELINE_POLLING_ASSUME_CLOSED_SEC)s --start-sleep 60 --directory "%(ENV_PCAP_PATH)s"/processed user=%(ENV_PUSER)s diff --git a/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh b/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh index da6beadbf..0670f2345 100755 --- a/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh +++ b/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh @@ -559,6 +559,7 @@ function InstallMalcolm { "LOGSTASH_REVERSE_DNS:'true'" "LOGSTASH_SEVERITY_SCORING:'true'" "PCAP_PIPELINE_IGNORE_PREEXISTING:'true'" + "PCAP_PIPELINE_POLLING:'true'" "YARA_MAX_REQUESTS:4" "ZEEK_AUTO_ANALYZE_PCAP_FILES:'true'" "ZEEK_DISABLE_BEST_GUESS_ICS:''" diff --git a/shared/bin/pcap_watcher.py b/shared/bin/pcap_watcher.py index f7eb13ceb..fb972a66e 100755 --- a/shared/bin/pcap_watcher.py +++ b/shared/bin/pcap_watcher.py @@ -16,7 +16,6 @@ import magic import os import pathlib -import pyinotify import signal import sys import time @@ -35,13 +34,19 @@ ) import malcolm_utils from malcolm_utils import eprint, str2bool, ParseCurlFile, remove_prefix, touch +import watch_common from collections import defaultdict +from multiprocessing.pool import ThreadPool from opensearchpy import OpenSearch, Search from opensearchpy.exceptions import ConnectionError, ConnectionTimeout from urllib3.exceptions import NewConnectionError +from watchdog.observers import Observer +from watchdog.observers.polling import PollingObserver +from watchdog.utils import WatchdogShutdown + ################################################################################################### MINIMUM_CHECKED_FILE_SIZE_DEFAULT = 24 MAXIMUM_CHECKED_FILE_SIZE_DEFAULT = 32 * 1024 * 1024 * 1024 @@ -61,21 +66,19 @@ scriptName = os.path.basename(__file__) scriptPath = os.path.dirname(os.path.realpath(__file__)) origPath = os.getcwd() -shuttingDown = False +shuttingDown = [False] DEFAULT_NODE_NAME = os.getenv('PCAP_NODE_NAME', 'malcolm') ################################################################################################### # watch files written to and moved to this directory -class EventWatcher(pyinotify.ProcessEvent): - # notify on files written in-place then closed (IN_CLOSE_WRITE), and moved into this directory (IN_MOVED_TO) - _methods = ["IN_CLOSE_WRITE", "IN_MOVED_TO"] - +class EventWatcher: def __init__(self): global args global opensearchHttpAuth global debug global verboseDebug + global shuttingDown super().__init__() @@ -88,7 +91,7 @@ def __init__(self): healthy = False # create the connection to OpenSearch - while (not connected) and (not shuttingDown): + while (not connected) and (not shuttingDown[0]): try: try: if debug: @@ -139,7 +142,7 @@ def __init__(self): break # if requested, wait for at least "yellow" health in the cluster for the "files" index - while connected and args.opensearchWaitForHealth and (not healthy) and (not shuttingDown): + while connected and args.opensearchWaitForHealth and (not healthy) and (not shuttingDown[0]): try: if debug: eprint(f"{scriptName}:\twaiting for OpenSearch to be healthy") @@ -181,31 +184,28 @@ def __init__(self): if debug: eprint(f"{scriptName}:\tEventWatcher initialized") - -################################################################################################### -# set up event processor to append processed events from to the event queue -def event_process_generator(cls, method): - # actual method called when we are notified of a file - def _method_name(self, event): + ################################################################################################### + # set up event processor to append processed events from to the event queue + def processFile(self, pathname): global args global debug global verboseDebug if debug: - eprint(f"{scriptName}:\t👓\t{event.pathname}") + eprint(f"{scriptName}:\t👓\t{pathname}") # the entity must be a regular PCAP file and actually exist - if (not event.dir) and os.path.isfile(event.pathname): + if os.path.isfile(pathname): # get the file magic description and mime type - fileMime = magic.from_file(event.pathname, mime=True) - fileType = magic.from_file(event.pathname) + fileMime = magic.from_file(pathname, mime=True) + fileType = magic.from_file(pathname) # get the file size, in bytes to compare against sane values - fileSize = os.path.getsize(event.pathname) + fileSize = os.path.getsize(pathname) if (args.minBytes <= fileSize <= args.maxBytes) and ( (fileMime in PCAP_MIME_TYPES) or ('pcap-ng' in fileType) ): - relativePath = remove_prefix(event.pathname, os.path.join(args.baseDir, '')) + relativePath = remove_prefix(pathname, os.path.join(args.baseDir, '')) # check with Arkime's files index in OpenSearch and make sure it's not a duplicate fileIsDuplicate = False @@ -225,15 +225,15 @@ def _method_name(self, event): if fileIsDuplicate: # this is duplicate file (it's been processed before) so ignore it if debug: - eprint(f"{scriptName}:\t📋\t{event.pathname}") + eprint(f"{scriptName}:\t📋\t{pathname}") else: # the entity is a right-sized non-duplicate file, and it exists, so send it to get processed if debug: - eprint(f"{scriptName}:\t📩\t{event.pathname}") + eprint(f"{scriptName}:\t📩\t{pathname}") try: fileInfo = { - FILE_INFO_DICT_NAME: event.pathname if args.includeAbsolutePath else relativePath, + FILE_INFO_DICT_NAME: pathname if args.includeAbsolutePath else relativePath, FILE_INFO_DICT_SIZE: fileSize, FILE_INFO_FILE_MIME: fileMime, FILE_INFO_FILE_TYPE: fileType, @@ -245,23 +245,24 @@ def _method_name(self, event): eprint(f"{scriptName}:\t📫\t{fileInfo}") except zmq.Again: if verboseDebug: - eprint(f"{scriptName}:\t🕑\t{event.pathname}") + eprint(f"{scriptName}:\t🕑\t{pathname}") else: # too small/big to care about, or the wrong type, ignore it if debug: - eprint(f"{scriptName}:\t✋\t{event.pathname}") + eprint(f"{scriptName}:\t✋\t{pathname}") - # assign process method to class - _method_name.__name__ = "process_{}".format(method) - setattr(cls, _method_name.__name__, _method_name) + +def file_processor(pathname, **kwargs): + if "watcher" in kwargs and kwargs["watcher"]: + kwargs["watcher"].processFile(pathname) ################################################################################################### # handle sigint/sigterm and set a global shutdown variable def shutdown_handler(signum, frame): global shuttingDown - shuttingDown = True + shuttingDown[0] = True ################################################################################################### @@ -430,6 +431,28 @@ def main(): type=str, required=False, ) + parser.add_argument( + '-p', + '--polling', + dest='polling', + help="Use polling (instead of inotify)", + metavar='true|false', + type=str2bool, + nargs='?', + const=True, + default=os.getenv('PCAP_PIPELINE_POLLING', False), + required=False, + ) + parser.add_argument( + '-c', + '--closed-sec', + dest='assumeClosedSec', + help="When polling, assume a file is closed after this many seconds of inactivity", + metavar='', + type=int, + default=int(os.getenv('PCAP_PIPELINE_POLLING_ASSUME_CLOSED_SEC', str(watch_common.ASSUME_CLOSED_SEC_DEFAULT))), + required=False, + ) requiredNamed = parser.add_argument_group('required arguments') requiredNamed.add_argument( '-d', '--directory', dest='baseDir', help='Directory to monitor', metavar='', type=str, required=True @@ -474,21 +497,17 @@ def main(): # sleep for a bit if requested sleepCount = 0 - while (not shuttingDown) and (sleepCount < args.startSleepSec): + while (not shuttingDown[0]) and (sleepCount < args.startSleepSec): time.sleep(1) sleepCount += 1 - # add events to watch to EventWatcher class - for method in EventWatcher._methods: - event_process_generator(EventWatcher, method) - # if directory to monitor doesn't exist, create it now if os.path.isdir(args.baseDir): preexistingDir = True else: preexistingDir = False if debug: - eprint(f'{scriptName}: creating "{args.baseDir}" to monitor') + eprint(f'{scriptName}:\tcreating "{args.baseDir}" to monitor') pathlib.Path(args.baseDir).mkdir(parents=False, exist_ok=True) # if recursion was requested, get list of directories to monitor @@ -502,44 +521,81 @@ def main(): # begin threaded watch of path(s) time.sleep(1) - event_notifier_started = False - watch_manager = pyinotify.WatchManager() - event_notifier = pyinotify.ThreadedNotifier(watch_manager, EventWatcher()) + observer = PollingObserver() if args.polling else Observer() + handler = watch_common.FileOperationEventHandler( + logger=None, + polling=args.polling, + ) for watchDir in watchDirs: - watch_manager.add_watch(os.path.abspath(watchDir), pyinotify.ALL_EVENTS) - if debug: - eprint(f"{scriptName}: monitoring {watchDirs}") - time.sleep(2) - if not shuttingDown: - event_notifier.start() - event_notifier_started = True - - # if there are any previously included files (and not ignoreExisting), "touch" them so that they will be notified on - if preexistingDir and (not args.ignoreExisting) and (not shuttingDown): - filesTouched = 0 - for watchDir in watchDirs: - for preexistingFile in [os.path.join(watchDir, x) for x in pathlib.Path(watchDir).iterdir() if x.is_file()]: - touch(preexistingFile) - filesTouched += 1 - if debug and (filesTouched > 0): - eprint(f"{scriptName}: found {filesTouched} preexisting files to check") - - # loop forever, or until we're told to shut down, whichever comes first - while not shuttingDown: - if pdbFlagged: - pdbFlagged = False - breakpoint() - time.sleep(0.2) - - # graceful shutdown + if verboseDebug: + eprint(f"{scriptName}:\tScheduling {watchDir}") + observer.schedule(handler, watchDir, recursive=False) + + observer.start() + if debug: - eprint(f"{scriptName}: shutting down...") - if event_notifier_started: - event_notifier.stop() + eprint(f"{scriptName}:\tmonitoring {watchDirs}") + + try: + time.sleep(2) + + # if there are any previously included files (and not ignoreExisting), "touch" them so that they will be notified on + if preexistingDir and (not args.ignoreExisting) and (not shuttingDown[0]): + filesTouched = 0 + for watchDir in watchDirs: + for preexistingFile in [ + os.path.join(watchDir, x) for x in pathlib.Path(watchDir).iterdir() if x.is_file() + ]: + touch(preexistingFile) + filesTouched += 1 + if debug and (filesTouched > 0): + eprint(f"{scriptName}:\tfound {filesTouched} preexisting files to check") + + # start the thread to actually handle the files as they're queued by the FileOperationEventHandler handler + workerThreadCount = malcolm_utils.AtomicInt(value=0) + ThreadPool( + 1, + watch_common.ProcessFileEventWorker( + [ + handler, + observer, + file_processor, + {'watcher': EventWatcher()}, + args.assumeClosedSec, + workerThreadCount, + shuttingDown, + None, + ], + ), + ) + + # loop forever, or until we're told to shut down, whichever comes first + while (not shuttingDown[0]) and observer.is_alive(): + if pdbFlagged: + pdbFlagged = False + breakpoint() + observer.join(1) + + # graceful shutdown + if debug: + eprint(f"{scriptName}:\tshutting down...") + + if shuttingDown[0]: + raise WatchdogShutdown() + + except WatchdogShutdown: + observer.unschedule_all() + + finally: + observer.stop() + observer.join() + time.sleep(1) + while workerThreadCount.value() > 0: + time.sleep(1) if debug: - eprint(f"{scriptName}: finished monitoring {watchDirs}") + eprint(f"{scriptName}:\tfinished monitoring {watchDirs}") if __name__ == '__main__': diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py index 6971e2fab..827dc19a5 100644 --- a/shared/bin/watch_common.py +++ b/shared/bin/watch_common.py @@ -27,6 +27,8 @@ from watchdog.observers import Observer from watchdog.observers.polling import PollingObserver +ASSUME_CLOSED_SEC_DEFAULT = 10 + ################################################################################################### class FileOperationEventHandler(FileSystemEventHandler): @@ -100,7 +102,7 @@ def on_deleted(self, event): ################################################################################################### def ProcessFileEventWorker(workerArgs): - handler, observer, fileProcessor, assumeClosedSec, workerThreadCount, shutDown, logger = ( + handler, observer, fileProcessor, fileProcessorKwargs, assumeClosedSec, workerThreadCount, shutDown, logger = ( workerArgs[0], workerArgs[1], workerArgs[2], @@ -108,6 +110,7 @@ def ProcessFileEventWorker(workerArgs): workerArgs[4], workerArgs[5], workerArgs[6], + workerArgs[7], ) with workerThreadCount as workerId: @@ -125,8 +128,11 @@ def ProcessFileEventWorker(workerArgs): else: del d[fileName] if fileProcessor is not None: - fileProcessor(fileName) - logger.debug(f"processed {fileName} at {(nowTime-eventTime) if (eventTime > 0) else 0} seconds") + fileProcessor(fileName, func(**fileProcessorKwargs)) + if logger is not None: + logger.debug( + f"processed {fileName} at {(nowTime-eventTime) if (eventTime > 0) else 0} seconds" + ) time.sleep(1) if logger is not None: @@ -137,6 +143,7 @@ def WatchAndProcessDirectory( directories, polling, fileProcessor, + fileProcessorKwargs, assumeClosedSec, shuttingDown, logger, @@ -161,6 +168,7 @@ def WatchAndProcessDirectory( handler, observer, fileProcessor, + fileProcessorKwargs, assumeClosedSec, workerThreadCount, shuttingDown, From 472b21e305fff5e7d6f12e1aa2adbfdf864f22fd Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 3 Apr 2023 16:05:48 -0600 Subject: [PATCH 082/239] work in progress for moving inotify to watchdog, see idaholab/Malcolm#168 --- shared/bin/watch_common.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py index 827dc19a5..d1a925a7c 100644 --- a/shared/bin/watch_common.py +++ b/shared/bin/watch_common.py @@ -63,9 +63,7 @@ def on_created(self, event): self.on_modified(event) def on_modified(self, event): - # we only care about "created" and "modified" events if polling, - # otherwise "on_closed" will take care of us - if not event.is_directory and self.polling: + if not event.is_directory: with self.deck as d: d[event.src_path] = self.nowTime d.move_to_end(event.src_path, last=True) @@ -128,7 +126,12 @@ def ProcessFileEventWorker(workerArgs): else: del d[fileName] if fileProcessor is not None: - fileProcessor(fileName, func(**fileProcessorKwargs)) + fileProcessor( + fileName, + **fileProcessorKwargs + if fileProcessorKwargs and isinstance(fileProcessorKwargs, dict) + else {}, + ) if logger is not None: logger.debug( f"processed {fileName} at {(nowTime-eventTime) if (eventTime > 0) else 0} seconds" From de0c35f2836a663917097e18548ff531930f1779 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 4 Apr 2023 09:42:11 -0600 Subject: [PATCH 083/239] prep for v23.04.0 --- scripts/demo/Vagrantfile | 2 +- scripts/demo/amazon_linux_2_malcolm_demo_setup.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/demo/Vagrantfile b/scripts/demo/Vagrantfile index b6fb47f01..a4859d27a 100644 --- a/scripts/demo/Vagrantfile +++ b/scripts/demo/Vagrantfile @@ -75,7 +75,7 @@ Vagrant.configure("2") do |config| config.vm.provision "shell", privileged: false, inline: <<-STEP2 mkdir -p ~/.local/bin ~/.config - curl -sSL -o ~/.local/bin/amazon_linux_2_malcolm_demo_setup.sh https://raw.githubusercontent.com/mmguero-dev/Malcolm/development/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh + curl -sSL -o ~/.local/bin/amazon_linux_2_malcolm_demo_setup.sh https://raw.githubusercontent.com/idaholab/Malcolm/main/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh chmod 755 ~/.local/bin/amazon_linux_2_malcolm_demo_setup.sh MALCOLM_SETUP_NONINTERACTIVE=1 ~/.local/bin/amazon_linux_2_malcolm_demo_setup.sh diff --git a/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh b/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh index b6d534f75..4ed3b5999 100755 --- a/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh +++ b/scripts/demo/amazon_linux_2_malcolm_demo_setup.sh @@ -579,7 +579,7 @@ function InstallMalcolm { fi pushd "$LOCAL_BIN_PATH" >/dev/null 2>&1 - curl -sSL -J -O https://raw.githubusercontent.com/mmguero-dev/Malcolm/development/scripts/demo/reset_and_auto_populate.sh + curl -sSL -J -O https://raw.githubusercontent.com/idaholab/Malcolm/main/scripts/demo/reset_and_auto_populate.sh curl -sSL -J -O https://raw.githubusercontent.com/mmguero-dev/Malcolm-PCAP/main/tools/pcap_time_shift.py chmod 755 reset_and_auto_populate.sh pcap_time_shift.py popd >/dev/null 2>&1 From b08baada554f1226115c5d444273646b15d6454b Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 4 Apr 2023 09:43:03 -0600 Subject: [PATCH 084/239] prep for v23.04.0 --- _config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_config.yml b/_config.yml index 0e92a8819..bfa1e1c42 100644 --- a/_config.yml +++ b/_config.yml @@ -6,8 +6,8 @@ remote_theme: pages-themes/minimal@v0.2.0 external_download_url: https://malcolm.fyi/docs/download.html youtube_url: https://www.youtube.com/@MalcolmNetworkTrafficAnalysis mastodon: - id: malcolm@malcolm.fyi - url: https://infosec.exchange/@mmguero + id: + url: docs_uri: docs/ alerting_docs_uri: docs/alerting.html anomaly_detection_docs_uri: docs/anomaly-detection.html From 8818a0b965c65699033715cb8b539cbf6f9483de Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 4 Apr 2023 09:45:20 -0600 Subject: [PATCH 085/239] prep for v23.04.0 --- docs/contributing-local-modifications.md | 2 +- docs/development.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributing-local-modifications.md b/docs/contributing-local-modifications.md index c621887b8..3a26098ab 100644 --- a/docs/contributing-local-modifications.md +++ b/docs/contributing-local-modifications.md @@ -119,4 +119,4 @@ Another method for modifying your local copies of Malcolm's services' containers For example, say you wanted to create a Malcolm container which includes a new dashboard for OpenSearch Dashboards and a new enrichment filter `.conf` file for Logstash. After placing these files under `./dashboards/dashboards` and `./logstash/pipelines/enrichment`, respectively, in your Malcolm working copy, run `./build.sh dashboards-helper logstash` to build just those containers. After the build completes, you can run `docker images` and see you have fresh images for `ghcr.io/idaholab/malcolm/dashboards-helper` and `ghcr.io/idaholab/malcolm/logstash-oss`. You may need to review the contents of the [Dockerfiles]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/Dockerfiles) to determine the correct service and filesystem location within that service's Docker image depending on what you're trying to accomplish. -Alternately, if you have forked Malcolm on GitHub, [workflow files]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}/.github/workflows/) are provided which contain instructions for GitHub to build the docker images and [sensor](live-analysis.md#Hedgehog) and [Malcolm](malcolm-iso.md#ISO) installer ISOs. The resulting images are named according to the pattern `ghcr.io/owner/ghcr.io/idaholab/malcolm/image:branch` (e.g., if you've forked Malcolm with the github user `romeogdetlevjr`, the `arkime` container built for the `main` would be named `ghcr.io/romeogdetlevjr/ghcr.io/idaholab/malcolm/arkime:main`). To run your local instance of Malcolm using these images instead of the official ones, you'll need to edit your `docker-compose.yml` file(s) and replace the `image:` tags according to this new pattern, or use the bash helper script `./shared/bin/github_image_helper.sh` to pull and re-tag the images. \ No newline at end of file +Alternately, if you have forked Malcolm on GitHub, [workflow files]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}/.github/workflows/) are provided which contain instructions for GitHub to build the docker images and [sensor](live-analysis.md#Hedgehog) and [Malcolm](malcolm-iso.md#ISO) installer ISOs. The resulting images are named according to the pattern `ghcr.io/owner/malcolm/image:branch` (e.g., if you've forked Malcolm with the github user `romeogdetlevjr`, the `arkime` container built for the `main` would be named `ghcr.io/romeogdetlevjr/malcolm/arkime:main`). To run your local instance of Malcolm using these images instead of the official ones, you'll need to edit your `docker-compose.yml` file(s) and replace the `image:` tags according to this new pattern, or use the bash helper script `./shared/bin/github_image_helper.sh` to pull and re-tag the images. \ No newline at end of file diff --git a/docs/development.md b/docs/development.md index d4f111ca8..6633038af 100644 --- a/docs/development.md +++ b/docs/development.md @@ -75,7 +75,7 @@ Then, go take a walk or something since it will be a while. When you're done, yo * `ghcr.io/idaholab/malcolm/suricata` (based on `debian:11-slim`) * `ghcr.io/idaholab/malcolm/zeek` (based on `debian:11-slim`) -Alternately, if you have forked Malcolm on GitHub, [workflow files]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}/.github/workflows/) are provided which contain instructions for GitHub to build the docker images and [sensor](live-analysis.md#Hedgehog) and [Malcolm](malcolm-iso.md#ISO) installer ISOs. The resulting images are named according to the pattern `ghcr.io/owner/ghcr.io/idaholab/malcolm/image:branch` (e.g., if you've forked Malcolm with the github user `romeogdetlevjr`, the `arkime` container built for the `main` would be named `ghcr.io/romeogdetlevjr/ghcr.io/idaholab/malcolm/arkime:main`). To run your local instance of Malcolm using these images instead of the official ones, you'll need to edit your `docker-compose.yml` file(s) and replace the `image:` tags according to this new pattern, or use the bash helper script `./shared/bin/github_image_helper.sh` to pull and re-tag the images. +Alternately, if you have forked Malcolm on GitHub, [workflow files]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}/.github/workflows/) are provided which contain instructions for GitHub to build the docker images and [sensor](live-analysis.md#Hedgehog) and [Malcolm](malcolm-iso.md#ISO) installer ISOs. The resulting images are named according to the pattern `ghcr.io/owner/malcolm/image:branch` (e.g., if you've forked Malcolm with the github user `romeogdetlevjr`, the `arkime` container built for the `main` would be named `ghcr.io/romeogdetlevjr/malcolm/arkime:main`). To run your local instance of Malcolm using these images instead of the official ones, you'll need to edit your `docker-compose.yml` file(s) and replace the `image:` tags according to this new pattern, or use the bash helper script `./shared/bin/github_image_helper.sh` to pull and re-tag the images. # Pre-Packaged installation files From ec0f223a55ee9b49f4465db5da75eabe53ffbd0d Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 4 Apr 2023 09:47:26 -0600 Subject: [PATCH 086/239] prep for v23.04.0 --- shared/bin/zeek_intel_from_threat_feed.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shared/bin/zeek_intel_from_threat_feed.py b/shared/bin/zeek_intel_from_threat_feed.py index f69d99c2f..b393ae1d4 100755 --- a/shared/bin/zeek_intel_from_threat_feed.py +++ b/shared/bin/zeek_intel_from_threat_feed.py @@ -19,6 +19,7 @@ script_name = os.path.basename(__file__) script_path = os.path.dirname(os.path.realpath(__file__)) + ################################################################################################### # main def main(): @@ -30,7 +31,7 @@ def main(): ' - MISP core format JSON files', '', 'See:', - ' - Malcolm documentation: https://github.com/idaholab/Malcolm#zeek-intelligence-framework', + ' - Malcolm documentation: https://idaholab.github.io/Malcolm/docs/zeek-intel.html#ZeekIntel', ' - Zeek intelligence framework: https://docs.zeek.org/en/master/frameworks/intel.html', ' - Zeek intel types: https://docs.zeek.org/en/stable/scripts/base/frameworks/intel/main.zeek.html#type-Intel::Type', ' - Introduction to STIX: https://oasis-open.github.io/cti-documentation/stix/intro.html', From d4e6046ae458b2a3090995bfceae01509e37a469 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 4 Apr 2023 12:50:17 -0600 Subject: [PATCH 087/239] work in progress for some python refactoring --- shared/bin/watch_common.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py index d1a925a7c..2518c7005 100644 --- a/shared/bin/watch_common.py +++ b/shared/bin/watch_common.py @@ -126,11 +126,14 @@ def ProcessFileEventWorker(workerArgs): else: del d[fileName] if fileProcessor is not None: + extraArgs = ( + fileProcessorKwargs + if fileProcessorKwargs and isinstance(fileProcessorKwargs, dict) + else {} + ) fileProcessor( fileName, - **fileProcessorKwargs - if fileProcessorKwargs and isinstance(fileProcessorKwargs, dict) - else {}, + **extraArgs, ) if logger is not None: logger.debug( From 9655b8a824ba02c414582faf517bf3574acd2ca5 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Tue, 4 Apr 2023 14:26:57 -0600 Subject: [PATCH 088/239] Adjust htadmin nginx --- htadmin/nginx/sites-available/default | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/htadmin/nginx/sites-available/default b/htadmin/nginx/sites-available/default index 672a85577..7a231f6d6 100644 --- a/htadmin/nginx/sites-available/default +++ b/htadmin/nginx/sites-available/default @@ -3,13 +3,13 @@ server { sendfile on; - root /var/www/htadmin; + root /var/www/; index index.php index.html index.htm; server_name htaccess.malcolm.local; location / { - try_files $uri $uri/ =404; + try_files $uri $uri/htadmin htadmin/$uri/n =404; } location ~ \.php$ { @@ -18,7 +18,7 @@ server { fastcgi_pass unix:/run/php/php7.4-fpm.sock; } - location /config { + location /htadmin/config { deny all; return 404; } From 7f75279800b70a46fb3d14348851df6acba3b275 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 4 Apr 2023 14:32:19 -0600 Subject: [PATCH 089/239] work in progress for idaholab/Malcolm#168 --- Dockerfiles/file-monitor.Dockerfile | 5 + Dockerfiles/zeek.Dockerfile | 3 +- config/zeek.env.example | 4 + file-monitor/supervisord.conf | 2 + scripts/install.py | 2 - scripts/malcolm_utils.py | 14 +- .../normal/0169-pip-installs.hook.chroot | 3 +- shared/bin/zeek_carve_watcher.py | 188 ++++++++++++------ shared/bin/zeek_intel_from_threat_feed.py | 2 +- 9 files changed, 152 insertions(+), 71 deletions(-) diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index 861341022..0eb40c30c 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -26,6 +26,8 @@ ARG ZEEK_LOG_DIRECTORY=/zeek/logs ARG EXTRACTED_FILE_IGNORE_EXISTING=false ARG EXTRACTED_FILE_PRESERVATION=quarantined ARG EXTRACTED_FILE_WATCHER_START_SLEEP=30 +ARG EXTRACTED_FILE_WATCHER_POLLING=false +ARG EXTRACTED_FILE_WATCHER_POLLING_ASSUME_CLOSED_SEC=10 ARG EXTRACTED_FILE_SCANNER_START_SLEEP=10 ARG EXTRACTED_FILE_LOGGER_START_SLEEP=5 ARG EXTRACTED_FILE_MIN_BYTES=64 @@ -55,6 +57,8 @@ ENV ZEEK_LOG_DIRECTORY $ZEEK_LOG_DIRECTORY ENV EXTRACTED_FILE_IGNORE_EXISTING $EXTRACTED_FILE_IGNORE_EXISTING ENV EXTRACTED_FILE_PRESERVATION $EXTRACTED_FILE_PRESERVATION ENV EXTRACTED_FILE_WATCHER_START_SLEEP $EXTRACTED_FILE_WATCHER_START_SLEEP +ENV EXTRACTED_FILE_WATCHER_POLLING $EXTRACTED_FILE_WATCHER_POLLING +ENV EXTRACTED_FILE_WATCHER_POLLING_ASSUME_CLOSED_SEC $EXTRACTED_FILE_WATCHER_POLLING_ASSUME_CLOSED_SEC ENV EXTRACTED_FILE_SCANNER_START_SLEEP $EXTRACTED_FILE_SCANNER_START_SLEEP ENV EXTRACTED_FILE_LOGGER_START_SLEEP $EXTRACTED_FILE_LOGGER_START_SLEEP ENV EXTRACTED_FILE_MIN_BYTES $EXTRACTED_FILE_MIN_BYTES @@ -205,6 +209,7 @@ COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic ADD shared/bin/zeek_carve*.py /usr/local/bin/ +ADD shared/bin/watch_common.py /usr/local/bin/ ADD scripts/malcolm_utils.py /usr/local/bin/ ADD file-monitor/supervisord.conf /etc/supervisord.conf ADD file-monitor/docker-entrypoint.sh /docker-entrypoint.sh diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index 4ab2f3262..ca688465c 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -190,7 +190,8 @@ RUN groupadd --gid ${DEFAULT_GID} ${PUSER} && \ setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip CAP_IPC_LOCK+eip' "${ZEEK_DIR}"/bin/capstats && \ touch "${SUPERCRONIC_CRONTAB}" && \ chown -R ${DEFAULT_UID}:${DEFAULT_GID} "${ZEEK_DIR}"/share/zeek/site/intel "${SUPERCRONIC_CRONTAB}" && \ - ln -sfr /usr/local/bin/pcap_processor.py /usr/local/bin/pcap_zeek_processor.py + ln -sfr /usr/local/bin/pcap_processor.py /usr/local/bin/pcap_zeek_processor.py && \ + ln -sfr /usr/local/bin/malcolm_utils.py "${ZEEK_DIR}"/bin/malcolm_utils.py #Whether or not to auto-tag logs based on filename ARG AUTO_TAG=true diff --git a/config/zeek.env.example b/config/zeek.env.example index cdf8139a5..f88cd4034 100644 --- a/config/zeek.env.example +++ b/config/zeek.env.example @@ -9,6 +9,10 @@ ZEEK_INTEL_FEED_SINCE= ZEEK_INTEL_REFRESH_CRON_EXPRESSION= # Determines the file extraction behavior for file transfers detected by Zeek ZEEK_EXTRACTOR_MODE=none +# Whether or not to use polling vs. native inotify API to watch for files +EXTRACTED_FILE_WATCHER_POLLING=false +# When polling, seconds of inactivity to assume a file is closed and ready for processing +EXTRACTED_FILE_WATCHER_POLLING_ASSUME_CLOSED_SEC=10 # Whether or not files extant in ./zeek-logs/extract_files/ will be ignored on startup EXTRACTED_FILE_IGNORE_EXISTING=false # Determines the behavior for preservation of Zeek-extracted files diff --git a/file-monitor/supervisord.conf b/file-monitor/supervisord.conf index 917044d55..ae9069044 100644 --- a/file-monitor/supervisord.conf +++ b/file-monitor/supervisord.conf @@ -22,6 +22,8 @@ command=/usr/local/bin/zeek_carve_watcher.py --extra-verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA)s --start-sleep %(ENV_EXTRACTED_FILE_WATCHER_START_SLEEP)s --ignore-existing %(ENV_EXTRACTED_FILE_IGNORE_EXISTING)s + --polling "%(ENV_EXTRACTED_FILE_WATCHER_POLLING)s" + --closed-sec %(ENV_EXTRACTED_FILE_WATCHER_POLLING_ASSUME_CLOSED_SEC)s --min-bytes %(ENV_EXTRACTED_FILE_MIN_BYTES)s --max-bytes %(ENV_EXTRACTED_FILE_MAX_BYTES)s --directory "%(ENV_ZEEK_EXTRACTOR_PATH)s" diff --git a/scripts/install.py b/scripts/install.py index abeb1efd7..f71d72dcf 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -34,7 +34,6 @@ ChooseOne, DisplayMessage, DOCKER_COMPOSE_INSTALL_URLS, - dockerComposeUrl, DOCKER_INSTALL_URLS, DotEnvDynamic, DownloadToFile, @@ -53,7 +52,6 @@ ScriptPath, UserInputDefaultsBehavior, UserInterfaceMode, - WindowsInstaller, YAMLDynamic, YesOrNo, ) diff --git a/scripts/malcolm_utils.py b/scripts/malcolm_utils.py index 6fde1fa83..e0d8acf09 100644 --- a/scripts/malcolm_utils.py +++ b/scripts/malcolm_utils.py @@ -4,7 +4,6 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. import contextlib -import datetime import hashlib import ipaddress import json @@ -19,6 +18,7 @@ from base64 import b64decode +from datetime import datetime from multiprocessing import RawValue from subprocess import PIPE, STDOUT, Popen, CalledProcessError from tempfile import NamedTemporaryFile @@ -154,10 +154,18 @@ def dictsearch(d, target): ################################################################################################### # print to stderr def eprint(*args, **kwargs): + filteredArgs = ( + {k: v for (k, v) in kwargs.items() if k not in ('timestamp', 'flush')} if isinstance(kwargs, dict) else {} + ) if "timestamp" in kwargs and kwargs["timestamp"]: - print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), *args, file=sys.stderr, **kwargs) + print( + datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + *args, + file=sys.stderr, + **filteredArgs, + ) else: - print(*args, file=sys.stderr, **kwargs) + print(*args, file=sys.stderr, **filteredArgs) if "flush" in kwargs and kwargs["flush"]: sys.stderr.flush() diff --git a/sensor-iso/config/hooks/normal/0169-pip-installs.hook.chroot b/sensor-iso/config/hooks/normal/0169-pip-installs.hook.chroot index f7f1e95a3..a02ef3d16 100755 --- a/sensor-iso/config/hooks/normal/0169-pip-installs.hook.chroot +++ b/sensor-iso/config/hooks/normal/0169-pip-installs.hook.chroot @@ -12,4 +12,5 @@ pip3 install --no-compile --no-cache-dir --force-reinstall --upgrade \ debinterface \ pymisp \ stix2 \ - taxii2-client + taxii2-client \ + watchdog diff --git a/shared/bin/zeek_carve_watcher.py b/shared/bin/zeek_carve_watcher.py index c60bbb6e4..30f482cdc 100755 --- a/shared/bin/zeek_carve_watcher.py +++ b/shared/bin/zeek_carve_watcher.py @@ -10,18 +10,21 @@ ################################################################################################### import argparse -import copy import glob import json import magic import os import pathlib -import pyinotify import signal import sys import time import zmq +from multiprocessing.pool import ThreadPool +from watchdog.observers import Observer +from watchdog.observers.polling import PollingObserver +from watchdog.utils import WatchdogShutdown + from zeek_carve_utils import ( CAPA_VIV_MIME, CAPA_VIV_SUFFIX, @@ -31,7 +34,9 @@ VENTILATOR_PORT, ) +import malcolm_utils from malcolm_utils import touch, eprint, str2bool +import watch_common ################################################################################################### MINIMUM_CHECKED_FILE_SIZE_DEFAULT = 64 @@ -45,15 +50,12 @@ scriptName = os.path.basename(__file__) scriptPath = os.path.dirname(os.path.realpath(__file__)) origPath = os.getcwd() -shuttingDown = False +shuttingDown = [False] ################################################################################################### # watch files written to and moved to this directory -class EventWatcher(pyinotify.ProcessEvent): - # notify on files written in-place then closed (IN_CLOSE_WRITE), and moved into this directory (IN_MOVED_TO) - _methods = ["IN_CLOSE_WRITE", "IN_MOVED_TO"] - +class EventWatcher: def __init__(self): global debug @@ -75,29 +77,26 @@ def __init__(self): if debug: eprint(f"{scriptName}:\tEventWatcher initialized") - -################################################################################################### -# set up event processor to append processed events from to the event queue -def event_process_generator(cls, method): - # actual method called when we are notified of a file - def _method_name(self, event): + ################################################################################################### + # set up event processor to append processed events from to the event queue + def processFile(self, pathname): global args global debug global verboseDebug if debug: - eprint(f"{scriptName}:\t👓\t{event.pathname}") + eprint(f"{scriptName}:\t👓\t{pathname}") - if (not event.dir) and os.path.isfile(event.pathname): - fileSize = os.path.getsize(event.pathname) + if os.path.isfile(pathname): + fileSize = os.path.getsize(pathname) if args.minBytes <= fileSize <= args.maxBytes: - fileType = magic.from_file(event.pathname, mime=True) - if (pathlib.Path(event.pathname).suffix != CAPA_VIV_SUFFIX) and (fileType != CAPA_VIV_MIME): + fileType = magic.from_file(pathname, mime=True) + if (pathlib.Path(pathname).suffix != CAPA_VIV_SUFFIX) and (fileType != CAPA_VIV_MIME): # the entity is a right-sized file, is not a capa .viv cache file, and it exists, so send it to get scanned fileInfo = json.dumps( { - FILE_SCAN_RESULT_FILE: event.pathname, + FILE_SCAN_RESULT_FILE: pathname, FILE_SCAN_RESULT_FILE_SIZE: fileSize, FILE_SCAN_RESULT_FILE_TYPE: fileType, } @@ -107,32 +106,33 @@ def _method_name(self, event): try: self.ventilator_socket.send_string(fileInfo) if debug: - eprint(f"{scriptName}:\t📫\t{event.pathname}") + eprint(f"{scriptName}:\t📫\t{pathname}") except zmq.Again: if verboseDebug: - eprint(f"{scriptName}:\t🕑\t{event.pathname}") + eprint(f"{scriptName}:\t🕑\t{pathname}") else: # temporary capa .viv file, just ignore it as it will get cleaned up by the scanner when it's done if debug: - eprint(f"{scriptName}:\t🚧\t{event.pathname}") + eprint(f"{scriptName}:\t🚧\t{pathname}") else: # too small/big to care about, delete it - os.remove(event.pathname) + os.remove(pathname) if debug: - eprint(f"{scriptName}:\t🚫\t{event.pathname}") + eprint(f"{scriptName}:\t🚫\t{pathname}") - # assign process method to class - _method_name.__name__ = "process_{}".format(method) - setattr(cls, _method_name.__name__, _method_name) + +def file_processor(pathname, **kwargs): + if "watcher" in kwargs and kwargs["watcher"]: + kwargs["watcher"].processFile(pathname) ################################################################################################### # handle sigint/sigterm and set a global shutdown variable def shutdown_handler(signum, frame): global shuttingDown - shuttingDown = True + shuttingDown[0] = True ################################################################################################### @@ -214,6 +214,30 @@ def main(): type=str, required=False, ) + parser.add_argument( + '-p', + '--polling', + dest='polling', + help="Use polling (instead of inotify)", + metavar='true|false', + type=str2bool, + nargs='?', + const=True, + default=os.getenv('EXTRACTED_FILE_WATCHER_POLLING', False), + required=False, + ) + parser.add_argument( + '-c', + '--closed-sec', + dest='assumeClosedSec', + help="When polling, assume a file is closed after this many seconds of inactivity", + metavar='', + type=int, + default=int( + os.getenv('EXTRACTED_FILE_WATCHER_POLLING_ASSUME_CLOSED_SEC', str(watch_common.ASSUME_CLOSED_SEC_DEFAULT)) + ), + required=False, + ) parser.add_argument( '--min-bytes', dest='minBytes', @@ -261,21 +285,17 @@ def main(): # sleep for a bit if requested sleepCount = 0 - while (not shuttingDown) and (sleepCount < args.startSleepSec): + while (not shuttingDown[0]) and (sleepCount < args.startSleepSec): time.sleep(1) sleepCount += 1 - # add events to watch to EventWatcher class - for method in EventWatcher._methods: - event_process_generator(EventWatcher, method) - # if directory to monitor doesn't exist, create it now if os.path.isdir(args.baseDir): preexistingDir = True else: preexistingDir = False if debug: - eprint(f'{scriptName}: creating "{args.baseDir}" to monitor') + eprint(f'{scriptName}:\tcreating "{args.baseDir}" to monitor') pathlib.Path(args.baseDir).mkdir(parents=False, exist_ok=True) # if recursion was requested, get list of directories to monitor @@ -288,40 +308,82 @@ def main(): # begin threaded watch of path(s) time.sleep(1) - watch_manager = pyinotify.WatchManager() - event_notifier = pyinotify.ThreadedNotifier(watch_manager, EventWatcher()) + + observer = PollingObserver() if args.polling else Observer() + handler = watch_common.FileOperationEventHandler( + logger=None, + polling=args.polling, + ) for watchDir in watchDirs: - watch_manager.add_watch(os.path.abspath(watchDir), pyinotify.ALL_EVENTS) - if debug: - eprint(f"{scriptName}: monitoring {watchDirs}") - time.sleep(2) - event_notifier.start() - - # if there are any previously included files (and not ignoreExisting), "touch" them so that they will be notified on - if preexistingDir and (not args.ignoreExisting): - filesTouched = 0 - for watchDir in watchDirs: - for preexistingFile in [os.path.join(watchDir, x) for x in pathlib.Path(watchDir).iterdir() if x.is_file()]: - touch(preexistingFile) - filesTouched += 1 - if debug and (filesTouched > 0): - eprint(f"{scriptName}: found {filesTouched} preexisting files to check") - - # loop forever, or until we're told to shut down, whichever comes first - while not shuttingDown: - if pdbFlagged: - pdbFlagged = False - breakpoint() - time.sleep(0.2) - - # graceful shutdown + if verboseDebug: + eprint(f"{scriptName}:\tScheduling {watchDir}") + observer.schedule(handler, watchDir, recursive=False) + + observer.start() + if debug: - eprint(f"{scriptName}: shutting down...") - event_notifier.stop() + eprint(f"{scriptName}:\tmonitoring {watchDirs}") + + try: + time.sleep(2) + + # if there are any previously included files (and not ignoreExisting), "touch" them so that they will be notified on + if preexistingDir and (not args.ignoreExisting) and (not shuttingDown[0]): + filesTouched = 0 + for watchDir in watchDirs: + for preexistingFile in [ + os.path.join(watchDir, x) for x in pathlib.Path(watchDir).iterdir() if x.is_file() + ]: + touch(preexistingFile) + filesTouched += 1 + if debug and (filesTouched > 0): + eprint(f"{scriptName}:\tfound {filesTouched} preexisting files to check") + + # start the thread to actually handle the files as they're queued by the FileOperationEventHandler handler + workerThreadCount = malcolm_utils.AtomicInt(value=0) + ThreadPool( + 1, + watch_common.ProcessFileEventWorker( + [ + handler, + observer, + file_processor, + {'watcher': EventWatcher()}, + args.assumeClosedSec, + workerThreadCount, + shuttingDown, + None, + ], + ), + ) + + # loop forever, or until we're told to shut down, whichever comes first + while (not shuttingDown[0]) and observer.is_alive(): + if pdbFlagged: + pdbFlagged = False + breakpoint() + observer.join(1) + + # graceful shutdown + if debug: + eprint(f"{scriptName}:\tshutting down...") + + if shuttingDown[0]: + raise WatchdogShutdown() + + except WatchdogShutdown: + observer.unschedule_all() + + finally: + observer.stop() + observer.join() + time.sleep(1) + while workerThreadCount.value() > 0: + time.sleep(1) if debug: - eprint(f"{scriptName}: finished monitoring {watchDirs}") + eprint(f"{scriptName}:\tfinished monitoring {watchDirs}") if __name__ == '__main__': diff --git a/shared/bin/zeek_intel_from_threat_feed.py b/shared/bin/zeek_intel_from_threat_feed.py index e2bb2cdfe..45184e838 100755 --- a/shared/bin/zeek_intel_from_threat_feed.py +++ b/shared/bin/zeek_intel_from_threat_feed.py @@ -16,7 +16,7 @@ import zeek_threat_feed_utils import malcolm_utils -from malcolm_utils import nullcontext +from contextlib import nullcontext ################################################################################################### script_name = os.path.basename(__file__) From cca2aa3b727315938ec95be0cb6efc278da3291b Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 4 Apr 2023 15:26:19 -0600 Subject: [PATCH 090/239] work in progress for idaholab/Malcolm#168 --- Dockerfiles/file-monitor.Dockerfile | 1 - Dockerfiles/pcap-monitor.Dockerfile | 2 +- sensor-iso/config/package-lists/python.list.chroot | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index 0eb40c30c..1a715ef29 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -134,7 +134,6 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l python3-bs4 \ python3-dev \ python3-pip \ - python3-pyinotify \ python3-requests \ python3-zmq \ rsync && \ diff --git a/Dockerfiles/pcap-monitor.Dockerfile b/Dockerfiles/pcap-monitor.Dockerfile index 27f298e64..6b089f71f 100644 --- a/Dockerfiles/pcap-monitor.Dockerfile +++ b/Dockerfiles/pcap-monitor.Dockerfile @@ -64,7 +64,7 @@ RUN apt-get -q update && \ vim-tiny && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ - pip3 install --no-cache-dir opensearch-py pyzmq pyinotify python-magic requests watchdog && \ + pip3 install --no-cache-dir opensearch-py pyzmq python-magic requests watchdog && \ groupadd --gid ${DEFAULT_GID} ${PGROUP} && \ useradd -M --uid ${DEFAULT_UID} --gid ${DEFAULT_GID} ${PUSER} diff --git a/sensor-iso/config/package-lists/python.list.chroot b/sensor-iso/config/package-lists/python.list.chroot index 385f990d6..88abf3f76 100644 --- a/sensor-iso/config/package-lists/python.list.chroot +++ b/sensor-iso/config/package-lists/python.list.chroot @@ -9,7 +9,6 @@ python3-netifaces python3-pip python3-psutil python3-pycryptodome -python3-pyinotify python3-requests python3-ruamel.yaml python3-semantic-version From 93583a3278a1b9347c7f23e7866c345cd5d58370 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 4 Apr 2023 21:41:57 -0600 Subject: [PATCH 091/239] work in progress for idaholab/Malcolm#168 --- Dockerfiles/arkime.Dockerfile | 1 + Dockerfiles/filebeat.Dockerfile | 7 +- config/filebeat.env.example | 4 + .../filebeat-watch-zeeklogs-uploads-folder.py | 240 ++++++++++++++++++ .../filebeat-watch-zeeklogs-uploads-folder.sh | 22 -- filebeat/supervisord.conf | 9 +- 6 files changed, 259 insertions(+), 24 deletions(-) create mode 100755 filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py delete mode 100755 filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.sh diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index b9026370d..76989be9d 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -142,6 +142,7 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l file \ geoip-bin \ gettext \ + inotify-tools \ jq \ libcap2-bin \ libjson-perl \ diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index 302d67438..e63eac2e8 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -17,7 +17,7 @@ ENV DEFAULT_GID $DEFAULT_GID ENV PUSER "filebeat" ENV PGROUP "filebeat" # not dropping privileges globally: supervisord will take care of it -# on a case-by-case basis so that one script (filebeat-watch-zeeklogs-uploads-folder.sh) +# on a case-by-case basis so that one script (filebeat-watch-zeeklogs-uploads-folder.py) # can chown uploaded files ENV PUSER_PRIV_DROP false @@ -37,6 +37,8 @@ ARG FILEBEAT_ZEEK_LOG_PATH="/zeek/current" ARG FILEBEAT_ZEEK_LOG_LIVE_PATH="/zeek/live" ARG FILEBEAT_SURICATA_LOG_PATH="/suricata" ARG FILEBEAT_NGINX_LOG_PATH="/nginx" +ARG FILEBEAT_WATCHER_POLLING=false +ARG FILEBEAT_WATCHER_POLLING_ASSUME_CLOSED_SEC=10 ARG LOG_CLEANUP_MINUTES=0 ARG ZIP_CLEANUP_MINUTES=0 ARG NGINX_LOG_ACCESS_AND_ERRORS=false @@ -110,6 +112,7 @@ ADD filebeat/filebeat-nginx.yml /usr/share/filebeat-nginx/filebeat-nginx.yml ADD filebeat/filebeat-tcp.yml /usr/share/filebeat-tcp/filebeat-tcp.yml ADD filebeat/scripts /usr/local/bin/ ADD scripts/malcolm_utils.py /usr/local/bin/ +ADD shared/bin/watch_common.py /usr/local/bin/ ADD shared/bin/opensearch_status.sh /usr/local/bin/ ADD filebeat/supervisord.conf /etc/supervisord.conf RUN for INPUT in nginx tcp; do \ @@ -125,6 +128,8 @@ RUN for INPUT in nginx tcp; do \ ENV AUTO_TAG $AUTO_TAG ENV LOG_CLEANUP_MINUTES $LOG_CLEANUP_MINUTES ENV ZIP_CLEANUP_MINUTES $ZIP_CLEANUP_MINUTES +ENV FILEBEAT_WATCHER_POLLING $FILEBEAT_WATCHER_POLLING +ENV FILEBEAT_WATCHER_POLLING_ASSUME_CLOSED_SEC $FILEBEAT_WATCHER_POLLING_ASSUME_CLOSED_SEC ENV FILEBEAT_SCAN_FREQUENCY $FILEBEAT_SCAN_FREQUENCY ENV FILEBEAT_CLEAN_INACTIVE $FILEBEAT_CLEAN_INACTIVE ENV FILEBEAT_IGNORE_OLDER $FILEBEAT_IGNORE_OLDER diff --git a/config/filebeat.env.example b/config/filebeat.env.example index 6745b7e97..e0c9d9bda 100644 --- a/config/filebeat.env.example +++ b/config/filebeat.env.example @@ -9,6 +9,10 @@ FILEBEAT_CLOSE_RENAMED=true FILEBEAT_CLOSE_REMOVED=true FILEBEAT_CLOSE_EOF=true FILEBEAT_CLEAN_REMOVED=true +# Whether or not to use polling vs. native inotify API to watch for files +FILEBEAT_WATCHER_POLLING=false +# When polling, seconds of inactivity to assume a file is closed and ready for processing +FILEBEAT_WATCHER_POLLING_ASSUME_CLOSED_SEC=10 # Whether or not to expose a filebeat TCP input listener (see # https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-tcp.html) FILEBEAT_TCP_LISTEN=false diff --git a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py new file mode 100755 index 000000000..3809a0ee4 --- /dev/null +++ b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. + +################################################################################################### +# Monitor a directory for PCAP files for processing (by publishing their filenames to a ZMQ socket) +# +# Run the script with --help for options +################################################################################################### + +import argparse +import glob +import logging +import magic +import os +import pathlib +import signal +import sys +import time + +import malcolm_utils +from malcolm_utils import eprint, str2bool, touch +import watch_common + +################################################################################################### +scriptName = os.path.basename(__file__) +scriptPath = os.path.dirname(os.path.realpath(__file__)) +origPath = os.getcwd() +shuttingDown = [False] + +SUPPORTED_MIME_TYPES = [ + 'application/gzip', + 'application/x-gzip', + 'application/x-7z-compressed', + 'application/x-bzip2', + 'application/x-cpio', + 'application/x-lzip', + 'application/x-lzma', + 'application/x-rar-compressed', + 'application/x-tar', + 'application/x-xz', + 'application/zip', +] + + +################################################################################################### +# handle sigint/sigterm and set a global shutdown variable +def shutdown_handler(signum, frame): + global shuttingDown + shuttingDown[0] = True + + +################################################################################################### +def file_processor(pathname, **kwargs): + mime_types = kwargs["mime_types"] + uid = kwargs["uid"] + gid = kwargs["gid"] + destination = kwargs["destination"] + logger = kwargs["logger"] + + logger.debug(f"{scriptName}:\t👓\t{pathname}") + + if os.path.isfile(pathname) and os.path.isdir(destination): + time.sleep(0.1) + try + os.chown(pathname, uid, gid) + + # get the file magic mime type + fileMime = magic.from_file(pathname, mime=True) + + if fileMime in mime_types: + # looks like this is a compressed file, we're assuming it's a zeek log archive to be processed by filebeat + logger.debug(f"{scriptName}:\t📩\t{pathname} ({fileMime})") + shutil.move(pathname, destination) + + else: + # unhandled file type uploaded, delete it + logger.info(f"{scriptName}:\t✋\t{pathname} ({fileMime})") + os.unlink(pathname) + + except Exception as genericError: + logger.warning(f"{scriptName}:\texception: {genericError}") + + +################################################################################################### +# main +def main(): + global shuttingDown + + parser = argparse.ArgumentParser( + description=scriptName, + add_help=False, + usage='{} '.format(scriptName), + ) + parser.add_argument( + '-r', + '--recursive-directory', + dest='recursiveDir', + help="If specified, monitor all directories with this name underneath --directory", + metavar='', + type=str, + required=False, + ) + parser.add_argument( + '-p', + '--polling', + dest='polling', + help="Use polling (instead of inotify)", + metavar='true|false', + type=str2bool, + nargs='?', + const=True, + default=os.getenv('FILEBEAT_WATCHER_POLLING', False), + required=False, + ) + parser.add_argument( + '-c', + '--closed-sec', + dest='assumeClosedSec', + help="When polling, assume a file is closed after this many seconds of inactivity", + metavar='', + type=int, + default=int( + os.getenv('FILEBEAT_WATCHER_POLLING_ASSUME_CLOSED_SEC', str(watch_common.ASSUME_CLOSED_SEC_DEFAULT)) + ), + required=False, + ) + requiredNamed.add_argument( + '-i', + '--in', + dest='srcDir', + help='Source directory to monitor', + metavar='', + type=str, + default=os.path.join(os.getenv('FILEBEAT_ZEEK_DIR', '/zeek'), 'upload'), + required=False, + ) + requiredNamed.add_argument( + '-o', + '--out', + dest='dstDir', + help='Destination directory', + metavar='', + type=str, + default=os.getenv('FILEBEAT_ZEEK_DIR', '/zeek'), + required=False, + ) + requiredNamed.add_argument( + '-u', + '--uid', + dest='chownUid', + help='UID to chown files', + metavar='', + type=int, + default=int(os.getenv('PUID', os.getenv('DEFAULT_UID', '1000'))), + required=False, + ) + requiredNamed.add_argument( + '-g', + '--gid', + dest='chownGid', + help='UID to chown files', + metavar='', + type=int, + default=int(os.getenv('PGID', os.getenv('DEFAULT_GID', '1000'))), + required=False, + ) + parser.add_argument( + '--start-sleep', + dest='startSleepSec', + help="Sleep for this many seconds before starting", + metavar='', + type=int, + default=0, + required=False, + ) + + try: + parser.error = parser.exit + args = parser.parse_args() + except SystemExit: + parser.print_help() + exit(2) + + args.verbose = logging.ERROR - (10 * args.verbose) if args.verbose > 0 else 0 + logging.basicConfig( + level=args.verbose, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' + ) + logging.debug(os.path.join(scriptPath, scriptName)) + logging.debug("Arguments: {}".format(sys.argv[1:])) + logging.debug("Arguments: {}".format(args)) + if args.verbose > logging.DEBUG: + sys.tracebacklimit = 0 + + # handle sigint and sigterm for graceful shutdown + signal.signal(signal.SIGINT, shutdown_handler) + signal.signal(signal.SIGTERM, shutdown_handler) + signal.signal(signal.SIGUSR1, pdb_handler) + signal.signal(signal.SIGUSR2, debug_toggle_handler) + + # sleep for a bit if requested + sleepCount = 0 + while (not shuttingDown[0]) and (sleepCount < args.startSleepSec): + time.sleep(1) + sleepCount += 1 + + # if directory to monitor doesn't exist, create it now + if not os.path.isdir(args.srcDir): + if debug: + eprint(f'{scriptName}:\tcreating "{args.srcDir}" to monitor') + pathlib.Path(args.srcDir).mkdir(parents=False, exist_ok=True) + + # if recursion was requested, get list of directories to monitor + watchDirs = [] + while len(watchDirs) == 0: + if args.recursiveDir is None: + watchDirs = [args.srcDir] + else: + watchDirs = glob.glob(f'{args.srcDir}/**/{args.recursiveDir}', recursive=True) + + watch_common.WatchAndProcessDirectory( + watchDirs, + args.polling, + file_processor, + { + "logger": logging, + "destination": args.dstDir, + "uid": args.chownUid, + "gid": args.chownGid, + "mime_types": SUPPORTED_MIME_TYPES, + }, + args.assumeClosedSec, + shuttingDown, + logging, + ) + + +if __name__ == '__main__': + main() diff --git a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.sh b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.sh deleted file mode 100755 index 09fc1b484..000000000 --- a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. - - -PROCESS_DIR=${FILEBEAT_ZEEK_DIR:-/zeek/} -UPLOAD_DIR="${PROCESS_DIR}/upload" -mkdir -p "$UPLOAD_DIR" - -# as new zeek log archives are closed for writing in /zeek/upload, move them to /zeek for processing -inotifywait -m -e close_write --format '%w%f' "${UPLOAD_DIR}" | while read NEWFILE -do - FILEMIME=$(file -b --mime-type "$NEWFILE") - if ( echo "$FILEMIME" | grep --quiet -P "(application/gzip|application/x-gzip|application/x-7z-compressed|application/x-bzip2|application/x-cpio|application/x-lzip|application/x-lzma|application/x-rar-compressed|application/x-tar|application/x-xz|application/zip)" ); then - # looks like this is a compressed file, we're assuming it's a zeek log archive to be processed by filebeat - sleep 0.1 && chown ${PUID:-${DEFAULT_UID}}:${PGID:-${DEFAULT_GID}} "$NEWFILE" && (>&2 mv -v "$NEWFILE" "$PROCESS_DIR/") - else - # unhandled file type uploaded, delete it - sleep 0.1 && chown ${PUID:-${DEFAULT_UID}}:${PGID:-${DEFAULT_GID}} && (>&2 rm "$NEWFILE") && echo "Removed \"$NEWFILE\", unhandled file type \"$FILEMIME\"" - fi -done - diff --git a/filebeat/supervisord.conf b/filebeat/supervisord.conf index e19cf460a..f0b2ab382 100644 --- a/filebeat/supervisord.conf +++ b/filebeat/supervisord.conf @@ -65,7 +65,14 @@ stdout_logfile_maxbytes=0 redirect_stderr=true [program:watch-upload] -command=/bin/bash -c "sleep 30 && /usr/local/bin/filebeat-watch-zeeklogs-uploads-folder.sh" +command=python3 /usr/local/bin/filebeat-watch-zeeklogs-uploads-folder.py + --start-sleep 30 + --polling "%(ENV_FILEBEAT_WATCHER_POLLING)s" + --closed-sec %(ENV_FILEBEAT_WATCHER_POLLING_ASSUME_CLOSED_SEC)s + --in "%(ENV_FILEBEAT_ZEEK_DIR)s"/upload + --out "%(ENV_FILEBEAT_ZEEK_DIR)s" + --uid %(ENV_PUID)s + --gid %(ENV_PGID)s user=root startsecs=35 startretries=1 From decc0f8de5fd8ac0406526a67bba342f3ff0b6b2 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 5 Apr 2023 09:32:36 -0600 Subject: [PATCH 092/239] update version numbers for v23.04.1 development --- docker-compose-standalone.yml | 46 +++++++++++++++++----------------- docker-compose.yml | 46 +++++++++++++++++----------------- docs/download.md | 4 +-- docs/hedgehog-iso-build.md | 2 +- docs/malcolm-iso.md | 2 +- docs/quickstart.md | 40 ++++++++++++++--------------- docs/ubuntu-install-example.md | 40 ++++++++++++++--------------- 7 files changed, 90 insertions(+), 90 deletions(-) diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 2e11c66c4..bdbfdb9a6 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -359,7 +359,7 @@ x-pcap-capture-variables: &pcap-capture-variables services: opensearch: - image: ghcr.io/idaholab/malcolm/opensearch:23.04.0 + image: ghcr.io/idaholab/malcolm/opensearch:23.04.1 restart: "no" stdin_open: false tty: true @@ -398,7 +398,7 @@ services: retries: 3 start_period: 180s dashboards-helper: - image: ghcr.io/idaholab/malcolm/dashboards-helper:23.04.0 + image: ghcr.io/idaholab/malcolm/dashboards-helper:23.04.1 restart: "no" stdin_open: false tty: true @@ -426,7 +426,7 @@ services: retries: 3 start_period: 30s dashboards: - image: ghcr.io/idaholab/malcolm/dashboards:23.04.0 + image: ghcr.io/idaholab/malcolm/dashboards:23.04.1 restart: "no" stdin_open: false tty: true @@ -449,7 +449,7 @@ services: retries: 3 start_period: 210s logstash: - image: ghcr.io/idaholab/malcolm/logstash-oss:23.04.0 + image: ghcr.io/idaholab/malcolm/logstash-oss:23.04.1 restart: "no" stdin_open: false tty: true @@ -486,7 +486,7 @@ services: retries: 3 start_period: 600s filebeat: - image: ghcr.io/idaholab/malcolm/filebeat-oss:23.04.0 + image: ghcr.io/idaholab/malcolm/filebeat-oss:23.04.1 restart: "no" stdin_open: false tty: true @@ -519,7 +519,7 @@ services: retries: 3 start_period: 60s arkime: - image: ghcr.io/idaholab/malcolm/arkime:23.04.0 + image: ghcr.io/idaholab/malcolm/arkime:23.04.1 restart: "no" stdin_open: false tty: true @@ -553,7 +553,7 @@ services: retries: 3 start_period: 210s zeek: - image: ghcr.io/idaholab/malcolm/zeek:23.04.0 + image: ghcr.io/idaholab/malcolm/zeek:23.04.1 restart: "no" stdin_open: false tty: true @@ -588,7 +588,7 @@ services: retries: 3 start_period: 60s zeek-live: - image: ghcr.io/idaholab/malcolm/zeek:23.04.0 + image: ghcr.io/idaholab/malcolm/zeek:23.04.1 restart: "no" stdin_open: false tty: true @@ -615,7 +615,7 @@ services: - ./zeek-logs/extract_files:/zeek/extract_files - ./zeek/intel:/opt/zeek/share/zeek/site/intel suricata: - image: ghcr.io/idaholab/malcolm/suricata:23.04.0 + image: ghcr.io/idaholab/malcolm/suricata:23.04.1 restart: "no" stdin_open: false tty: true @@ -648,7 +648,7 @@ services: retries: 3 start_period: 120s suricata-live: - image: ghcr.io/idaholab/malcolm/suricata:23.04.0 + image: ghcr.io/idaholab/malcolm/suricata:23.04.1 restart: "no" stdin_open: false tty: true @@ -670,7 +670,7 @@ services: - ./suricata-logs:/var/log/suricata - ./suricata/rules:/opt/suricata/rules:ro file-monitor: - image: ghcr.io/idaholab/malcolm/file-monitor:23.04.0 + image: ghcr.io/idaholab/malcolm/file-monitor:23.04.1 restart: "no" stdin_open: false tty: true @@ -692,7 +692,7 @@ services: retries: 3 start_period: 60s pcap-capture: - image: ghcr.io/idaholab/malcolm/pcap-capture:23.04.0 + image: ghcr.io/idaholab/malcolm/pcap-capture:23.04.1 restart: "no" stdin_open: false tty: true @@ -712,7 +712,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/pcap pcap-monitor: - image: ghcr.io/idaholab/malcolm/pcap-monitor:23.04.0 + image: ghcr.io/idaholab/malcolm/pcap-monitor:23.04.1 restart: "no" stdin_open: false tty: true @@ -735,7 +735,7 @@ services: retries: 3 start_period: 90s upload: - image: ghcr.io/idaholab/malcolm/file-upload:23.04.0 + image: ghcr.io/idaholab/malcolm/file-upload:23.04.1 restart: "no" stdin_open: false tty: true @@ -762,7 +762,7 @@ services: retries: 3 start_period: 60s htadmin: - image: ghcr.io/idaholab/malcolm/htadmin:23.04.0 + image: ghcr.io/idaholab/malcolm/htadmin:23.04.1 restart: "no" stdin_open: false tty: true @@ -784,7 +784,7 @@ services: retries: 3 start_period: 60s freq: - image: ghcr.io/idaholab/malcolm/freq:23.04.0 + image: ghcr.io/idaholab/malcolm/freq:23.04.1 restart: "no" stdin_open: false tty: true @@ -803,7 +803,7 @@ services: retries: 3 start_period: 60s name-map-ui: - image: ghcr.io/idaholab/malcolm/name-map-ui:23.04.0 + image: ghcr.io/idaholab/malcolm/name-map-ui:23.04.1 restart: "no" stdin_open: false tty: true @@ -823,7 +823,7 @@ services: retries: 3 start_period: 60s netbox: - image: ghcr.io/idaholab/malcolm/netbox:23.04.0 + image: ghcr.io/idaholab/malcolm/netbox:23.04.1 restart: "no" stdin_open: false tty: true @@ -852,7 +852,7 @@ services: retries: 3 start_period: 120s netbox-postgres: - image: ghcr.io/idaholab/malcolm/postgresql:23.04.0 + image: ghcr.io/idaholab/malcolm/postgresql:23.04.1 restart: "no" stdin_open: false tty: true @@ -873,7 +873,7 @@ services: retries: 3 start_period: 45s netbox-redis: - image: ghcr.io/idaholab/malcolm/redis:23.04.0 + image: ghcr.io/idaholab/malcolm/redis:23.04.1 restart: "no" stdin_open: false tty: true @@ -898,7 +898,7 @@ services: retries: 3 start_period: 45s netbox-redis-cache: - image: ghcr.io/idaholab/malcolm/redis:23.04.0 + image: ghcr.io/idaholab/malcolm/redis:23.04.1 restart: "no" stdin_open: false tty: true @@ -922,7 +922,7 @@ services: retries: 3 start_period: 45s api: - image: ghcr.io/idaholab/malcolm/api:23.04.0 + image: ghcr.io/idaholab/malcolm/api:23.04.1 command: gunicorn --bind 0:5000 manage:app restart: "no" stdin_open: false @@ -943,7 +943,7 @@ services: retries: 3 start_period: 60s nginx-proxy: - image: ghcr.io/idaholab/malcolm/nginx-proxy:23.04.0 + image: ghcr.io/idaholab/malcolm/nginx-proxy:23.04.1 restart: "no" stdin_open: false tty: true diff --git a/docker-compose.yml b/docker-compose.yml index 600d418e8..db60c7e3b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -362,7 +362,7 @@ services: build: context: . dockerfile: Dockerfiles/opensearch.Dockerfile - image: ghcr.io/idaholab/malcolm/opensearch:23.04.0 + image: ghcr.io/idaholab/malcolm/opensearch:23.04.1 restart: "no" stdin_open: false tty: true @@ -404,7 +404,7 @@ services: build: context: . dockerfile: Dockerfiles/dashboards-helper.Dockerfile - image: ghcr.io/idaholab/malcolm/dashboards-helper:23.04.0 + image: ghcr.io/idaholab/malcolm/dashboards-helper:23.04.1 restart: "no" stdin_open: false tty: true @@ -435,7 +435,7 @@ services: build: context: . dockerfile: Dockerfiles/dashboards.Dockerfile - image: ghcr.io/idaholab/malcolm/dashboards:23.04.0 + image: ghcr.io/idaholab/malcolm/dashboards:23.04.1 restart: "no" stdin_open: false tty: true @@ -461,7 +461,7 @@ services: build: context: . dockerfile: Dockerfiles/logstash.Dockerfile - image: ghcr.io/idaholab/malcolm/logstash-oss:23.04.0 + image: ghcr.io/idaholab/malcolm/logstash-oss:23.04.1 restart: "no" stdin_open: false tty: true @@ -505,7 +505,7 @@ services: build: context: . dockerfile: Dockerfiles/filebeat.Dockerfile - image: ghcr.io/idaholab/malcolm/filebeat-oss:23.04.0 + image: ghcr.io/idaholab/malcolm/filebeat-oss:23.04.1 restart: "no" stdin_open: false tty: true @@ -541,7 +541,7 @@ services: build: context: . dockerfile: Dockerfiles/arkime.Dockerfile - image: ghcr.io/idaholab/malcolm/arkime:23.04.0 + image: ghcr.io/idaholab/malcolm/arkime:23.04.1 restart: "no" stdin_open: false tty: true @@ -581,7 +581,7 @@ services: build: context: . dockerfile: Dockerfiles/zeek.Dockerfile - image: ghcr.io/idaholab/malcolm/zeek:23.04.0 + image: ghcr.io/idaholab/malcolm/zeek:23.04.1 restart: "no" stdin_open: false tty: true @@ -620,7 +620,7 @@ services: build: context: . dockerfile: Dockerfiles/zeek.Dockerfile - image: ghcr.io/idaholab/malcolm/zeek:23.04.0 + image: ghcr.io/idaholab/malcolm/zeek:23.04.1 restart: "no" stdin_open: false tty: true @@ -651,7 +651,7 @@ services: build: context: . dockerfile: Dockerfiles/suricata.Dockerfile - image: ghcr.io/idaholab/malcolm/suricata:23.04.0 + image: ghcr.io/idaholab/malcolm/suricata:23.04.1 restart: "no" stdin_open: false tty: true @@ -687,7 +687,7 @@ services: build: context: . dockerfile: Dockerfiles/suricata.Dockerfile - image: ghcr.io/idaholab/malcolm/suricata:23.04.0 + image: ghcr.io/idaholab/malcolm/suricata:23.04.1 restart: "no" stdin_open: false tty: true @@ -712,7 +712,7 @@ services: build: context: . dockerfile: Dockerfiles/file-monitor.Dockerfile - image: ghcr.io/idaholab/malcolm/file-monitor:23.04.0 + image: ghcr.io/idaholab/malcolm/file-monitor:23.04.1 restart: "no" stdin_open: false tty: true @@ -737,7 +737,7 @@ services: build: context: . dockerfile: Dockerfiles/pcap-capture.Dockerfile - image: ghcr.io/idaholab/malcolm/pcap-capture:23.04.0 + image: ghcr.io/idaholab/malcolm/pcap-capture:23.04.1 restart: "no" stdin_open: false tty: true @@ -760,7 +760,7 @@ services: build: context: . dockerfile: Dockerfiles/pcap-monitor.Dockerfile - image: ghcr.io/idaholab/malcolm/pcap-monitor:23.04.0 + image: ghcr.io/idaholab/malcolm/pcap-monitor:23.04.1 restart: "no" stdin_open: false tty: true @@ -786,7 +786,7 @@ services: build: context: . dockerfile: Dockerfiles/file-upload.Dockerfile - image: ghcr.io/idaholab/malcolm/file-upload:23.04.0 + image: ghcr.io/idaholab/malcolm/file-upload:23.04.1 restart: "no" stdin_open: false tty: true @@ -813,7 +813,7 @@ services: retries: 3 start_period: 60s htadmin: - image: ghcr.io/idaholab/malcolm/htadmin:23.04.0 + image: ghcr.io/idaholab/malcolm/htadmin:23.04.1 build: context: . dockerfile: Dockerfiles/htadmin.Dockerfile @@ -838,7 +838,7 @@ services: retries: 3 start_period: 60s freq: - image: ghcr.io/idaholab/malcolm/freq:23.04.0 + image: ghcr.io/idaholab/malcolm/freq:23.04.1 build: context: . dockerfile: Dockerfiles/freq.Dockerfile @@ -860,7 +860,7 @@ services: retries: 3 start_period: 60s name-map-ui: - image: ghcr.io/idaholab/malcolm/name-map-ui:23.04.0 + image: ghcr.io/idaholab/malcolm/name-map-ui:23.04.1 build: context: . dockerfile: Dockerfiles/name-map-ui.Dockerfile @@ -883,7 +883,7 @@ services: retries: 3 start_period: 60s netbox: - image: ghcr.io/idaholab/malcolm/netbox:23.04.0 + image: ghcr.io/idaholab/malcolm/netbox:23.04.1 build: context: . dockerfile: Dockerfiles/netbox.Dockerfile @@ -916,7 +916,7 @@ services: retries: 3 start_period: 120s netbox-postgres: - image: ghcr.io/idaholab/malcolm/postgresql:23.04.0 + image: ghcr.io/idaholab/malcolm/postgresql:23.04.1 build: context: . dockerfile: Dockerfiles/postgresql.Dockerfile @@ -940,7 +940,7 @@ services: retries: 3 start_period: 45s netbox-redis: - image: ghcr.io/idaholab/malcolm/redis:23.04.0 + image: ghcr.io/idaholab/malcolm/redis:23.04.1 build: context: . dockerfile: Dockerfiles/redis.Dockerfile @@ -968,7 +968,7 @@ services: retries: 3 start_period: 45s netbox-redis-cache: - image: ghcr.io/idaholab/malcolm/redis:23.04.0 + image: ghcr.io/idaholab/malcolm/redis:23.04.1 build: context: . dockerfile: Dockerfiles/redis.Dockerfile @@ -995,7 +995,7 @@ services: retries: 3 start_period: 45s api: - image: ghcr.io/idaholab/malcolm/api:23.04.0 + image: ghcr.io/idaholab/malcolm/api:23.04.1 build: context: . dockerfile: Dockerfiles/api.Dockerfile @@ -1022,7 +1022,7 @@ services: build: context: . dockerfile: Dockerfiles/nginx.Dockerfile - image: ghcr.io/idaholab/malcolm/nginx-proxy:23.04.0 + image: ghcr.io/idaholab/malcolm/nginx-proxy:23.04.1 restart: "no" stdin_open: false tty: true diff --git a/docs/download.md b/docs/download.md index 228d137d8..2612f4852 100644 --- a/docs/download.md +++ b/docs/download.md @@ -16,7 +16,7 @@ While official downloads of the Malcolm installer ISO are not provided, an **uno | ISO | SHA256 | |---|---| -| [malcolm-23.04.0.iso](/iso/malcolm-23.04.0.iso) (5.2GiB) | [`6f8292a3c0c0c43b3ea7919b0b5ad1caa1140796da315a779522cb998dea8d13`](/iso/malcolm-23.04.0.iso.sha256.txt) | +| [malcolm-23.04.1.iso](/iso/malcolm-23.04.1.iso) (5.2GiB) | [`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`](/iso/malcolm-23.04.1.iso.sha256.txt) | ## Hedgehog Linux @@ -26,7 +26,7 @@ While official downloads of the Malcolm installer ISO are not provided, an **uno | ISO | SHA256 | |---|---| -| [hedgehog-23.04.0.iso](/iso/hedgehog-23.04.0.iso) (2.3GiB) | [`b0ef7afbd1fb8157b55115ca2a7ab118206b9498ab5a11c916f315c26775b0df`](/iso/hedgehog-23.04.0.iso.sha256.txt) | +| [hedgehog-23.04.1.iso](/iso/hedgehog-23.04.1.iso) (2.3GiB) | [`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`](/iso/hedgehog-23.04.1.iso.sha256.txt) | ## Warning diff --git a/docs/hedgehog-iso-build.md b/docs/hedgehog-iso-build.md index 884995b0c..746ea6e84 100644 --- a/docs/hedgehog-iso-build.md +++ b/docs/hedgehog-iso-build.md @@ -29,7 +29,7 @@ Building the ISO may take 90 minutes or more depending on your system. As the bu ``` … -Finished, created "/sensor-build/hedgehog-23.04.0.iso" +Finished, created "/sensor-build/hedgehog-23.04.1.iso" … ``` diff --git a/docs/malcolm-iso.md b/docs/malcolm-iso.md index 4c013dbbb..89bec95c3 100644 --- a/docs/malcolm-iso.md +++ b/docs/malcolm-iso.md @@ -41,7 +41,7 @@ Building the ISO may take 30 minutes or more depending on your system. As the bu ``` … -Finished, created "/malcolm-build/malcolm-iso/malcolm-23.04.0.iso" +Finished, created "/malcolm-build/malcolm-iso/malcolm-23.04.1.iso" … ``` diff --git a/docs/quickstart.md b/docs/quickstart.md index 55416e359..ec05eafd7 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -53,26 +53,26 @@ You can then observe that the images have been retrieved by running `docker imag ``` $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE -ghcr.io/idaholab/malcolm/api 23.04.0 xxxxxxxxxxxx 3 days ago 158MB -ghcr.io/idaholab/malcolm/arkime 23.04.0 xxxxxxxxxxxx 3 days ago 816MB -ghcr.io/idaholab/malcolm/dashboards 23.04.0 xxxxxxxxxxxx 3 days ago 1.02GB -ghcr.io/idaholab/malcolm/dashboards-helper 23.04.0 xxxxxxxxxxxx 3 days ago 184MB -ghcr.io/idaholab/malcolm/file-monitor 23.04.0 xxxxxxxxxxxx 3 days ago 588MB -ghcr.io/idaholab/malcolm/file-upload 23.04.0 xxxxxxxxxxxx 3 days ago 259MB -ghcr.io/idaholab/malcolm/filebeat-oss 23.04.0 xxxxxxxxxxxx 3 days ago 624MB -ghcr.io/idaholab/malcolm/freq 23.04.0 xxxxxxxxxxxx 3 days ago 132MB -ghcr.io/idaholab/malcolm/htadmin 23.04.0 xxxxxxxxxxxx 3 days ago 242MB -ghcr.io/idaholab/malcolm/logstash-oss 23.04.0 xxxxxxxxxxxx 3 days ago 1.35GB -ghcr.io/idaholab/malcolm/name-map-ui 23.04.0 xxxxxxxxxxxx 3 days ago 143MB -ghcr.io/idaholab/malcolm/netbox 23.04.0 xxxxxxxxxxxx 3 days ago 1.01GB -ghcr.io/idaholab/malcolm/nginx-proxy 23.04.0 xxxxxxxxxxxx 3 days ago 121MB -ghcr.io/idaholab/malcolm/opensearch 23.04.0 xxxxxxxxxxxx 3 days ago 1.17GB -ghcr.io/idaholab/malcolm/pcap-capture 23.04.0 xxxxxxxxxxxx 3 days ago 121MB -ghcr.io/idaholab/malcolm/pcap-monitor 23.04.0 xxxxxxxxxxxx 3 days ago 213MB -ghcr.io/idaholab/malcolm/postgresql 23.04.0 xxxxxxxxxxxx 3 days ago 268MB -ghcr.io/idaholab/malcolm/redis 23.04.0 xxxxxxxxxxxx 3 days ago 34.2MB -ghcr.io/idaholab/malcolm/suricata 23.04.0 xxxxxxxxxxxx 3 days ago 278MB -ghcr.io/idaholab/malcolm/zeek 23.04.0 xxxxxxxxxxxx 3 days ago 1GB +ghcr.io/idaholab/malcolm/api 23.04.1 xxxxxxxxxxxx 3 days ago 158MB +ghcr.io/idaholab/malcolm/arkime 23.04.1 xxxxxxxxxxxx 3 days ago 816MB +ghcr.io/idaholab/malcolm/dashboards 23.04.1 xxxxxxxxxxxx 3 days ago 1.02GB +ghcr.io/idaholab/malcolm/dashboards-helper 23.04.1 xxxxxxxxxxxx 3 days ago 184MB +ghcr.io/idaholab/malcolm/file-monitor 23.04.1 xxxxxxxxxxxx 3 days ago 588MB +ghcr.io/idaholab/malcolm/file-upload 23.04.1 xxxxxxxxxxxx 3 days ago 259MB +ghcr.io/idaholab/malcolm/filebeat-oss 23.04.1 xxxxxxxxxxxx 3 days ago 624MB +ghcr.io/idaholab/malcolm/freq 23.04.1 xxxxxxxxxxxx 3 days ago 132MB +ghcr.io/idaholab/malcolm/htadmin 23.04.1 xxxxxxxxxxxx 3 days ago 242MB +ghcr.io/idaholab/malcolm/logstash-oss 23.04.1 xxxxxxxxxxxx 3 days ago 1.35GB +ghcr.io/idaholab/malcolm/name-map-ui 23.04.1 xxxxxxxxxxxx 3 days ago 143MB +ghcr.io/idaholab/malcolm/netbox 23.04.1 xxxxxxxxxxxx 3 days ago 1.01GB +ghcr.io/idaholab/malcolm/nginx-proxy 23.04.1 xxxxxxxxxxxx 3 days ago 121MB +ghcr.io/idaholab/malcolm/opensearch 23.04.1 xxxxxxxxxxxx 3 days ago 1.17GB +ghcr.io/idaholab/malcolm/pcap-capture 23.04.1 xxxxxxxxxxxx 3 days ago 121MB +ghcr.io/idaholab/malcolm/pcap-monitor 23.04.1 xxxxxxxxxxxx 3 days ago 213MB +ghcr.io/idaholab/malcolm/postgresql 23.04.1 xxxxxxxxxxxx 3 days ago 268MB +ghcr.io/idaholab/malcolm/redis 23.04.1 xxxxxxxxxxxx 3 days ago 34.2MB +ghcr.io/idaholab/malcolm/suricata 23.04.1 xxxxxxxxxxxx 3 days ago 278MB +ghcr.io/idaholab/malcolm/zeek 23.04.1 xxxxxxxxxxxx 3 days ago 1GB ``` ### Import from pre-packaged tarballs diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index 051aacf61..90b083060 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -263,26 +263,26 @@ Pulling zeek ... done user@host:~/Malcolm$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE -ghcr.io/idaholab/malcolm/api 23.04.0 xxxxxxxxxxxx 3 days ago 158MB -ghcr.io/idaholab/malcolm/arkime 23.04.0 xxxxxxxxxxxx 3 days ago 816MB -ghcr.io/idaholab/malcolm/dashboards 23.04.0 xxxxxxxxxxxx 3 days ago 1.02GB -ghcr.io/idaholab/malcolm/dashboards-helper 23.04.0 xxxxxxxxxxxx 3 days ago 184MB -ghcr.io/idaholab/malcolm/file-monitor 23.04.0 xxxxxxxxxxxx 3 days ago 588MB -ghcr.io/idaholab/malcolm/file-upload 23.04.0 xxxxxxxxxxxx 3 days ago 259MB -ghcr.io/idaholab/malcolm/filebeat-oss 23.04.0 xxxxxxxxxxxx 3 days ago 624MB -ghcr.io/idaholab/malcolm/freq 23.04.0 xxxxxxxxxxxx 3 days ago 132MB -ghcr.io/idaholab/malcolm/htadmin 23.04.0 xxxxxxxxxxxx 3 days ago 242MB -ghcr.io/idaholab/malcolm/logstash-oss 23.04.0 xxxxxxxxxxxx 3 days ago 1.35GB -ghcr.io/idaholab/malcolm/name-map-ui 23.04.0 xxxxxxxxxxxx 3 days ago 143MB -ghcr.io/idaholab/malcolm/netbox 23.04.0 xxxxxxxxxxxx 3 days ago 1.01GB -ghcr.io/idaholab/malcolm/nginx-proxy 23.04.0 xxxxxxxxxxxx 3 days ago 121MB -ghcr.io/idaholab/malcolm/opensearch 23.04.0 xxxxxxxxxxxx 3 days ago 1.17GB -ghcr.io/idaholab/malcolm/pcap-capture 23.04.0 xxxxxxxxxxxx 3 days ago 121MB -ghcr.io/idaholab/malcolm/pcap-monitor 23.04.0 xxxxxxxxxxxx 3 days ago 213MB -ghcr.io/idaholab/malcolm/postgresql 23.04.0 xxxxxxxxxxxx 3 days ago 268MB -ghcr.io/idaholab/malcolm/redis 23.04.0 xxxxxxxxxxxx 3 days ago 34.2MB -ghcr.io/idaholab/malcolm/suricata 23.04.0 xxxxxxxxxxxx 3 days ago 278MB -ghcr.io/idaholab/malcolm/zeek 23.04.0 xxxxxxxxxxxx 3 days ago 1GB +ghcr.io/idaholab/malcolm/api 23.04.1 xxxxxxxxxxxx 3 days ago 158MB +ghcr.io/idaholab/malcolm/arkime 23.04.1 xxxxxxxxxxxx 3 days ago 816MB +ghcr.io/idaholab/malcolm/dashboards 23.04.1 xxxxxxxxxxxx 3 days ago 1.02GB +ghcr.io/idaholab/malcolm/dashboards-helper 23.04.1 xxxxxxxxxxxx 3 days ago 184MB +ghcr.io/idaholab/malcolm/file-monitor 23.04.1 xxxxxxxxxxxx 3 days ago 588MB +ghcr.io/idaholab/malcolm/file-upload 23.04.1 xxxxxxxxxxxx 3 days ago 259MB +ghcr.io/idaholab/malcolm/filebeat-oss 23.04.1 xxxxxxxxxxxx 3 days ago 624MB +ghcr.io/idaholab/malcolm/freq 23.04.1 xxxxxxxxxxxx 3 days ago 132MB +ghcr.io/idaholab/malcolm/htadmin 23.04.1 xxxxxxxxxxxx 3 days ago 242MB +ghcr.io/idaholab/malcolm/logstash-oss 23.04.1 xxxxxxxxxxxx 3 days ago 1.35GB +ghcr.io/idaholab/malcolm/name-map-ui 23.04.1 xxxxxxxxxxxx 3 days ago 143MB +ghcr.io/idaholab/malcolm/netbox 23.04.1 xxxxxxxxxxxx 3 days ago 1.01GB +ghcr.io/idaholab/malcolm/nginx-proxy 23.04.1 xxxxxxxxxxxx 3 days ago 121MB +ghcr.io/idaholab/malcolm/opensearch 23.04.1 xxxxxxxxxxxx 3 days ago 1.17GB +ghcr.io/idaholab/malcolm/pcap-capture 23.04.1 xxxxxxxxxxxx 3 days ago 121MB +ghcr.io/idaholab/malcolm/pcap-monitor 23.04.1 xxxxxxxxxxxx 3 days ago 213MB +ghcr.io/idaholab/malcolm/postgresql 23.04.1 xxxxxxxxxxxx 3 days ago 268MB +ghcr.io/idaholab/malcolm/redis 23.04.1 xxxxxxxxxxxx 3 days ago 34.2MB +ghcr.io/idaholab/malcolm/suricata 23.04.1 xxxxxxxxxxxx 3 days ago 278MB +ghcr.io/idaholab/malcolm/zeek 23.04.1 xxxxxxxxxxxx 3 days ago 1GB ``` Finally, we can start Malcolm. When Malcolm starts it will stream informational and debug messages to the console. If you wish, you can safely close the console or use `Ctrl+C` to stop these messages; Malcolm will continue running in the background. From c739914fbf6efcd624d733fc9ae153ed75bf2932 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 5 Apr 2023 09:44:05 -0600 Subject: [PATCH 093/239] bump beats to v8.7.0 (https://www.elastic.co/guide/en/beats/libbeat/current/release-notes-8.7.0.html) --- Dockerfiles/filebeat.Dockerfile | 2 +- Dockerfiles/logstash.Dockerfile | 2 +- sensor-iso/beats/Dockerfile | 2 +- sensor-iso/beats/beat-build.sh | 2 +- sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index 3090e047e..c68695a0e 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -1,4 +1,4 @@ -FROM docker.elastic.co/beats/filebeat-oss:8.6.2 +FROM docker.elastic.co/beats/filebeat-oss:8.7.0 # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. LABEL maintainer="malcolm@inl.gov" diff --git a/Dockerfiles/logstash.Dockerfile b/Dockerfiles/logstash.Dockerfile index 0d3fc882e..1539d35cb 100644 --- a/Dockerfiles/logstash.Dockerfile +++ b/Dockerfiles/logstash.Dockerfile @@ -1,4 +1,4 @@ -FROM opensearchproject/logstash-oss-with-opensearch-output-plugin:8.6.1 +FROM opensearchproject/logstash-oss-with-opensearch-output-plugin:8.7.0 LABEL maintainer="malcolm@inl.gov" LABEL org.opencontainers.image.authors='malcolm@inl.gov' diff --git a/sensor-iso/beats/Dockerfile b/sensor-iso/beats/Dockerfile index 5126c1bb3..eb89a777d 100644 --- a/sensor-iso/beats/Dockerfile +++ b/sensor-iso/beats/Dockerfile @@ -41,7 +41,7 @@ RUN set -x && \ go run bootstrap.go ENV BEATS=filebeat -ENV BEATS_VERSION=8.6.2 +ENV BEATS_VERSION=8.7.0 ADD ./build.sh /build.sh RUN [ "chmod", "+x", "/build.sh" ] diff --git a/sensor-iso/beats/beat-build.sh b/sensor-iso/beats/beat-build.sh index 63ada694c..e2282a8b5 100755 --- a/sensor-iso/beats/beat-build.sh +++ b/sensor-iso/beats/beat-build.sh @@ -2,7 +2,7 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. -VERSION="8.6.0" +VERSION="8.7.0" THIRD_PARTY_BRANCH="master" while getopts b:v:t: opts; do case ${opts} in diff --git a/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot b/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot index 2c2e182d4..a43cab53c 100755 --- a/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot +++ b/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot @@ -20,7 +20,7 @@ export PATH="${ZEEK_DIR}"/bin:$PATH SURICATA_RULES_DIR="/etc/suricata/rules" -BEATS_VER="8.6.2" +BEATS_VER="8.7.0" BEATS_OSS="-oss" BEATS_DEB_URL_TEMPLATE_REPLACER="XXXXX" BEATS_DEB_URL_TEMPLATE="https://artifacts.elastic.co/downloads/beats/$BEATS_DEB_URL_TEMPLATE_REPLACER/$BEATS_DEB_URL_TEMPLATE_REPLACER$BEATS_OSS-$BEATS_VER-amd64.deb" From 3f1a44dd9665acc23ee86a2e076c74dc36f38041 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 5 Apr 2023 09:57:17 -0600 Subject: [PATCH 094/239] fix logstash version --- Dockerfiles/logstash.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/logstash.Dockerfile b/Dockerfiles/logstash.Dockerfile index 1539d35cb..0d3fc882e 100644 --- a/Dockerfiles/logstash.Dockerfile +++ b/Dockerfiles/logstash.Dockerfile @@ -1,4 +1,4 @@ -FROM opensearchproject/logstash-oss-with-opensearch-output-plugin:8.7.0 +FROM opensearchproject/logstash-oss-with-opensearch-output-plugin:8.6.1 LABEL maintainer="malcolm@inl.gov" LABEL org.opencontainers.image.authors='malcolm@inl.gov' From 19a30d4b3a6f38017d23ff2a1c1af0d02dd47b66 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Wed, 5 Apr 2023 11:28:15 -0600 Subject: [PATCH 095/239] Remove extra open port --- nginx/nginx.conf | 92 ++++++++++++++++-------------------------------- 1 file changed, 31 insertions(+), 61 deletions(-) diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 42b619eb3..6bc2b4af8 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -80,34 +80,39 @@ http { } } - # htadmin (htpasswd/user management) - server { - listen 488; - include /etc/nginx/nginx_ssl_config.conf; - - location / { - proxy_pass http://htadmin; - proxy_redirect off; - proxy_set_header Host htadmin.malcolm.local; - } - } + # # htadmin (htpasswd/user management) + # server { + # listen 488; + # include /etc/nginx/nginx_ssl_config.conf; + + # location / { + # proxy_pass http://htadmin; + # proxy_redirect off; + # proxy_set_header Host htadmin.malcolm.local; + # } + # } # Main web interface server { listen 443; include /etc/nginx/nginx_ssl_config.conf; - # use either auth_basic or auth_ldap - include /etc/nginx/nginx_auth_rt.conf; - # Malcolm readme location /readme { + include /etc/nginx/nginx_auth_rt.conf; root /usr/share/nginx/html; try_files $uri $uri/index.html; } + location /htadmin(.*)$ { + proxy_pass http://htadmin($1); + proxy_redirect off; + proxy_set_header Host htadmin.malcolm.local; + } + # Malcolm file upload location /upload { + include /etc/nginx/nginx_auth_rt.conf; proxy_http_version 1.1; proxy_set_header Connection ""; rewrite ^/upload(.*)/?$ /$1 break; @@ -119,6 +124,7 @@ http { client_max_body_size 50G; } location /server/php { + include /etc/nginx/nginx_auth_rt.conf; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_pass http://upload/server/php; @@ -131,14 +137,17 @@ http { # Logstash statistics location ~* ^/logstash\b(.*) { + include /etc/nginx/nginx_auth_rt.conf; proxy_pass http://logstash-stats/_node/stats$1; proxy_redirect off; proxy_set_header Host arkime.malcolm.local; } + + # Arkime -> Dashboards shortcut location ~* ^/idark2dash(.*) { - + include /etc/nginx/nginx_auth_rt.conf; set $filter_start_time now-1d; if ($arg_start != '') { set $filter_start_time \'$arg_start\'; @@ -186,6 +195,7 @@ http { # already prepended /dashboards to match the server.basePath in OpenSearch Dashboards's YML config file location /dashboards { + include /etc/nginx/nginx_auth_rt.conf; proxy_pass http://dashboards; proxy_redirect off; proxy_set_header Host dashboards.malcolm.local; @@ -193,12 +203,14 @@ http { # offline region maps for dashboards location /world.geojson { + include /etc/nginx/nginx_auth_rt.conf; proxy_pass http://dashboards-maps; proxy_redirect off; proxy_set_header Host dashboards-helper.malcolm.local; } location ~* ^/extracted-files\b(.*) { + include /etc/nginx/nginx_auth_rt.conf; proxy_pass http://extracted-file-http-server$1; proxy_redirect off; proxy_set_header Host file-monitor.malcolm.local; @@ -206,6 +218,7 @@ http { # netbox location /netbox { + include /etc/nginx/nginx_auth_rt.conf; proxy_pass http://netbox; proxy_redirect off; proxy_set_header Host netbox.malcolm.local; @@ -221,6 +234,7 @@ http { # Fix cyberchef JS module(s) # https://localhost/arkime/session/190924-KgO9H30qhdREw7ltsDXn1Rgp/modules/Regex.js location ~* ^/arkime/session/.*/(modules/.*\.js) { + include /etc/nginx/nginx_auth_rt.conf; proxy_hide_header Content-Type; proxy_set_header Content-Type "application/javascript"; add_header Content-Type "application/javascript"; @@ -236,6 +250,7 @@ http { # Malcolm API location /mapi { + include /etc/nginx/nginx_auth_rt.conf; proxy_pass http://api; proxy_redirect off; proxy_set_header Host api.malcolm.local; @@ -243,6 +258,7 @@ http { # Arkime location / { + include /etc/nginx/nginx_auth_rt.conf; proxy_pass https://arkime; proxy_ssl_verify off; proxy_redirect off; @@ -252,52 +268,6 @@ http { } } - # OpenSearch dashboards interface - server { - listen 5601; - include /etc/nginx/nginx_ssl_config.conf; - - # use either auth_basic or auth_ldap - include /etc/nginx/nginx_auth_rt.conf; - - # favicon, logos, banners, etc. - include /etc/nginx/nginx_image_aliases.conf; - - # Dashboards -> Arkime shortcut - location ~* /iddash2ark/(.*) { - rewrite ^.*/iddash2ark/(.*) /sessions?expression=($1) redirect; - proxy_pass https://arkime; - proxy_ssl_verify off; - proxy_redirect off; - proxy_set_header Host arkime.malcolm.local; - proxy_set_header http_auth_http_user $authenticated_user; - proxy_set_header Authorization ""; - } - - # Dashboards -> extracted file download - location ~* /dl-extracted-files/(.*) { - rewrite ^.*/dl-extracted-files/(.*) /extracted-files/$1 redirect; - proxy_pass http://extracted-file-http-server; - proxy_redirect off; - proxy_set_header Host file-monitor.malcolm.local; - } - - # already prepended /dashboards to match the server.basePath in OpenSearch Dashboards' YML config file - location /dashboards { - proxy_pass http://dashboards; - proxy_redirect off; - proxy_set_header Host dashboards.malcolm.local; - } - - # otherwise prepend /dashboards to match the server.basePath in OpenSearch Dashboards' YML config file - location / { - rewrite ^/(.*) /dashboards/$1; - proxy_pass http://dashboards; - proxy_redirect off; - proxy_set_header Host dashboards.malcolm.local; - } - } - # OpenSearch API server { listen 9200; From da8fd216df1eed94fe5e4262e7e02e2449ebc183 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 5 Apr 2023 11:31:02 -0600 Subject: [PATCH 096/239] fix typo in py --- filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py index 3809a0ee4..a56713f8b 100755 --- a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py +++ b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py @@ -63,7 +63,7 @@ def file_processor(pathname, **kwargs): if os.path.isfile(pathname) and os.path.isdir(destination): time.sleep(0.1) - try + try: os.chown(pathname, uid, gid) # get the file magic mime type From f645d3bca67827379be96be33cce1088a888b075 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 5 Apr 2023 12:18:24 -0600 Subject: [PATCH 097/239] don't try to copy a file that doesn't exist --- nginx/scripts/docker_entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nginx/scripts/docker_entrypoint.sh b/nginx/scripts/docker_entrypoint.sh index ceb2cf29e..2f63f3aa8 100755 --- a/nginx/scripts/docker_entrypoint.sh +++ b/nginx/scripts/docker_entrypoint.sh @@ -92,7 +92,7 @@ if (( ${#CA_FILES} )) ; then # variables for nginx config NGINX_LDAP_CA_PATH_LINE=" ssl_ca_dir $CA_TRUST_RUN_DIR;" - ( [[ -n $NGINX_LDAP_TLS_STUNNEL_CHECK_HOST ]] || [[ -n $NGINX_LDAP_TLS_STUNNEL_CHECK_IP ]] ) && NGINX_LDAP_CHECK_REMOTE_CERT_LINE=" ssl_check_cert on;" || NGINX_LDAP_CHECK_REMOTE_CERT_LINE=" ssl_check_cert chain;" + ( [[ -n $NGINX_LDAP_TLS_STUNNEL_CHECK_HOST ]] || [[ -n $NGINX_LDAP_TLS_STUNNEL_CHECK_IP ]] ) && NGINX_LDAP_CHECK_REMOTE_CERT_LINE=" ssl_check_cert on;" || NGINX_LDAP_CHECK_REMOTE_CERT_LINE=" ssl_check_cert off;" fi popd >/dev/null 2>&1 fi @@ -237,7 +237,7 @@ EOF fi # basic vs. ldap -if [[ ! -f /etc/nginx/auth/htpasswd ]]; then +if [[ ! -f /etc/nginx/auth/htpasswd ]] && [[ -f /tmp/auth/default/htpasswd ]]; then cp /tmp/auth/default/htpasswd /etc/nginx/auth/ rm -rf /tmp/auth/* fi From 54d50bf9689b30cfba30c27d899294c0fe201c26 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Wed, 5 Apr 2023 12:43:48 -0600 Subject: [PATCH 098/239] Additional edits for htadmin idaholab/Malcolm#169 --- nginx/nginx.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 6bc2b4af8..9d1c3a35e 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -104,8 +104,8 @@ http { try_files $uri $uri/index.html; } - location /htadmin(.*)$ { - proxy_pass http://htadmin($1); + location /htadmin { + proxy_pass http://htadmin; proxy_redirect off; proxy_set_header Host htadmin.malcolm.local; } From 7d12b82cce9ebb09492b8c8a870e2c370e6258c5 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 5 Apr 2023 15:01:29 -0600 Subject: [PATCH 099/239] work in progress for moving inotify to watchdog, see idaholab/Malcolm#168 --- .../filebeat-watch-zeeklogs-uploads-folder.py | 25 +++++++++++-------- filebeat/supervisord.conf | 2 +- scripts/malcolm_utils.py | 9 +++++++ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py index a56713f8b..39d863c4a 100755 --- a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py +++ b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py @@ -15,12 +15,13 @@ import magic import os import pathlib +import shutil import signal import sys import time import malcolm_utils -from malcolm_utils import eprint, str2bool, touch +from malcolm_utils import eprint, str2bool, remove_suffix import watch_common ################################################################################################### @@ -71,12 +72,12 @@ def file_processor(pathname, **kwargs): if fileMime in mime_types: # looks like this is a compressed file, we're assuming it's a zeek log archive to be processed by filebeat - logger.debug(f"{scriptName}:\t📩\t{pathname} ({fileMime})") + logger.debug(f"{scriptName}:\t🖅\t{pathname} ({fileMime}) to {destination}") shutil.move(pathname, destination) else: # unhandled file type uploaded, delete it - logger.info(f"{scriptName}:\t✋\t{pathname} ({fileMime})") + logger.info(f"{scriptName}:\t🗑\t{pathname} ({fileMime})") os.unlink(pathname) except Exception as genericError: @@ -93,6 +94,7 @@ def main(): add_help=False, usage='{} '.format(scriptName), ) + parser.add_argument('--verbose', '-v', action='count', default=1, help='Increase verbosity (e.g., -v, -vv, etc.)') parser.add_argument( '-r', '--recursive-directory', @@ -126,27 +128,27 @@ def main(): ), required=False, ) - requiredNamed.add_argument( + parser.add_argument( '-i', '--in', dest='srcDir', help='Source directory to monitor', metavar='', type=str, - default=os.path.join(os.getenv('FILEBEAT_ZEEK_DIR', '/zeek'), 'upload'), + default=os.path.join(remove_suffix(os.getenv('FILEBEAT_ZEEK_DIR', '/zeek'), '/'), 'upload'), required=False, ) - requiredNamed.add_argument( + parser.add_argument( '-o', '--out', dest='dstDir', help='Destination directory', metavar='', type=str, - default=os.getenv('FILEBEAT_ZEEK_DIR', '/zeek'), + default=remove_suffix(os.getenv('FILEBEAT_ZEEK_DIR', '/zeek'), '/'), required=False, ) - requiredNamed.add_argument( + parser.add_argument( '-u', '--uid', dest='chownUid', @@ -156,7 +158,7 @@ def main(): default=int(os.getenv('PUID', os.getenv('DEFAULT_UID', '1000'))), required=False, ) - requiredNamed.add_argument( + parser.add_argument( '-g', '--gid', dest='chownGid', @@ -196,8 +198,6 @@ def main(): # handle sigint and sigterm for graceful shutdown signal.signal(signal.SIGINT, shutdown_handler) signal.signal(signal.SIGTERM, shutdown_handler) - signal.signal(signal.SIGUSR1, pdb_handler) - signal.signal(signal.SIGUSR2, debug_toggle_handler) # sleep for a bit if requested sleepCount = 0 @@ -205,6 +205,9 @@ def main(): time.sleep(1) sleepCount += 1 + args.dstDir = remove_suffix(args.dstDir, '/') + args.srcDir = remove_suffix(args.srcDir, '/') + # if directory to monitor doesn't exist, create it now if not os.path.isdir(args.srcDir): if debug: diff --git a/filebeat/supervisord.conf b/filebeat/supervisord.conf index f0b2ab382..78faf96e1 100644 --- a/filebeat/supervisord.conf +++ b/filebeat/supervisord.conf @@ -69,7 +69,7 @@ command=python3 /usr/local/bin/filebeat-watch-zeeklogs-uploads-folder.py --start-sleep 30 --polling "%(ENV_FILEBEAT_WATCHER_POLLING)s" --closed-sec %(ENV_FILEBEAT_WATCHER_POLLING_ASSUME_CLOSED_SEC)s - --in "%(ENV_FILEBEAT_ZEEK_DIR)s"/upload + --in "%(ENV_FILEBEAT_ZEEK_DIR)s"upload --out "%(ENV_FILEBEAT_ZEEK_DIR)s" --uid %(ENV_PUID)s --gid %(ENV_PGID)s diff --git a/scripts/malcolm_utils.py b/scripts/malcolm_utils.py index e0d8acf09..860ba873e 100644 --- a/scripts/malcolm_utils.py +++ b/scripts/malcolm_utils.py @@ -382,6 +382,15 @@ def remove_prefix(text, prefix): return text +################################################################################################### +# strip a suffix from the end of a string if needed +def remove_suffix(text, suffix): + if (len(suffix) > 0) and text.endswith(suffix): + return text[: len(text) - len(suffix)] + else: + return text + + ################################################################################################### # return true if os.path.samefile, also False on exception def same_file_or_dir(path1, path2): From b5713586a0f90e8ad9aeaea9cad96d03600803b4 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 5 Apr 2023 15:28:55 -0600 Subject: [PATCH 100/239] work in progress for moving inotify to watchdog, see idaholab/Malcolm#168 --- filebeat/supervisord.conf | 2 +- .../scripts/watch-pcap-uploads-folder.py | 261 ++++++++++++++++++ .../scripts/watch-pcap-uploads-folder.sh | 32 --- pcap-monitor/supervisord.conf | 10 +- 4 files changed, 271 insertions(+), 34 deletions(-) create mode 100755 pcap-monitor/scripts/watch-pcap-uploads-folder.py delete mode 100755 pcap-monitor/scripts/watch-pcap-uploads-folder.sh diff --git a/filebeat/supervisord.conf b/filebeat/supervisord.conf index 78faf96e1..f0b2ab382 100644 --- a/filebeat/supervisord.conf +++ b/filebeat/supervisord.conf @@ -69,7 +69,7 @@ command=python3 /usr/local/bin/filebeat-watch-zeeklogs-uploads-folder.py --start-sleep 30 --polling "%(ENV_FILEBEAT_WATCHER_POLLING)s" --closed-sec %(ENV_FILEBEAT_WATCHER_POLLING_ASSUME_CLOSED_SEC)s - --in "%(ENV_FILEBEAT_ZEEK_DIR)s"upload + --in "%(ENV_FILEBEAT_ZEEK_DIR)s"/upload --out "%(ENV_FILEBEAT_ZEEK_DIR)s" --uid %(ENV_PUID)s --gid %(ENV_PGID)s diff --git a/pcap-monitor/scripts/watch-pcap-uploads-folder.py b/pcap-monitor/scripts/watch-pcap-uploads-folder.py new file mode 100755 index 000000000..a69ee8792 --- /dev/null +++ b/pcap-monitor/scripts/watch-pcap-uploads-folder.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. + +################################################################################################### +# Monitor a directory for PCAP files for processing (by publishing their filenames to a ZMQ socket) +# +# Run the script with --help for options +################################################################################################### + +import argparse +import glob +import logging +import magic +import os +import pathlib +import shutil +import signal +import sys +import time + +import malcolm_utils +from malcolm_utils import eprint, str2bool, remove_suffix +import watch_common + +################################################################################################### +scriptName = os.path.basename(__file__) +scriptPath = os.path.dirname(os.path.realpath(__file__)) +origPath = os.getcwd() +shuttingDown = [False] + + +################################################################################################### +# handle sigint/sigterm and set a global shutdown variable +def shutdown_handler(signum, frame): + global shuttingDown + shuttingDown[0] = True + + +################################################################################################### +def file_processor(pathname, **kwargs): + uid = kwargs["uid"] + gid = kwargs["gid"] + pcapDir = kwargs["destination"] + zeekDir = kwargs["zeek"] + logger = kwargs["logger"] + + logger.debug(f"{scriptName}:\t👓\t{pathname}") + + if os.path.isfile(pathname): + time.sleep(0.1) + try: + os.chown(pathname, uid, gid) + + # get the file magic mime type + fileMime = magic.from_file(pathname, mime=True) + fileType = magic.from_file(pathname) + + if os.path.isdir(pcapDir) and ( + (fileMime in ('application/vnd.tcpdump.pcap', 'application/x-pcapng')) or ('pcap-ng' in fileType) + ): + # a pcap file to be processed by dropping it into pcapDir + logger.debug(f"{scriptName}:\t🖅\t{pathname} ({fileMime}/{fileType}) to {pcapDir}") + shutil.move(pathname, pcapDir) + + elif os.path.isdir(zeekDir) and ( + fileMime + in [ + 'application/gzip', + 'application/x-gzip', + 'application/x-7z-compressed', + 'application/x-bzip2', + 'application/x-cpio', + 'application/x-lzip', + 'application/x-lzma', + 'application/x-rar-compressed', + 'application/x-tar', + 'application/x-xz', + 'application/zip', + ] + ): + # looks like this is a compressed file, we're assuming it's a zeek log archive to be processed by filebeat + logger.debug(f"{scriptName}:\t🖅\t{pathname} ({fileMime}/{fileType}) to {zeekDir}") + shutil.move(pathname, zeekDir) + + else: + # unhandled file type uploaded, delete it + logger.info(f"{scriptName}:\t🗑\t{pathname} ({fileMime}/{fileType})") + os.unlink(pathname) + + except Exception as genericError: + logger.warning(f"{scriptName}:\texception: {genericError}") + + +################################################################################################### +# main +def main(): + global shuttingDown + + parser = argparse.ArgumentParser( + description=scriptName, + add_help=False, + usage='{} '.format(scriptName), + ) + parser.add_argument('--verbose', '-v', action='count', default=1, help='Increase verbosity (e.g., -v, -vv, etc.)') + parser.add_argument( + '-r', + '--recursive-directory', + dest='recursiveDir', + help="If specified, monitor all directories with this name underneath --directory", + metavar='', + type=str, + required=False, + ) + parser.add_argument( + '-p', + '--polling', + dest='polling', + help="Use polling (instead of inotify)", + metavar='true|false', + type=str2bool, + nargs='?', + const=True, + default=os.getenv('PCAP_PIPELINE_POLLING', False), + required=False, + ) + parser.add_argument( + '-c', + '--closed-sec', + dest='assumeClosedSec', + help="When polling, assume a file is closed after this many seconds of inactivity", + metavar='', + type=int, + default=int(os.getenv('PCAP_PIPELINE_POLLING_ASSUME_CLOSED_SEC', str(watch_common.ASSUME_CLOSED_SEC_DEFAULT))), + required=False, + ) + parser.add_argument( + '-i', + '--in', + dest='srcDir', + help='Source directory to monitor', + metavar='', + type=str, + default=os.path.join(remove_suffix(os.getenv('PCAP_PATH', '/pcap'), '/'), 'upload'), + required=False, + ) + parser.add_argument( + '-o', + '--out', + dest='dstDir', + help='Destination directory', + metavar='', + type=str, + default=os.path.join(remove_suffix(os.getenv('PCAP_PATH', '/pcap'), '/'), 'processed'), + required=False, + ) + parser.add_argument( + '-z', + '--zeek', + dest='zeekDir', + help='Zeek upload directory', + metavar='', + type=str, + default=os.path.join(remove_suffix(os.getenv('ZEEK_PATH', '/zeek'), '/'), 'upload'), + required=False, + ) + parser.add_argument( + '-u', + '--uid', + dest='chownUid', + help='UID to chown files', + metavar='', + type=int, + default=int(os.getenv('PUID', os.getenv('DEFAULT_UID', '1000'))), + required=False, + ) + parser.add_argument( + '-g', + '--gid', + dest='chownGid', + help='UID to chown files', + metavar='', + type=int, + default=int(os.getenv('PGID', os.getenv('DEFAULT_GID', '1000'))), + required=False, + ) + parser.add_argument( + '--start-sleep', + dest='startSleepSec', + help="Sleep for this many seconds before starting", + metavar='', + type=int, + default=0, + required=False, + ) + + try: + parser.error = parser.exit + args = parser.parse_args() + except SystemExit: + parser.print_help() + exit(2) + + args.verbose = logging.ERROR - (10 * args.verbose) if args.verbose > 0 else 0 + logging.basicConfig( + level=args.verbose, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' + ) + logging.debug(os.path.join(scriptPath, scriptName)) + logging.debug("Arguments: {}".format(sys.argv[1:])) + logging.debug("Arguments: {}".format(args)) + if args.verbose > logging.DEBUG: + sys.tracebacklimit = 0 + + # handle sigint and sigterm for graceful shutdown + signal.signal(signal.SIGINT, shutdown_handler) + signal.signal(signal.SIGTERM, shutdown_handler) + + # sleep for a bit if requested + sleepCount = 0 + while (not shuttingDown[0]) and (sleepCount < args.startSleepSec): + time.sleep(1) + sleepCount += 1 + + args.dstDir = remove_suffix(args.dstDir, '/') + args.srcDir = remove_suffix(args.srcDir, '/') + args.zeekDir = remove_suffix(args.zeekDir, '/') + + # if directory to monitor doesn't exist, create it now + if not os.path.isdir(args.srcDir): + if debug: + eprint(f'{scriptName}:\tcreating "{args.srcDir}" to monitor') + pathlib.Path(args.srcDir).mkdir(parents=False, exist_ok=True) + + # if recursion was requested, get list of directories to monitor + watchDirs = [] + while len(watchDirs) == 0: + if args.recursiveDir is None: + watchDirs = [args.srcDir] + else: + watchDirs = glob.glob(f'{args.srcDir}/**/{args.recursiveDir}', recursive=True) + + watch_common.WatchAndProcessDirectory( + watchDirs, + args.polling, + file_processor, + { + "logger": logging, + "destination": args.dstDir, + "zeek": args.zeekDir, + "uid": args.chownUid, + "gid": args.chownGid, + }, + args.assumeClosedSec, + shuttingDown, + logging, + ) + + +if __name__ == '__main__': + main() diff --git a/pcap-monitor/scripts/watch-pcap-uploads-folder.sh b/pcap-monitor/scripts/watch-pcap-uploads-folder.sh deleted file mode 100755 index a72d088d8..000000000 --- a/pcap-monitor/scripts/watch-pcap-uploads-folder.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. - -PCAP_BASE_PATH=${PCAP_PATH:-"/pcap"} -ZEEK_BASE_PATH=${ZEEK_PATH:-"/zeek"} - -PROCESS_DIR="$PCAP_BASE_PATH/processed" -PCAP_UPLOAD_DIR="$PCAP_BASE_PATH/upload" -ZEEK_UPLOAD_DIR="$ZEEK_BASE_PATH/upload" -mkdir -p "$PCAP_UPLOAD_DIR" - -# as new pcaps are closed for writing in /pcap/upload, move them to /pcap/processed for processing -inotifywait -m -e close_write --format '%w%f' "${PCAP_UPLOAD_DIR}" | while read NEWFILE -do - FILEMAGIC=$(file -b "$NEWFILE") - FILEMIME=$(file -b --mime-type "$NEWFILE") - if [[ "$FILEMIME" == 'application/vnd.tcpdump.pcap' ]] || [[ "$FILEMIME" == 'application/x-pcapng' ]] || [[ "$FILEMAGIC" == *"pcap-ng"* ]]; then - # a pcap file to be processed by dropping it into $PROCESS_DIR - sleep 0.1 && chown ${PUID:-${DEFAULT_UID}}:${PGID:-${DEFAULT_GID}} "$NEWFILE" && (>&2 mv -v "$NEWFILE" "$PROCESS_DIR/") - - elif [[ -d "$ZEEK_UPLOAD_DIR" ]] && ( echo "$FILEMIME" | grep --quiet -P "(application/gzip|application/x-gzip|application/x-7z-compressed|application/x-bzip2|application/x-cpio|application/x-lzip|application/x-lzma|application/x-rar-compressed|application/x-tar|application/x-xz|application/zip)" ); then - # looks like this is a compressed file, we're assuming it's a zeek log archive to be processed by filebeat - sleep 0.1 && chown ${PUID:-${DEFAULT_UID}}:${PGID:-${DEFAULT_GID}} "$NEWFILE" && (>&2 mv -v "$NEWFILE" "$ZEEK_UPLOAD_DIR/") - - else - # unhandled file type uploaded, delete it - sleep 0.1 && (>&2 rm "$NEWFILE") - echo "Removed \"$NEWFILE\", unhandled file type \"$FILEMIME\"" - - fi -done diff --git a/pcap-monitor/supervisord.conf b/pcap-monitor/supervisord.conf index a4ad0d9f8..50c232c33 100644 --- a/pcap-monitor/supervisord.conf +++ b/pcap-monitor/supervisord.conf @@ -18,7 +18,15 @@ supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface serverurl=unix:///tmp/supervisor.sock [program:watch-upload] -command=/bin/bash -c "sleep 30 && /usr/local/bin/watch-pcap-uploads-folder.sh" +command=python3 /usr/local/bin/watch-pcap-uploads-folder.py + --start-sleep 30 + --polling "%(ENV_PCAP_PIPELINE_POLLING)s" + --closed-sec %(ENV_PCAP_PIPELINE_POLLING_ASSUME_CLOSED_SEC)s + --in "%(ENV_PCAP_PATH)s"/upload + --out "%(ENV_PCAP_PATH)s"/processed + --zeek "%(ENV_ZEEK_PATH)s"/upload + --uid %(ENV_PUID)s + --gid %(ENV_PGID)s startsecs=35 startretries=1 stopasgroup=true From c6819d5b89cf7c91df9a8a26399f395663f0fad6 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 5 Apr 2023 15:40:40 -0600 Subject: [PATCH 101/239] debugging polling for files/pcap --- .../filebeat-watch-zeeklogs-uploads-folder.py | 14 +++++++------- .../scripts/watch-pcap-uploads-folder.py | 16 ++++++++-------- shared/bin/watch_common.py | 10 +++++----- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py index 39d863c4a..2756f6670 100755 --- a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py +++ b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py @@ -60,7 +60,7 @@ def file_processor(pathname, **kwargs): destination = kwargs["destination"] logger = kwargs["logger"] - logger.debug(f"{scriptName}:\t👓\t{pathname}") + logger.info(f"{scriptName}:\t👓\t{pathname}") if os.path.isfile(pathname) and os.path.isdir(destination): time.sleep(0.1) @@ -72,16 +72,16 @@ def file_processor(pathname, **kwargs): if fileMime in mime_types: # looks like this is a compressed file, we're assuming it's a zeek log archive to be processed by filebeat - logger.debug(f"{scriptName}:\t🖅\t{pathname} ({fileMime}) to {destination}") + logger.info(f"{scriptName}:\t🖅\t{pathname} ({fileMime}) to {destination}") shutil.move(pathname, destination) else: # unhandled file type uploaded, delete it - logger.info(f"{scriptName}:\t🗑\t{pathname} ({fileMime})") + logger.warning(f"{scriptName}:\t🗑\t{pathname} ({fileMime})") os.unlink(pathname) except Exception as genericError: - logger.warning(f"{scriptName}:\texception: {genericError}") + logger.error(f"{scriptName}:\texception: {genericError}") ################################################################################################### @@ -189,9 +189,9 @@ def main(): logging.basicConfig( level=args.verbose, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) - logging.debug(os.path.join(scriptPath, scriptName)) - logging.debug("Arguments: {}".format(sys.argv[1:])) - logging.debug("Arguments: {}".format(args)) + logging.info(os.path.join(scriptPath, scriptName)) + logging.info("Arguments: {}".format(sys.argv[1:])) + logging.info("Arguments: {}".format(args)) if args.verbose > logging.DEBUG: sys.tracebacklimit = 0 diff --git a/pcap-monitor/scripts/watch-pcap-uploads-folder.py b/pcap-monitor/scripts/watch-pcap-uploads-folder.py index a69ee8792..ad7d8f56c 100755 --- a/pcap-monitor/scripts/watch-pcap-uploads-folder.py +++ b/pcap-monitor/scripts/watch-pcap-uploads-folder.py @@ -46,7 +46,7 @@ def file_processor(pathname, **kwargs): zeekDir = kwargs["zeek"] logger = kwargs["logger"] - logger.debug(f"{scriptName}:\t👓\t{pathname}") + logger.info(f"{scriptName}:\t👓\t{pathname}") if os.path.isfile(pathname): time.sleep(0.1) @@ -61,7 +61,7 @@ def file_processor(pathname, **kwargs): (fileMime in ('application/vnd.tcpdump.pcap', 'application/x-pcapng')) or ('pcap-ng' in fileType) ): # a pcap file to be processed by dropping it into pcapDir - logger.debug(f"{scriptName}:\t🖅\t{pathname} ({fileMime}/{fileType}) to {pcapDir}") + logger.info(f"{scriptName}:\t🖅\t{pathname} ({fileMime}/{fileType}) to {pcapDir}") shutil.move(pathname, pcapDir) elif os.path.isdir(zeekDir) and ( @@ -81,16 +81,16 @@ def file_processor(pathname, **kwargs): ] ): # looks like this is a compressed file, we're assuming it's a zeek log archive to be processed by filebeat - logger.debug(f"{scriptName}:\t🖅\t{pathname} ({fileMime}/{fileType}) to {zeekDir}") + logger.info(f"{scriptName}:\t🖅\t{pathname} ({fileMime}/{fileType}) to {zeekDir}") shutil.move(pathname, zeekDir) else: # unhandled file type uploaded, delete it - logger.info(f"{scriptName}:\t🗑\t{pathname} ({fileMime}/{fileType})") + logger.warning(f"{scriptName}:\t🗑\t{pathname} ({fileMime}/{fileType})") os.unlink(pathname) except Exception as genericError: - logger.warning(f"{scriptName}:\texception: {genericError}") + logger.error(f"{scriptName}:\texception: {genericError}") ################################################################################################### @@ -206,9 +206,9 @@ def main(): logging.basicConfig( level=args.verbose, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) - logging.debug(os.path.join(scriptPath, scriptName)) - logging.debug("Arguments: {}".format(sys.argv[1:])) - logging.debug("Arguments: {}".format(args)) + logging.info(os.path.join(scriptPath, scriptName)) + logging.info("Arguments: {}".format(sys.argv[1:])) + logging.info("Arguments: {}".format(args)) if args.verbose > logging.DEBUG: sys.tracebacklimit = 0 diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py index 2518c7005..9ff1af62a 100644 --- a/shared/bin/watch_common.py +++ b/shared/bin/watch_common.py @@ -57,7 +57,7 @@ def on_any_event(self, event): if not event.is_directory: self.updateTime() if self.logger: - self.logger.debug(f"{self.nowTime}: {event.event_type} {event.src_path}") + self.logger.info(f"{self.nowTime}: {event.event_type} {event.src_path}") def on_created(self, event): self.on_modified(event) @@ -113,7 +113,7 @@ def ProcessFileEventWorker(workerArgs): with workerThreadCount as workerId: if logger is not None: - logger.debug(f"[{workerId}]:started") + logger.info(f"[{workerId}]:started") while (not shutDown[0]) and observer.is_alive(): time.sleep(1) @@ -136,13 +136,13 @@ def ProcessFileEventWorker(workerArgs): **extraArgs, ) if logger is not None: - logger.debug( + logger.info( f"processed {fileName} at {(nowTime-eventTime) if (eventTime > 0) else 0} seconds" ) time.sleep(1) if logger is not None: - logger.debug(f"[{workerId}]: finished") + logger.info(f"[{workerId}]: finished") def WatchAndProcessDirectory( @@ -161,7 +161,7 @@ def WatchAndProcessDirectory( ) for directory in directories: if logger: - logger.debug(f"Scheduling {directory}") + logger.info(f"Scheduling {directory}") observer.schedule(handler, directory, recursive=True) observer.start() From 7e2a4a9ae437930ec2e4b8963b0a8ae1d8bbccdb Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Wed, 5 Apr 2023 17:56:16 -0600 Subject: [PATCH 102/239] Return default file to previous version --- htadmin/nginx/sites-available/default | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/htadmin/nginx/sites-available/default b/htadmin/nginx/sites-available/default index 7a231f6d6..672a85577 100644 --- a/htadmin/nginx/sites-available/default +++ b/htadmin/nginx/sites-available/default @@ -3,13 +3,13 @@ server { sendfile on; - root /var/www/; + root /var/www/htadmin; index index.php index.html index.htm; server_name htaccess.malcolm.local; location / { - try_files $uri $uri/htadmin htadmin/$uri/n =404; + try_files $uri $uri/ =404; } location ~ \.php$ { @@ -18,7 +18,7 @@ server { fastcgi_pass unix:/run/php/php7.4-fpm.sock; } - location /htadmin/config { + location /config { deny all; return 404; } From 07e8b5a0f89dcff0e00142edff35e75e28c4f989 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Wed, 5 Apr 2023 18:15:31 -0600 Subject: [PATCH 103/239] Return to previous version for idaholab/Malcolm#1699 --- htadmin/nginx/sites-available/default | 1 - 1 file changed, 1 deletion(-) diff --git a/htadmin/nginx/sites-available/default b/htadmin/nginx/sites-available/default index 672a85577..5420189b6 100644 --- a/htadmin/nginx/sites-available/default +++ b/htadmin/nginx/sites-available/default @@ -22,4 +22,3 @@ server { deny all; return 404; } -} From 1939cc9eb0764052839eef9bdb6cdd3b87f64225 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Wed, 5 Apr 2023 18:15:58 -0600 Subject: [PATCH 104/239] Fix syntax error --- htadmin/nginx/sites-available/default | 1 + 1 file changed, 1 insertion(+) diff --git a/htadmin/nginx/sites-available/default b/htadmin/nginx/sites-available/default index 5420189b6..82cb823ee 100644 --- a/htadmin/nginx/sites-available/default +++ b/htadmin/nginx/sites-available/default @@ -22,3 +22,4 @@ server { deny all; return 404; } +} \ No newline at end of file From 393bc21f320ef627cb0177d8803d32f5741d19f9 Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Wed, 5 Apr 2023 18:35:48 -0600 Subject: [PATCH 105/239] Nginx conf adjustments --- nginx/nginx.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 9d1c3a35e..8c424ae09 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -105,6 +105,7 @@ http { } location /htadmin { + rewrite ^/htadmin(.*)/?$ /$1 break; proxy_pass http://htadmin; proxy_redirect off; proxy_set_header Host htadmin.malcolm.local; From c6d206d9f69546f9eb76ca99e5f510dfb8f5520c Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 6 Apr 2023 09:52:06 -0600 Subject: [PATCH 106/239] work in progress for idaholab/Malcolm#168 --- shared/bin/watch_common.py | 188 +++++++++++++++++++++++-------------- 1 file changed, 118 insertions(+), 70 deletions(-) diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py index 9ff1af62a..01570fe67 100644 --- a/shared/bin/watch_common.py +++ b/shared/bin/watch_common.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- import os +import json import time from malcolm_utils import AtomicInt, ContextLockedOrderedDict, same_file_or_dir @@ -26,9 +27,12 @@ from watchdog.utils import WatchdogShutdown from watchdog.observers import Observer from watchdog.observers.polling import PollingObserver +from collections import namedtuple, defaultdict ASSUME_CLOSED_SEC_DEFAULT = 10 +OperationEvent = namedtuple("OperationEvent", ["timestamp", "operation"], rename=False) + ################################################################################################### class FileOperationEventHandler(FileSystemEventHandler): @@ -41,11 +45,14 @@ def __init__( ): super().__init__(*args, **kwargs) self.polling = polling - # items at the first (idx=0) of this OrderedDict are the - # oldest, items at the last (idx=len-1) are the newest - self.deck = ContextLockedOrderedDict() self.logger = logger self.updateTime() + # self.deck is a dictionary mapping filenames to a list of OperationEvent of length n, + # with [0] being the oldest timestamp/operation and [n-1] being the newest + # timestamp/operation. + # In self.dec itself, items at the first (idx=0) of this OrderedDict are the + # oldest, items at the last (idx=len-1) are the newest. + self.deck = ContextLockedOrderedDict() def done(self): return True @@ -54,48 +61,79 @@ def updateTime(self): self.nowTime = int(time.time()) def on_any_event(self, event): - if not event.is_directory: - self.updateTime() - if self.logger: - self.logger.info(f"{self.nowTime}: {event.event_type} {event.src_path}") - - def on_created(self, event): - self.on_modified(event) - - def on_modified(self, event): - if not event.is_directory: - with self.deck as d: - d[event.src_path] = self.nowTime - d.move_to_end(event.src_path, last=True) - - def on_moved(self, event): - if not event.is_directory: - if isinstance(event, FileSystemMovedEvent) and same_file_or_dir( - os.path.dirname(event.src_path), os.path.dirname(event.dest_path) - ): - # a file was simply renamed in the watched directory (not moved - # from some other directory) so just update the filename + fName = None + try: + if not event.is_directory: + self.updateTime() + + # FileClosedEvent is only going to come from inotify events, not polling + # so we know we're good to go (a FileClosedEvent signals we can process the + # file immediately). We can signal this by setting the timestamp to 0. + newOpLog = OperationEvent( + self.nowTime if (not isinstance(event, FileClosedEvent)) else 0, event.event_type + ) + + # if this is a move event, we need to track the old and new filenames + if isinstance(event, FileSystemMovedEvent): + fName = event.dest_path + fNameOld = event.src_path + if self.logger: + self.logger.info(f"↦\t{event.event_type: >10}\t{event.src_path} {event.dest_path}") + else: + fName = event.src_path + fNameOld = None + if self.logger: + self.logger.info(f"🗲\t{event.event_type: <10}\t{event.src_path}") + with self.deck as d: - d.pop(event.src_path, self.nowTime) - d[event.dest_path] = self.nowTime - d.move_to_end(event.dest_path, last=True) - else: - # the file was moved from somewhere else, treat it as a create - self.on_created(event) - - def on_closed(self, event): - # on_closed is only going to come from inotify events, not polling - # so we know we're good to go. set its time to expire immediately in the worker - if not event.is_directory: - with self.deck as d: - d[event.src_path] = 0 - d.move_to_end(event.src_path, last=False) - - def on_deleted(self, event): - # if a file is deleted I guess we don't need to track it any more - if not event.is_directory: - with self.deck as d: - d.pop(event.src_path, self.nowTime) + if fNameOld: + if same_file_or_dir(os.path.dirname(fNameOld), os.path.dirname(fName)): + # a file was simply renamed in the watched directory (not moved + # from some other directory) so just update the filename + d.pop(fNameOld, None) + else: + # the file was moved from somewhere else, treat it like a create + pass + + # insert or update file event(s) + + if fName in d: + # this is a file we're already currently tracking + + # if the previous operation (the last one in the history) was the same as this one, + # replace the operation rather than appending a new one (effectively just updating the timestamp) + if (len(d[fName]) > 0) and (d[fName][-1].operation == event.event_type): + d[fName][-1] = newOpLog + + else: + # otherwise append a new history item + d[fName].append(newOpLog) + + else: + # this is a file we were not previously tracking + d[fName] = [newOpLog] + + if ( + isinstance(event, FileModifiedEvent) + or isinstance(event, FileClosedEvent) + or isinstance(event, FileCreatedEvent) + or isinstance(event, FileSystemMovedEvent) + ): + # put FileClosedEvent events (which now have a timestamp of 0) at the front of + # the deck (to be processed first), and others to the back + d.move_to_end(fName, last=d[fName][-1] > 0) + + elif isinstance(event, FileDeletedEvent): + # if a file is deleted I guess we don't need to track it any more + d.pop(fName, None) + fName = None + + if self.logger and fName: + self.logger.debug(f"⎗\t{fName}\t{json.dumps(d[fName])}") + + except Exception as e: + if self.logger: + self.logger.error(f"⨳\t{fName}\t{e}") ################################################################################################### @@ -112,37 +150,47 @@ def ProcessFileEventWorker(workerArgs): ) with workerThreadCount as workerId: - if logger is not None: - logger.info(f"[{workerId}]:started") + if logger: + logger.info(f"۞\tstarted\t[{workerId}]") while (not shutDown[0]) and observer.is_alive(): - time.sleep(1) + time.sleep(0.5) nowTime = int(time.time()) + with handler.deck as d: - for fileName, eventTime in list(d.items()): - if nowTime < eventTime + assumeClosedSec: - # we can break because the list is ordered - break - else: - del d[fileName] - if fileProcessor is not None: - extraArgs = ( - fileProcessorKwargs - if fileProcessorKwargs and isinstance(fileProcessorKwargs, dict) - else {} - ) - fileProcessor( - fileName, - **extraArgs, - ) - if logger is not None: - logger.info( - f"processed {fileName} at {(nowTime-eventTime) if (eventTime > 0) else 0} seconds" - ) + for fileName, fileHistory in list(d.items()): + if logger: + logger.debug(f"⏿ checking {fileName}\t{json.dumps(fileHistory)}\t[{workerId}]") + + if len(fileHistory) > 0: + if nowTime < fileHistory[-1].timestamp + assumeClosedSec: + # we can break because the list is ordered + if logger: + logger.debug( + f"⎊\tbreaking early because {nowTime} < {fileHistory[-1].timestamp + assumeClosedSec}\t[{workerId}]" + ) + break + + else: + del d[fileName] + if fileProcessor is not None: + extraArgs = ( + fileProcessorKwargs + if fileProcessorKwargs and isinstance(fileProcessorKwargs, dict) + else {} + ) + fileProcessor( + fileName, + **extraArgs, + ) + if logger: + logger.info( + f"🖄\tprocessed\t{fileName} at {(nowTime-fileHistory[-1].timestamp) if (fileHistory[-1].timestamp > 0) else 0} seconds\t[{workerId}]" + ) time.sleep(1) - if logger is not None: - logger.info(f"[{workerId}]: finished") + if logger: + logger.info(f"⛒\tfinished\t[{workerId}]") def WatchAndProcessDirectory( @@ -161,7 +209,7 @@ def WatchAndProcessDirectory( ) for directory in directories: if logger: - logger.info(f"Scheduling {directory}") + logger.info(f"🗐\tScheduling {directory}") observer.schedule(handler, directory, recursive=True) observer.start() From 9fe07cdf498c1765662c782f94a41876b0f07fd2 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 6 Apr 2023 10:19:30 -0600 Subject: [PATCH 107/239] work in progress for idaholab/Malcolm#168 --- filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py | 2 +- pcap-monitor/scripts/watch-pcap-uploads-folder.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py index 2756f6670..bba70e1e9 100755 --- a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py +++ b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py @@ -73,7 +73,7 @@ def file_processor(pathname, **kwargs): if fileMime in mime_types: # looks like this is a compressed file, we're assuming it's a zeek log archive to be processed by filebeat logger.info(f"{scriptName}:\t🖅\t{pathname} ({fileMime}) to {destination}") - shutil.move(pathname, destination) + shutil.move(pathname, os.path.join(destination, os.path.basename(pathname))) else: # unhandled file type uploaded, delete it diff --git a/pcap-monitor/scripts/watch-pcap-uploads-folder.py b/pcap-monitor/scripts/watch-pcap-uploads-folder.py index ad7d8f56c..5563a41f0 100755 --- a/pcap-monitor/scripts/watch-pcap-uploads-folder.py +++ b/pcap-monitor/scripts/watch-pcap-uploads-folder.py @@ -62,7 +62,7 @@ def file_processor(pathname, **kwargs): ): # a pcap file to be processed by dropping it into pcapDir logger.info(f"{scriptName}:\t🖅\t{pathname} ({fileMime}/{fileType}) to {pcapDir}") - shutil.move(pathname, pcapDir) + shutil.move(pathname, os.path.join(pcapDir, os.path.basename(pathname))) elif os.path.isdir(zeekDir) and ( fileMime @@ -82,7 +82,7 @@ def file_processor(pathname, **kwargs): ): # looks like this is a compressed file, we're assuming it's a zeek log archive to be processed by filebeat logger.info(f"{scriptName}:\t🖅\t{pathname} ({fileMime}/{fileType}) to {zeekDir}") - shutil.move(pathname, zeekDir) + shutil.move(pathname, os.path.join(zeekDir, os.path.basename(pathname))) else: # unhandled file type uploaded, delete it @@ -228,8 +228,7 @@ def main(): # if directory to monitor doesn't exist, create it now if not os.path.isdir(args.srcDir): - if debug: - eprint(f'{scriptName}:\tcreating "{args.srcDir}" to monitor') + logging.info(f'{scriptName}:\tcreating "{args.srcDir}" to monitor') pathlib.Path(args.srcDir).mkdir(parents=False, exist_ok=True) # if recursion was requested, get list of directories to monitor From be05fab2c4cc579fd4c8c9607fbb0f5850e22f62 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 6 Apr 2023 11:40:31 -0600 Subject: [PATCH 108/239] work in progress for idaholab/Malcolm#168 --- .../filebeat-watch-zeeklogs-uploads-folder.py | 9 +- .../scripts/watch-pcap-uploads-folder.py | 6 +- shared/bin/pcap_watcher.py | 137 ++++---------- shared/bin/watch_common.py | 174 ++++++++++-------- shared/bin/zeek_carve_watcher.py | 109 +++-------- 5 files changed, 173 insertions(+), 262 deletions(-) diff --git a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py index bba70e1e9..43978a662 100755 --- a/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py +++ b/filebeat/scripts/filebeat-watch-zeeklogs-uploads-folder.py @@ -58,7 +58,7 @@ def file_processor(pathname, **kwargs): uid = kwargs["uid"] gid = kwargs["gid"] destination = kwargs["destination"] - logger = kwargs["logger"] + logger = kwargs["logger"] if "logger" in kwargs and kwargs["logger"] else logging logger.info(f"{scriptName}:\t👓\t{pathname}") @@ -72,12 +72,12 @@ def file_processor(pathname, **kwargs): if fileMime in mime_types: # looks like this is a compressed file, we're assuming it's a zeek log archive to be processed by filebeat - logger.info(f"{scriptName}:\t🖅\t{pathname} ({fileMime}) to {destination}") + logger.info(f"{scriptName}:\t🖅\t{pathname} [{fileMime}] to {destination}") shutil.move(pathname, os.path.join(destination, os.path.basename(pathname))) else: # unhandled file type uploaded, delete it - logger.warning(f"{scriptName}:\t🗑\t{pathname} ({fileMime})") + logger.warning(f"{scriptName}:\t🗑\t{pathname} [{fileMime}]") os.unlink(pathname) except Exception as genericError: @@ -210,8 +210,7 @@ def main(): # if directory to monitor doesn't exist, create it now if not os.path.isdir(args.srcDir): - if debug: - eprint(f'{scriptName}:\tcreating "{args.srcDir}" to monitor') + logging.info(f'{scriptName}:\tcreating "{args.srcDir}" to monitor') pathlib.Path(args.srcDir).mkdir(parents=False, exist_ok=True) # if recursion was requested, get list of directories to monitor diff --git a/pcap-monitor/scripts/watch-pcap-uploads-folder.py b/pcap-monitor/scripts/watch-pcap-uploads-folder.py index 5563a41f0..d526589ad 100755 --- a/pcap-monitor/scripts/watch-pcap-uploads-folder.py +++ b/pcap-monitor/scripts/watch-pcap-uploads-folder.py @@ -44,7 +44,7 @@ def file_processor(pathname, **kwargs): gid = kwargs["gid"] pcapDir = kwargs["destination"] zeekDir = kwargs["zeek"] - logger = kwargs["logger"] + logger = kwargs["logger"] if "logger" in kwargs and kwargs["logger"] else logging logger.info(f"{scriptName}:\t👓\t{pathname}") @@ -61,7 +61,7 @@ def file_processor(pathname, **kwargs): (fileMime in ('application/vnd.tcpdump.pcap', 'application/x-pcapng')) or ('pcap-ng' in fileType) ): # a pcap file to be processed by dropping it into pcapDir - logger.info(f"{scriptName}:\t🖅\t{pathname} ({fileMime}/{fileType}) to {pcapDir}") + logger.info(f"{scriptName}:\t🖅\t{pathname} [{fileMime}][{fileType}] to {pcapDir}") shutil.move(pathname, os.path.join(pcapDir, os.path.basename(pathname))) elif os.path.isdir(zeekDir) and ( @@ -81,7 +81,7 @@ def file_processor(pathname, **kwargs): ] ): # looks like this is a compressed file, we're assuming it's a zeek log archive to be processed by filebeat - logger.info(f"{scriptName}:\t🖅\t{pathname} ({fileMime}/{fileType}) to {zeekDir}") + logger.info(f"{scriptName}:\t🖅\t{pathname} [{fileMime}][{fileType}] to {zeekDir}") shutil.move(pathname, os.path.join(zeekDir, os.path.basename(pathname))) else: diff --git a/shared/bin/pcap_watcher.py b/shared/bin/pcap_watcher.py index fb972a66e..f275493d4 100755 --- a/shared/bin/pcap_watcher.py +++ b/shared/bin/pcap_watcher.py @@ -58,8 +58,6 @@ ARKIME_FILE_SIZE_FIELD = "filesize" ################################################################################################### -debug = False -verboseDebug = False pdbFlagged = False args = None opensearchHttpAuth = None @@ -73,15 +71,14 @@ ################################################################################################### # watch files written to and moved to this directory class EventWatcher: - def __init__(self): + def __init__(self, logger=None): global args global opensearchHttpAuth - global debug - global verboseDebug global shuttingDown super().__init__() + self.logger = logger if logger else logging self.useOpenSearch = False self.openSearchClient = None @@ -94,8 +91,7 @@ def __init__(self): while (not connected) and (not shuttingDown[0]): try: try: - if debug: - eprint(f"{scriptName}:\tconnecting to OpenSearch {args.opensearchUrl}...") + self.logger.info(f"{scriptName}:\tconnecting to OpenSearch {args.opensearchUrl}...") self.openSearchClient = OpenSearch( hosts=[args.opensearchUrl], @@ -106,16 +102,14 @@ def __init__(self): request_timeout=1, ) - if verboseDebug: - eprint(f"{scriptName}:\t{self.openSearchClient.cluster.health()}") + self.logger.debug(f"{scriptName}:\t{self.openSearchClient.cluster.health()}") self.openSearchClient.cluster.health( wait_for_status='red', request_timeout=1, ) - if verboseDebug: - eprint(f"{scriptName}:\t{self.openSearchClient.cluster.health()}") + self.logger.debug(f"{scriptName}:\t{self.openSearchClient.cluster.health()}") connected = self.openSearchClient is not None if not connected: @@ -127,12 +121,12 @@ def __init__(self): ConnectionRefusedError, NewConnectionError, ) as connError: - if debug: - eprint(f"{scriptName}:\tOpenSearch connection error: {connError}") + self.logger.error(f"{scriptName}:\tOpenSearch connection error: {connError}") except Exception as genericError: - if debug: - eprint(f"{scriptName}:\tUnexpected exception while connecting to OpenSearch: {genericError}") + self.logger.error( + f"{scriptName}:\tUnexpected exception while connecting to OpenSearch: {genericError}" + ) if (not connected) and args.opensearchWaitForHealth: time.sleep(1) @@ -144,14 +138,12 @@ def __init__(self): # if requested, wait for at least "yellow" health in the cluster for the "files" index while connected and args.opensearchWaitForHealth and (not healthy) and (not shuttingDown[0]): try: - if debug: - eprint(f"{scriptName}:\twaiting for OpenSearch to be healthy") + self.logger.info(f"{scriptName}:\twaiting for OpenSearch to be healthy") self.openSearchClient.cluster.health( index=ARKIME_FILES_INDEX, wait_for_status='yellow', ) - if verboseDebug: - eprint(f"{scriptName}:\t{self.openSearchClient.cluster.health()}") + self.logger.debug(f"{scriptName}:\t{self.openSearchClient.cluster.health()}") healthy = True except ( @@ -160,8 +152,7 @@ def __init__(self): ConnectionRefusedError, NewConnectionError, ) as connError: - if verboseDebug: - eprint(f"{scriptName}:\tOpenSearch health check: {connError}") + self.logger.debug(f"{scriptName}:\tOpenSearch health check: {connError}") if not healthy: time.sleep(1) @@ -172,8 +163,7 @@ def __init__(self): self.context = zmq.Context() # Socket to send messages on - if debug: - eprint(f"{scriptName}:\tbinding publisher port {PCAP_TOPIC_PORT}") + self.logger.info(f"{scriptName}:\tbinding publisher port {PCAP_TOPIC_PORT}") self.topic_socket = self.context.socket(zmq.PUB) self.topic_socket.bind(f"tcp://*:{PCAP_TOPIC_PORT}") @@ -181,18 +171,14 @@ def __init__(self): # and if he can't then what's the point? just block # self.topic_socket.SNDTIMEO = 5000 - if debug: - eprint(f"{scriptName}:\tEventWatcher initialized") + self.logger.info(f"{scriptName}:\tEventWatcher initialized") ################################################################################################### # set up event processor to append processed events from to the event queue def processFile(self, pathname): global args - global debug - global verboseDebug - if debug: - eprint(f"{scriptName}:\t👓\t{pathname}") + self.logger.info(f"{scriptName}:\t👓\t{pathname}") # the entity must be a regular PCAP file and actually exist if os.path.isfile(pathname): @@ -224,13 +210,11 @@ def processFile(self, pathname): if fileIsDuplicate: # this is duplicate file (it's been processed before) so ignore it - if debug: - eprint(f"{scriptName}:\t📋\t{pathname}") + self.logger.info(f"{scriptName}:\t📋\t{pathname}") else: # the entity is a right-sized non-duplicate file, and it exists, so send it to get processed - if debug: - eprint(f"{scriptName}:\t📩\t{pathname}") + self.logger.info(f"{scriptName}:\t📩\t{pathname}") try: fileInfo = { FILE_INFO_DICT_NAME: pathname if args.includeAbsolutePath else relativePath, @@ -241,16 +225,13 @@ def processFile(self, pathname): FILE_INFO_DICT_TAGS: tags_from_filename(relativePath), } self.topic_socket.send_string(json.dumps(fileInfo)) - if debug: - eprint(f"{scriptName}:\t📫\t{fileInfo}") + self.logger.info(f"{scriptName}:\t📫\t{fileInfo}") except zmq.Again: - if verboseDebug: - eprint(f"{scriptName}:\t🕑\t{pathname}") + self.logger.debug(f"{scriptName}:\t🕑\t{pathname}") else: # too small/big to care about, or the wrong type, ignore it - if debug: - eprint(f"{scriptName}:\t✋\t{pathname}") + self.logger.info(f"{scriptName}:\t✋\t{pathname}") def file_processor(pathname, **kwargs): @@ -272,51 +253,16 @@ def pdb_handler(sig, frame): pdbFlagged = True -################################################################################################### -# handle sigusr2 for toggling debug -def debug_toggle_handler(signum, frame): - global debug - global debugToggled - debug = not debug - debugToggled = True - - ################################################################################################### # main def main(): global args global opensearchHttpAuth - global debug - global verboseDebug - global debugToggled global pdbFlagged global shuttingDown parser = argparse.ArgumentParser(description=scriptName, add_help=False, usage='{} '.format(scriptName)) - parser.add_argument( - '-v', - '--verbose', - dest='debug', - help="Verbose output", - metavar='true|false', - type=str2bool, - nargs='?', - const=True, - default=False, - required=False, - ) - parser.add_argument( - '--extra-verbose', - dest='verboseDebug', - help="Super verbose output", - metavar='true|false', - type=str2bool, - nargs='?', - const=True, - default=False, - required=False, - ) - + parser.add_argument('--verbose', '-v', action='count', default=1, help='Increase verbosity (e.g., -v, -vv, etc.)') parser.add_argument( '--min-bytes', dest='minBytes', @@ -465,17 +411,16 @@ def main(): parser.print_help() exit(2) - verboseDebug = args.verboseDebug - debug = args.debug or verboseDebug - if debug: - eprint(os.path.join(scriptPath, scriptName)) - eprint("{} arguments: {}".format(scriptName, sys.argv[1:])) - eprint("{} arguments: {}".format(scriptName, args)) - else: + args.verbose = logging.ERROR - (10 * args.verbose) if args.verbose > 0 else 0 + logging.basicConfig( + level=args.verbose, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' + ) + logging.info(os.path.join(scriptPath, scriptName)) + logging.info("Arguments: {}".format(sys.argv[1:])) + logging.info("Arguments: {}".format(args)) + if args.verbose > logging.DEBUG: sys.tracebacklimit = 0 - logging.basicConfig(level=logging.ERROR) - args.opensearchIsLocal = args.opensearchIsLocal or (args.opensearchUrl == 'http://opensearch:9200') opensearchCreds = ( ParseCurlFile(args.opensearchCurlRcFile) if (not args.opensearchIsLocal) else defaultdict(lambda: None) @@ -493,7 +438,6 @@ def main(): signal.signal(signal.SIGINT, shutdown_handler) signal.signal(signal.SIGTERM, shutdown_handler) signal.signal(signal.SIGUSR1, pdb_handler) - signal.signal(signal.SIGUSR2, debug_toggle_handler) # sleep for a bit if requested sleepCount = 0 @@ -506,8 +450,7 @@ def main(): preexistingDir = True else: preexistingDir = False - if debug: - eprint(f'{scriptName}:\tcreating "{args.baseDir}" to monitor') + logging.info(f'{scriptName}:\tcreating "{args.baseDir}" to monitor') pathlib.Path(args.baseDir).mkdir(parents=False, exist_ok=True) # if recursion was requested, get list of directories to monitor @@ -527,14 +470,12 @@ def main(): polling=args.polling, ) for watchDir in watchDirs: - if verboseDebug: - eprint(f"{scriptName}:\tScheduling {watchDir}") + logging.debug(f"{scriptName}:\tScheduling {watchDir}") observer.schedule(handler, watchDir, recursive=False) observer.start() - if debug: - eprint(f"{scriptName}:\tmonitoring {watchDirs}") + logging.info(f"{scriptName}:\tmonitoring {watchDirs}") try: time.sleep(2) @@ -548,8 +489,8 @@ def main(): ]: touch(preexistingFile) filesTouched += 1 - if debug and (filesTouched > 0): - eprint(f"{scriptName}:\tfound {filesTouched} preexisting files to check") + if filesTouched > 0: + logging.info(f"{scriptName}:\tfound {filesTouched} preexisting files to check") # start the thread to actually handle the files as they're queued by the FileOperationEventHandler handler workerThreadCount = malcolm_utils.AtomicInt(value=0) @@ -560,11 +501,11 @@ def main(): handler, observer, file_processor, - {'watcher': EventWatcher()}, + {'watcher': EventWatcher(logger=logging)}, args.assumeClosedSec, workerThreadCount, shuttingDown, - None, + logging, ], ), ) @@ -577,8 +518,7 @@ def main(): observer.join(1) # graceful shutdown - if debug: - eprint(f"{scriptName}:\tshutting down...") + logging.info(f"{scriptName}:\tshutting down...") if shuttingDown[0]: raise WatchdogShutdown() @@ -594,8 +534,7 @@ def main(): while workerThreadCount.value() > 0: time.sleep(1) - if debug: - eprint(f"{scriptName}:\tfinished monitoring {watchDirs}") + logging.info(f"{scriptName}:\tfinished monitoring {watchDirs}") if __name__ == '__main__': diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py index 01570fe67..2e5f55226 100644 --- a/shared/bin/watch_common.py +++ b/shared/bin/watch_common.py @@ -3,6 +3,7 @@ import os import json +import logging import time from malcolm_utils import AtomicInt, ContextLockedOrderedDict, same_file_or_dir @@ -31,7 +32,7 @@ ASSUME_CLOSED_SEC_DEFAULT = 10 -OperationEvent = namedtuple("OperationEvent", ["timestamp", "operation"], rename=False) +OperationEvent = namedtuple("OperationEvent", ["timestamp", "operation", "size"], rename=False) ################################################################################################### @@ -45,7 +46,7 @@ def __init__( ): super().__init__(*args, **kwargs) self.polling = polling - self.logger = logger + self.logger = logger if logger else logging self.updateTime() # self.deck is a dictionary mapping filenames to a list of OperationEvent of length n, # with [0] being the oldest timestamp/operation and [n-1] being the newest @@ -62,48 +63,68 @@ def updateTime(self): def on_any_event(self, event): fName = None - try: - if not event.is_directory: - self.updateTime() - - # FileClosedEvent is only going to come from inotify events, not polling - # so we know we're good to go (a FileClosedEvent signals we can process the - # file immediately). We can signal this by setting the timestamp to 0. - newOpLog = OperationEvent( - self.nowTime if (not isinstance(event, FileClosedEvent)) else 0, event.event_type - ) - - # if this is a move event, we need to track the old and new filenames - if isinstance(event, FileSystemMovedEvent): - fName = event.dest_path - fNameOld = event.src_path - if self.logger: - self.logger.info(f"↦\t{event.event_type: >10}\t{event.src_path} {event.dest_path}") - else: - fName = event.src_path - fNameOld = None - if self.logger: - self.logger.info(f"🗲\t{event.event_type: <10}\t{event.src_path}") - - with self.deck as d: - if fNameOld: - if same_file_or_dir(os.path.dirname(fNameOld), os.path.dirname(fName)): - # a file was simply renamed in the watched directory (not moved - # from some other directory) so just update the filename - d.pop(fNameOld, None) - else: - # the file was moved from somewhere else, treat it like a create - pass + if not event.is_directory: + self.updateTime() + + # if this is a move event, we need to track the old and new filenames + if isinstance(event, FileSystemMovedEvent): + fName = event.dest_path + fNameOld = event.src_path + self.logger.info(f"↦\t{event.event_type: >10}\t{event.src_path} {event.dest_path}") + else: + fName = event.src_path + fNameOld = None + self.logger.info(f"🗲\t{event.event_type: <10}\t{event.src_path}") + + # This is a pain, but due to this watchdog issue (see + # https://github.com/gorakhargosh/watchdog/issues/260 and) + # we get FileModifiedEvent triggered for metadata-only changes + # even if content has not changed (e.g., file access time). + # So for now, if we detect a file has been modified but the size + # hasn't changed, we will ignore that event. + try: + fSize = os.path.getsize(fName) + except Exception: + fSize = 0 + + # FileClosedEvent is only going to come from inotify events, not polling + # so we know we're good to go (a FileClosedEvent signals we can process the + # file immediately). We can signal this by setting the timestamp to 0. + newOpLog = OperationEvent( + self.nowTime if (not isinstance(event, FileClosedEvent)) else 0, + event.event_type, + fSize, + ) + noop = False + + with self.deck as d: + try: + if fNameOld and same_file_or_dir(os.path.dirname(fNameOld), os.path.dirname(fName)): + # a file was simply renamed in the watched directory (not moved + # from some other directory) so remove the old filename from our list + # and a new one will get added + d.pop(fNameOld, None) # insert or update file event(s) if fName in d: # this is a file we're already currently tracking - # if the previous operation (the last one in the history) was the same as this one, - # replace the operation rather than appending a new one (effectively just updating the timestamp) - if (len(d[fName]) > 0) and (d[fName][-1].operation == event.event_type): - d[fName][-1] = newOpLog + # see comment about fSize above (FileModifiedEvent only counts if the file size is changed) + if ( + isinstance(event, FileModifiedEvent) + and (newOpLog.size > 0) + and (len(d[fName]) > 0) + and (newOpLog.size == d[fName][-1].size) + ): + # don't do *anything*, leave the entry untouched in the list + noop = True + + elif (len(d[fName]) > 0) and (d[fName][-1].operation == event.event_type): + # if the previous operation (the last one in the history) was the same as this one, + # replace the operation rather than appending a new one (effectively just updating the timestamp) + if (newOpLog.timestamp > d[fName][-1].timestamp) or (newOpLog.size != d[fName][-1].size): + d[fName][-1] = newOpLog else: # otherwise append a new history item @@ -113,27 +134,33 @@ def on_any_event(self, event): # this is a file we were not previously tracking d[fName] = [newOpLog] - if ( - isinstance(event, FileModifiedEvent) - or isinstance(event, FileClosedEvent) - or isinstance(event, FileCreatedEvent) - or isinstance(event, FileSystemMovedEvent) - ): - # put FileClosedEvent events (which now have a timestamp of 0) at the front of - # the deck (to be processed first), and others to the back - d.move_to_end(fName, last=d[fName][-1] > 0) - - elif isinstance(event, FileDeletedEvent): - # if a file is deleted I guess we don't need to track it any more - d.pop(fName, None) - fName = None - - if self.logger and fName: + if not noop: + if ( + isinstance(event, FileModifiedEvent) + or isinstance(event, FileClosedEvent) + or isinstance(event, FileCreatedEvent) + or isinstance(event, FileSystemMovedEvent) + ): + # put FileClosedEvent events (which now have a timestamp of 0) at the front of + # the deck (to be processed first), and others to the back + d.move_to_end(fName, last=d[fName][-1].timestamp > 0) + + elif isinstance(event, FileDeletedEvent): + # if a file is deleted I guess we don't need to track it any more + d.pop(fName, None) + fName = None + + else: + noop = True + + if noop: + self.logger.debug(f"🗑\t{event.event_type: <10}\t{fName}") + + elif fName: self.logger.debug(f"⎗\t{fName}\t{json.dumps(d[fName])}") - except Exception as e: - if self.logger: - self.logger.error(f"⨳\t{fName}\t{e}") + except Exception as e: + self.logger.error(f"⨳\t{fName}\t{e}") ################################################################################################### @@ -148,10 +175,11 @@ def ProcessFileEventWorker(workerArgs): workerArgs[6], workerArgs[7], ) + if not logger: + logger = logging with workerThreadCount as workerId: - if logger: - logger.info(f"۞\tstarted\t[{workerId}]") + logger.info(f"۞\tstarted\t[{workerId}]") while (not shutDown[0]) and observer.is_alive(): time.sleep(0.5) @@ -159,16 +187,14 @@ def ProcessFileEventWorker(workerArgs): with handler.deck as d: for fileName, fileHistory in list(d.items()): - if logger: - logger.debug(f"⏿ checking {fileName}\t{json.dumps(fileHistory)}\t[{workerId}]") + logger.debug(f"⏿ checking {fileName}\t{json.dumps(fileHistory)}\t[{workerId}]") if len(fileHistory) > 0: if nowTime < fileHistory[-1].timestamp + assumeClosedSec: # we can break because the list is ordered - if logger: - logger.debug( - f"⎊\tbreaking early because {nowTime} < {fileHistory[-1].timestamp + assumeClosedSec}\t[{workerId}]" - ) + logger.debug( + f"⎊\tbreaking early because {nowTime} < {fileHistory[-1].timestamp + assumeClosedSec}\t[{workerId}]" + ) break else: @@ -183,14 +209,12 @@ def ProcessFileEventWorker(workerArgs): fileName, **extraArgs, ) - if logger: - logger.info( - f"🖄\tprocessed\t{fileName} at {(nowTime-fileHistory[-1].timestamp) if (fileHistory[-1].timestamp > 0) else 0} seconds\t[{workerId}]" - ) + logger.info( + f"🖄\tprocessed\t{fileName} at {(nowTime-fileHistory[-1].timestamp) if (fileHistory[-1].timestamp > 0) else 0} seconds\t[{workerId}]" + ) time.sleep(1) - if logger: - logger.info(f"⛒\tfinished\t[{workerId}]") + logger.info(f"⛒\tfinished\t[{workerId}]") def WatchAndProcessDirectory( @@ -203,13 +227,13 @@ def WatchAndProcessDirectory( logger, ): observer = PollingObserver() if polling else Observer() + loggerToUse = logger if logger else logging handler = FileOperationEventHandler( - logger=logger, + logger=loggerToUse, polling=polling, ) for directory in directories: - if logger: - logger.info(f"🗐\tScheduling {directory}") + loggerToUse.info(f"🗐\tScheduling {directory}") observer.schedule(handler, directory, recursive=True) observer.start() @@ -226,7 +250,7 @@ def WatchAndProcessDirectory( assumeClosedSec, workerThreadCount, shuttingDown, - logger, + loggerToUse, ], ), ) diff --git a/shared/bin/zeek_carve_watcher.py b/shared/bin/zeek_carve_watcher.py index 30f482cdc..5528bd214 100755 --- a/shared/bin/zeek_carve_watcher.py +++ b/shared/bin/zeek_carve_watcher.py @@ -43,8 +43,6 @@ MAXIMUM_CHECKED_FILE_SIZE_DEFAULT = 134217728 ################################################################################################### -debug = False -verboseDebug = False pdbFlagged = False args = None scriptName = os.path.basename(__file__) @@ -56,17 +54,16 @@ ################################################################################################### # watch files written to and moved to this directory class EventWatcher: - def __init__(self): - global debug - + def __init__(self, logger=None): super().__init__() + self.logger = logger if logger else logging + # initialize ZeroMQ context and socket(s) to send messages to self.context = zmq.Context() # Socket to send messages on - if debug: - eprint(f"{scriptName}:\tbinding ventilator port {VENTILATOR_PORT}") + self.logger.info(f"{scriptName}:\tbinding ventilator port {VENTILATOR_PORT}") self.ventilator_socket = self.context.socket(zmq.PUB) self.ventilator_socket.bind(f"tcp://*:{VENTILATOR_PORT}") @@ -74,18 +71,14 @@ def __init__(self): # and if he can't then what's the point? just block # self.ventilator_socket.SNDTIMEO = 5000 - if debug: - eprint(f"{scriptName}:\tEventWatcher initialized") + self.logger.info(f"{scriptName}:\tEventWatcher initialized") ################################################################################################### # set up event processor to append processed events from to the event queue def processFile(self, pathname): global args - global debug - global verboseDebug - if debug: - eprint(f"{scriptName}:\t👓\t{pathname}") + self.logger.info(f"{scriptName}:\t👓\t{pathname}") if os.path.isfile(pathname): fileSize = os.path.getsize(pathname) @@ -101,26 +94,21 @@ def processFile(self, pathname): FILE_SCAN_RESULT_FILE_TYPE: fileType, } ) - if debug: - eprint(f"{scriptName}:\t📩\t{fileInfo}") + self.logger.info(f"{scriptName}:\t📩\t{fileInfo}") try: self.ventilator_socket.send_string(fileInfo) - if debug: - eprint(f"{scriptName}:\t📫\t{pathname}") + self.logger.info(f"{scriptName}:\t📫\t{pathname}") except zmq.Again: - if verboseDebug: - eprint(f"{scriptName}:\t🕑\t{pathname}") + self.logger.debug(f"{scriptName}:\t🕑\t{pathname}") else: # temporary capa .viv file, just ignore it as it will get cleaned up by the scanner when it's done - if debug: - eprint(f"{scriptName}:\t🚧\t{pathname}") + self.logger.info(f"{scriptName}:\t🚧\t{pathname}") else: # too small/big to care about, delete it os.remove(pathname) - if debug: - eprint(f"{scriptName}:\t🚫\t{pathname}") + self.logger.info(f"{scriptName}:\t🚫\t{pathname}") def file_processor(pathname, **kwargs): @@ -142,49 +130,15 @@ def pdb_handler(sig, frame): pdbFlagged = True -################################################################################################### -# handle sigusr2 for toggling debug -def debug_toggle_handler(signum, frame): - global debug - global debugToggled - debug = not debug - debugToggled = True - - ################################################################################################### # main def main(): global args - global debug - global verboseDebug - global debugToggled global pdbFlagged global shuttingDown parser = argparse.ArgumentParser(description=scriptName, add_help=False, usage='{} '.format(scriptName)) - parser.add_argument( - '-v', - '--verbose', - dest='debug', - help="Verbose output", - metavar='true|false', - type=str2bool, - nargs='?', - const=True, - default=False, - required=False, - ) - parser.add_argument( - '--extra-verbose', - dest='verboseDebug', - help="Super verbose output", - metavar='true|false', - type=str2bool, - nargs='?', - const=True, - default=False, - required=False, - ) + parser.add_argument('--verbose', '-v', action='count', default=1, help='Increase verbosity (e.g., -v, -vv, etc.)') parser.add_argument( '--ignore-existing', dest='ignoreExisting', @@ -268,20 +222,20 @@ def main(): parser.print_help() exit(2) - verboseDebug = args.verboseDebug - debug = args.debug or verboseDebug - if debug: - eprint(os.path.join(scriptPath, scriptName)) - eprint("{} arguments: {}".format(scriptName, sys.argv[1:])) - eprint("{} arguments: {}".format(scriptName, args)) - else: + args.verbose = logging.ERROR - (10 * args.verbose) if args.verbose > 0 else 0 + logging.basicConfig( + level=args.verbose, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' + ) + logging.info(os.path.join(scriptPath, scriptName)) + logging.info("Arguments: {}".format(sys.argv[1:])) + logging.info("Arguments: {}".format(args)) + if args.verbose > logging.DEBUG: sys.tracebacklimit = 0 # handle sigint and sigterm for graceful shutdown signal.signal(signal.SIGINT, shutdown_handler) signal.signal(signal.SIGTERM, shutdown_handler) signal.signal(signal.SIGUSR1, pdb_handler) - signal.signal(signal.SIGUSR2, debug_toggle_handler) # sleep for a bit if requested sleepCount = 0 @@ -294,8 +248,7 @@ def main(): preexistingDir = True else: preexistingDir = False - if debug: - eprint(f'{scriptName}:\tcreating "{args.baseDir}" to monitor') + logging.info(f'{scriptName}:\tcreating "{args.baseDir}" to monitor') pathlib.Path(args.baseDir).mkdir(parents=False, exist_ok=True) # if recursion was requested, get list of directories to monitor @@ -315,14 +268,12 @@ def main(): polling=args.polling, ) for watchDir in watchDirs: - if verboseDebug: - eprint(f"{scriptName}:\tScheduling {watchDir}") + logging.info(f"{scriptName}:\tScheduling {watchDir}") observer.schedule(handler, watchDir, recursive=False) observer.start() - if debug: - eprint(f"{scriptName}:\tmonitoring {watchDirs}") + logging.info(f"{scriptName}:\tmonitoring {watchDirs}") try: time.sleep(2) @@ -336,8 +287,8 @@ def main(): ]: touch(preexistingFile) filesTouched += 1 - if debug and (filesTouched > 0): - eprint(f"{scriptName}:\tfound {filesTouched} preexisting files to check") + if filesTouched > 0: + logging.info(f"{scriptName}:\tfound {filesTouched} preexisting files to check") # start the thread to actually handle the files as they're queued by the FileOperationEventHandler handler workerThreadCount = malcolm_utils.AtomicInt(value=0) @@ -348,11 +299,11 @@ def main(): handler, observer, file_processor, - {'watcher': EventWatcher()}, + {'watcher': EventWatcher(logger=logging)}, args.assumeClosedSec, workerThreadCount, shuttingDown, - None, + logging, ], ), ) @@ -365,8 +316,7 @@ def main(): observer.join(1) # graceful shutdown - if debug: - eprint(f"{scriptName}:\tshutting down...") + logging.info(f"{scriptName}:\tshutting down...") if shuttingDown[0]: raise WatchdogShutdown() @@ -382,8 +332,7 @@ def main(): while workerThreadCount.value() > 0: time.sleep(1) - if debug: - eprint(f"{scriptName}:\tfinished monitoring {watchDirs}") + logging.info(f"{scriptName}:\tfinished monitoring {watchDirs}") if __name__ == '__main__': From 3ab934abce59b232147e243a2290b750f0ecf51a Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 6 Apr 2023 12:45:52 -0600 Subject: [PATCH 109/239] work in progress on a little bit more of the python refactoring with the inotify stuff, might be slightly broken :P --- Dockerfiles/arkime.Dockerfile | 6 +- Dockerfiles/file-monitor.Dockerfile | 6 +- Dockerfiles/pcap-monitor.Dockerfile | 6 +- Dockerfiles/suricata.Dockerfile | 6 +- Dockerfiles/zeek.Dockerfile | 6 +- arkime/supervisord.conf | 4 +- config/upload-common.env.example | 6 +- config/zeek.env.example | 6 +- docs/contributing-file-scanners.md | 2 +- docs/contributing-pcap.md | 2 +- file-monitor/supervisord.conf | 24 +--- filebeat/supervisord.conf | 2 +- freq-server/supervisord.conf | 4 +- pcap-monitor/supervisord.conf | 6 +- shared/bin/pcap_processor.py | 195 +++++++++++----------------- shared/bin/zeek_carve_logger.py | 102 ++++----------- shared/bin/zeek_carve_scanner.py | 117 +++++------------ shared/bin/zeek_carve_utils.py | 194 ++++++++++++++------------- shared/bin/zeek_carve_watcher.py | 1 + suricata/supervisord.conf | 4 +- zeek/supervisord.conf | 4 +- 21 files changed, 264 insertions(+), 439 deletions(-) diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index 76989be9d..8186571f0 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -107,8 +107,7 @@ ARG VIEWER=on ARG MANAGE_PCAP_FILES=false #Whether or not to auto-tag logs based on filename ARG AUTO_TAG=true -ARG PCAP_PIPELINE_DEBUG=false -ARG PCAP_PIPELINE_DEBUG_EXTRA=false +ARG PCAP_PIPELINE_VERBOSITY="" ARG PCAP_MONITOR_HOST=pcap-monitor ARG MAXMIND_GEOIP_DB_LICENSE_KEY="" @@ -128,8 +127,7 @@ ENV WISE $WISE ENV VIEWER $VIEWER ENV MANAGE_PCAP_FILES $MANAGE_PCAP_FILES ENV AUTO_TAG $AUTO_TAG -ENV PCAP_PIPELINE_DEBUG $PCAP_PIPELINE_DEBUG -ENV PCAP_PIPELINE_DEBUG_EXTRA $PCAP_PIPELINE_DEBUG_EXTRA +ENV PCAP_PIPELINE_VERBOSITY $PCAP_PIPELINE_VERBOSITY ENV PCAP_MONITOR_HOST $PCAP_MONITOR_HOST COPY --from=build $ARKIME_DIR $ARKIME_DIR diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index 1a715ef29..c31d711a2 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -36,8 +36,7 @@ ARG VTOT_API2_KEY=0 ARG VTOT_REQUESTS_PER_MINUTE=4 ARG EXTRACTED_FILE_ENABLE_CLAMAV=false ARG EXTRACTED_FILE_UPDATE_RULES=false -ARG EXTRACTED_FILE_PIPELINE_DEBUG=false -ARG EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA=false +ARG EXTRACTED_FILE_PIPELINE_VERBOSITY="" ARG CLAMD_SOCKET_FILE=/tmp/clamd.ctl ARG CLAMD_MAX_REQUESTS=8 ARG YARA_MAX_REQUESTS=8 @@ -67,8 +66,7 @@ ENV VTOT_API2_KEY $VTOT_API2_KEY ENV VTOT_REQUESTS_PER_MINUTE $VTOT_REQUESTS_PER_MINUTE ENV EXTRACTED_FILE_ENABLE_CLAMAV $EXTRACTED_FILE_ENABLE_CLAMAV ENV EXTRACTED_FILE_UPDATE_RULES $EXTRACTED_FILE_UPDATE_RULES -ENV EXTRACTED_FILE_PIPELINE_DEBUG $EXTRACTED_FILE_PIPELINE_DEBUG -ENV EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA $EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA +ENV EXTRACTED_FILE_PIPELINE_VERBOSITY $EXTRACTED_FILE_PIPELINE_VERBOSITY ENV CLAMD_SOCKET_FILE $CLAMD_SOCKET_FILE ENV CLAMD_MAX_REQUESTS $CLAMD_MAX_REQUESTS ENV YARA_MAX_REQUESTS $YARA_MAX_REQUESTS diff --git a/Dockerfiles/pcap-monitor.Dockerfile b/Dockerfiles/pcap-monitor.Dockerfile index 6b089f71f..9410d2542 100644 --- a/Dockerfiles/pcap-monitor.Dockerfile +++ b/Dockerfiles/pcap-monitor.Dockerfile @@ -27,8 +27,7 @@ ENV TERM xterm ARG OPENSEARCH_URL="http://opensearch:9200" ARG OPENSEARCH_LOCAL=true ARG PCAP_PATH=/pcap -ARG PCAP_PIPELINE_DEBUG=false -ARG PCAP_PIPELINE_DEBUG_EXTRA=false +ARG PCAP_PIPELINE_VERBOSITY="" ARG PCAP_PIPELINE_IGNORE_PREEXISTING=false ARG PCAP_PIPELINE_POLLING=false ARG PCAP_PIPELINE_POLLING_ASSUME_CLOSED_SEC=10 @@ -38,8 +37,7 @@ ARG ZEEK_PATH=/zeek ENV OPENSEARCH_URL $OPENSEARCH_URL ENV OPENSEARCH_LOCAL $OPENSEARCH_LOCAL ENV PCAP_PATH $PCAP_PATH -ENV PCAP_PIPELINE_DEBUG $PCAP_PIPELINE_DEBUG -ENV PCAP_PIPELINE_DEBUG_EXTRA $PCAP_PIPELINE_DEBUG_EXTRA +ENV PCAP_PIPELINE_VERBOSITY $PCAP_PIPELINE_VERBOSITY ENV PCAP_PIPELINE_IGNORE_PREEXISTING $PCAP_PIPELINE_IGNORE_PREEXISTING ENV PCAP_PIPELINE_POLLING $PCAP_PIPELINE_POLLING ENV PCAP_PIPELINE_POLLING_ASSUME_CLOSED_SEC $PCAP_PIPELINE_POLLING_ASSUME_CLOSED_SEC diff --git a/Dockerfiles/suricata.Dockerfile b/Dockerfiles/suricata.Dockerfile index 17cef6e09..131bed0e7 100644 --- a/Dockerfiles/suricata.Dockerfile +++ b/Dockerfiles/suricata.Dockerfile @@ -138,8 +138,7 @@ COPY --chmod=755 suricata/scripts/docker_entrypoint.sh /usr/local/bin/ COPY --chmod=755 suricata/scripts/eve-clean-logs.sh /usr/local/bin/ COPY --chmod=755 suricata/scripts/suricata-update-rules.sh /usr/local/bin/ -ARG PCAP_PIPELINE_DEBUG=false -ARG PCAP_PIPELINE_DEBUG_EXTRA=false +ARG PCAP_PIPELINE_VERBOSITY="" ARG PCAP_MONITOR_HOST=pcap-monitor ARG AUTO_TAG=true ARG SURICATA_PCAP_PROCESSOR=true @@ -158,8 +157,7 @@ ARG PCAP_IFACE=lo ARG PCAP_IFACE_TWEAK=false ARG PCAP_FILTER= -ENV PCAP_PIPELINE_DEBUG $PCAP_PIPELINE_DEBUG -ENV PCAP_PIPELINE_DEBUG_EXTRA $PCAP_PIPELINE_DEBUG_EXTRA +ENV PCAP_PIPELINE_VERBOSITY $PCAP_PIPELINE_VERBOSITY ENV PCAP_MONITOR_HOST $PCAP_MONITOR_HOST ENV AUTO_TAG $AUTO_TAG ENV SURICATA_PCAP_PROCESSOR $SURICATA_PCAP_PROCESSOR diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index ca688465c..aca1a7b43 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -208,8 +208,7 @@ ARG ZEEK_INTEL_REFRESH_THREADS=2 ARG ZEEK_INTEL_FEED_SINCE= ARG ZEEK_EXTRACTOR_MODE=none ARG ZEEK_EXTRACTOR_PATH=/zeek/extract_files -ARG PCAP_PIPELINE_DEBUG=false -ARG PCAP_PIPELINE_DEBUG_EXTRA=false +ARG PCAP_PIPELINE_VERBOSITY="" ARG PCAP_MONITOR_HOST=pcap-monitor ARG ZEEK_LIVE_CAPTURE=false ARG ZEEK_ROTATED_PCAP=false @@ -229,8 +228,7 @@ ENV ZEEK_INTEL_REFRESH_THREADS $ZEEK_INTEL_REFRESH_THREADS ENV ZEEK_INTEL_FEED_SINCE $ZEEK_INTEL_FEED_SINCE ENV ZEEK_EXTRACTOR_MODE $ZEEK_EXTRACTOR_MODE ENV ZEEK_EXTRACTOR_PATH $ZEEK_EXTRACTOR_PATH -ENV PCAP_PIPELINE_DEBUG $PCAP_PIPELINE_DEBUG -ENV PCAP_PIPELINE_DEBUG_EXTRA $PCAP_PIPELINE_DEBUG_EXTRA +ENV PCAP_PIPELINE_VERBOSITY $PCAP_PIPELINE_VERBOSITY ENV PCAP_MONITOR_HOST $PCAP_MONITOR_HOST ENV ZEEK_LIVE_CAPTURE $ZEEK_LIVE_CAPTURE ENV ZEEK_ROTATED_PCAP $ZEEK_ROTATED_PCAP diff --git a/arkime/supervisord.conf b/arkime/supervisord.conf index 7b54e623b..e86840b7d 100644 --- a/arkime/supervisord.conf +++ b/arkime/supervisord.conf @@ -50,9 +50,7 @@ stdout_logfile_maxbytes=0 redirect_stderr=true [program:pcap-arkime] -command=python3 /opt/pcap_arkime_processor.py - --verbose "%(ENV_PCAP_PIPELINE_DEBUG)s" - --extra-verbose "%(ENV_PCAP_PIPELINE_DEBUG_EXTRA)s" +command=python3 /opt/pcap_arkime_processor.py %(ENV_PCAP_PIPELINE_VERBOSITY)s --start-sleep 10 --threads %(ENV_ARKIME_ANALYZE_PCAP_THREADS)s --publisher "%(ENV_PCAP_MONITOR_HOST)s" diff --git a/config/upload-common.env.example b/config/upload-common.env.example index e5891711f..ad55df213 100644 --- a/config/upload-common.env.example +++ b/config/upload-common.env.example @@ -4,10 +4,8 @@ AUTO_TAG=true # The node name (e.g., the hostname of this machine running Malcolm) to associate with # network traffic metadata PCAP_NODE_NAME=malcolm -# Whether or not to enable debug output for processing uploaded/captured PCAP files -PCAP_PIPELINE_DEBUG=false -# Whether or not to enable very verbose debug output for processing uploaded/captured PCAP files -PCAP_PIPELINE_DEBUG_EXTRA=false +# Verbosity flag for pcap pipeline debugging (e.g., -v, -vv, -vvv, etc.) +PCAP_PIPELINE_VERBOSITY= # Whether or not PCAP files extant in ./pcap/ will be ignored on startup PCAP_PIPELINE_IGNORE_PREEXISTING=false # Whether or not to use polling vs. native inotify API to watch for files diff --git a/config/zeek.env.example b/config/zeek.env.example index f88cd4034..ba3d5db6f 100644 --- a/config/zeek.env.example +++ b/config/zeek.env.example @@ -40,10 +40,8 @@ EXTRACTED_FILE_CAPA_VERBOSE=false EXTRACTED_FILE_ENABLE_CLAMAV=false # Whether or not to regularly update rule definitions for file scanning engines EXTRACTED_FILE_UPDATE_RULES=false -# Whether or not to enable debug output for Zeek-extracted file scanning -EXTRACTED_FILE_PIPELINE_DEBUG=false -# Whether or not to enable very verbose debug output for Zeek-extracted file scanning -EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA=false +# Verbosity flag for extracted file pipeline debugging (e.g., -v, -vv, -vvv, etc.) +EXTRACTED_FILE_PIPELINE_VERBOSITY= # Whether or not to serve the directory containing Zeek-extracted over HTTP at ./extracted-files/ EXTRACTED_FILE_HTTP_SERVER_ENABLE=false # Whether or not Zeek-extracted files served over HTTP will be AES-256-CBC-encrypted diff --git a/docs/contributing-file-scanners.md b/docs/contributing-file-scanners.md index 0dc0bd572..139e7b510 100644 --- a/docs/contributing-file-scanners.md +++ b/docs/contributing-file-scanners.md @@ -11,4 +11,4 @@ When Zeek extracts a file it observes being transfered in network traffic, the ` Additional file scanners could either be added to the `file-monitor` service, or to avoid coupling with Malcolm's code you could simply define a new service as instructed in the [Adding a new service](contributing-new-image.md#NewImage) section and write your own scripts to subscribe and publish to the topics as described above. While that might be a bit of hand-waving, these general steps take care of the plumbing around extracting the file and notifying your tool, as well as handling the logging of "hits": you shouldn't have to really edit any *existing* code to add a new carved file scanner. -The `EXTRACTED_FILE_PIPELINE_DEBUG` and `EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA` environment variables in the `docker-compose` files can be set to `true` to enable verbose debug logging from the output of the Docker containers involved in the carved file processing pipeline. \ No newline at end of file +The `EXTRACTED_FILE_PIPELINE_VERBOSITY` environment variables in can be set to `-v`, `-vv`, etc., to increase the verbosity of debug logging from the output of the Docker containers involved in the carved file processing pipeline. \ No newline at end of file diff --git a/docs/contributing-pcap.md b/docs/contributing-pcap.md index 04cdb7ae1..d77862cd6 100644 --- a/docs/contributing-pcap.md +++ b/docs/contributing-pcap.md @@ -9,4 +9,4 @@ When a PCAP is uploaded (either through Malcolm's [upload web interface](upload. While that might be a bit of hand-waving, these general steps take care of the PCAP processing piece: you shouldn't have to really edit any *existing* code to add a new PCAP processor. You're just creating a new container for the Malcolm appliance to the ZeroMQ topic and handle the PCAPs your tool receives. -The `PCAP_PIPELINE_DEBUG` and `PCAP_PIPELINE_DEBUG_EXTRA` environment variables in the `docker-compose` files can be set to `true` to enable verbose debug logging from the output of the Docker containers involved in the PCAP processing pipeline. +The `PCAP_PIPELINE_VERBOSITY` environment variables in can be set to `-v`, `-vv`, etc., to increase the verbosity of debug logging from the output of the Docker containers involved in the PCAP processing pipeline. \ No newline at end of file diff --git a/file-monitor/supervisord.conf b/file-monitor/supervisord.conf index ae9069044..c61eb90c7 100644 --- a/file-monitor/supervisord.conf +++ b/file-monitor/supervisord.conf @@ -17,9 +17,7 @@ supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface serverurl=unix:///tmp/supervisor.sock [program:watcher] -command=/usr/local/bin/zeek_carve_watcher.py - --verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG)s - --extra-verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA)s +command=/usr/local/bin/zeek_carve_watcher.py %(ENV_EXTRACTED_FILE_PIPELINE_VERBOSITY)s --start-sleep %(ENV_EXTRACTED_FILE_WATCHER_START_SLEEP)s --ignore-existing %(ENV_EXTRACTED_FILE_IGNORE_EXISTING)s --polling "%(ENV_EXTRACTED_FILE_WATCHER_POLLING)s" @@ -41,9 +39,7 @@ redirect_stderr=true programs=virustotal,clamav,yara,capa [program:virustotal] -command=/usr/local/bin/vtot_scan.py - --verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG)s - --extra-verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA)s +command=/usr/local/bin/vtot_scan.py %(ENV_EXTRACTED_FILE_PIPELINE_VERBOSITY)s --start-sleep %(ENV_EXTRACTED_FILE_SCANNER_START_SLEEP)s --vtot-api %(ENV_VTOT_API2_KEY)s --req-limit %(ENV_VTOT_REQUESTS_PER_MINUTE)s @@ -58,9 +54,7 @@ stdout_logfile_maxbytes=0 redirect_stderr=true [program:clamav] -command=/usr/local/bin/clam_scan.py - --verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG)s - --extra-verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA)s +command=/usr/local/bin/clam_scan.py %(ENV_EXTRACTED_FILE_PIPELINE_VERBOSITY)s --start-sleep %(ENV_EXTRACTED_FILE_SCANNER_START_SLEEP)s --clamav %(ENV_EXTRACTED_FILE_ENABLE_CLAMAV)s --clamav-socket "%(ENV_CLAMD_SOCKET_FILE)s" @@ -76,9 +70,7 @@ stdout_logfile_maxbytes=0 redirect_stderr=true [program:yara] -command=/usr/local/bin/yara_scan.py - --verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG)s - --extra-verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA)s +command=/usr/local/bin/yara_scan.py %(ENV_EXTRACTED_FILE_PIPELINE_VERBOSITY)s --start-sleep %(ENV_EXTRACTED_FILE_SCANNER_START_SLEEP)s --yara %(ENV_EXTRACTED_FILE_ENABLE_YARA)s --yara-custom-only %(ENV_EXTRACTED_FILE_YARA_CUSTOM_ONLY)s @@ -94,9 +86,7 @@ stdout_logfile_maxbytes=0 redirect_stderr=true [program:capa] -command=/usr/local/bin/capa_scan.py - --verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG)s - --extra-verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA)s +command=/usr/local/bin/capa_scan.py %(ENV_EXTRACTED_FILE_PIPELINE_VERBOSITY)s --start-sleep %(ENV_EXTRACTED_FILE_SCANNER_START_SLEEP)s --capa %(ENV_EXTRACTED_FILE_ENABLE_CAPA)s --capa-verbose %(ENV_EXTRACTED_FILE_CAPA_VERBOSE)s @@ -112,9 +102,7 @@ stdout_logfile_maxbytes=0 redirect_stderr=true [program:logger] -command=/usr/local/bin/zeek_carve_logger.py - --verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG)s - --extra-verbose %(ENV_EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA)s +command=/usr/local/bin/zeek_carve_logger.py %(ENV_EXTRACTED_FILE_PIPELINE_VERBOSITY)s --start-sleep %(ENV_EXTRACTED_FILE_LOGGER_START_SLEEP)s --preserve %(ENV_EXTRACTED_FILE_PRESERVATION)s --directory "%(ENV_ZEEK_EXTRACTOR_PATH)s" diff --git a/filebeat/supervisord.conf b/filebeat/supervisord.conf index f0b2ab382..b81c54ef5 100644 --- a/filebeat/supervisord.conf +++ b/filebeat/supervisord.conf @@ -65,7 +65,7 @@ stdout_logfile_maxbytes=0 redirect_stderr=true [program:watch-upload] -command=python3 /usr/local/bin/filebeat-watch-zeeklogs-uploads-folder.py +command=python3 /usr/local/bin/filebeat-watch-zeeklogs-uploads-folder.py %(ENV_PCAP_PIPELINE_VERBOSITY)s --start-sleep 30 --polling "%(ENV_FILEBEAT_WATCHER_POLLING)s" --closed-sec %(ENV_FILEBEAT_WATCHER_POLLING_ASSUME_CLOSED_SEC)s diff --git a/freq-server/supervisord.conf b/freq-server/supervisord.conf index e6db46e37..1688ae430 100644 --- a/freq-server/supervisord.conf +++ b/freq-server/supervisord.conf @@ -17,7 +17,9 @@ supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface serverurl=unix:///tmp/supervisor.sock [program:freq] -command=/usr/bin/python3 /opt/freq_server/freq_server.py -ip 0.0.0.0 %(ENV_FREQ_API_PORT)s /opt/freq_server/freq_table.freq +command=/usr/bin/python3 /opt/freq_server/freq_server.py + -ip 0.0.0.0 %(ENV_FREQ_API_PORT)s + /opt/freq_server/freq_table.freq startsecs=5 startretries=2000000000 stopasgroup=true diff --git a/pcap-monitor/supervisord.conf b/pcap-monitor/supervisord.conf index 50c232c33..08674792d 100644 --- a/pcap-monitor/supervisord.conf +++ b/pcap-monitor/supervisord.conf @@ -18,7 +18,7 @@ supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface serverurl=unix:///tmp/supervisor.sock [program:watch-upload] -command=python3 /usr/local/bin/watch-pcap-uploads-folder.py +command=python3 /usr/local/bin/watch-pcap-uploads-folder.py %(ENV_PCAP_PIPELINE_VERBOSITY)s --start-sleep 30 --polling "%(ENV_PCAP_PIPELINE_POLLING)s" --closed-sec %(ENV_PCAP_PIPELINE_POLLING_ASSUME_CLOSED_SEC)s @@ -36,9 +36,7 @@ stdout_logfile_maxbytes=0 redirect_stderr=true [program:pcap-publisher] -command=python3 /usr/local/bin/pcap_watcher.py - --verbose "%(ENV_PCAP_PIPELINE_DEBUG)s" - --extra-verbose "%(ENV_PCAP_PIPELINE_DEBUG_EXTRA)s" +command=python3 /usr/local/bin/pcap_watcher.py %(ENV_PCAP_PIPELINE_VERBOSITY)s --opensearch "%(ENV_OPENSEARCH_URL)s" --opensearch-curlrc "%(ENV_OPENSEARCH_CREDS_CONFIG_FILE)s" --opensearch-ssl-verify %(ENV_OPENSEARCH_SSL_CERTIFICATE_VERIFICATION)s diff --git a/shared/bin/pcap_processor.py b/shared/bin/pcap_processor.py index a171cfe6b..17f734e36 100755 --- a/shared/bin/pcap_processor.py +++ b/shared/bin/pcap_processor.py @@ -12,6 +12,7 @@ import argparse import json +import logging import os import shutil import signal @@ -77,9 +78,7 @@ ################################################################################################### -debug = False -verboseDebug = False -debugToggled = False + pdbFlagged = False args = None scriptName = os.path.basename(__file__) @@ -105,19 +104,8 @@ def pdb_handler(sig, frame): pdbFlagged = True -################################################################################################### -# handle sigusr2 for toggling debug -def debug_toggle_handler(signum, frame): - global debug - global debugToggled - debug = not debug - debugToggled = True - - ################################################################################################### def arkimeCaptureFileWorker(arkimeWorkerArgs): - global debug - global verboseDebug global shuttingDown global scanWorkersCount global arkimeProvider @@ -125,17 +113,20 @@ def arkimeCaptureFileWorker(arkimeWorkerArgs): scanWorkerId = scanWorkersCount.increment() # unique ID for this thread - newFileQueue, pcapBaseDir, arkimeBin, nodeName, autoTag, notLocked = ( + newFileQueue, pcapBaseDir, arkimeBin, nodeName, autoTag, notLocked, logger = ( arkimeWorkerArgs[0], arkimeWorkerArgs[1], arkimeWorkerArgs[2], arkimeWorkerArgs[3], arkimeWorkerArgs[4], arkimeWorkerArgs[5], + arkimeWorkerArgs[6], ) - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\tstarted") + if not logger: + logger = logging + + logger.info(f"{scriptName}[{scanWorkerId}]:\tstarted") # loop forever, or until we're told to shut down while not shuttingDown: @@ -160,8 +151,7 @@ def arkimeCaptureFileWorker(arkimeWorkerArgs): if ((FILE_INFO_DICT_TAGS in fileInfo) and autoTag) else list() ) - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\t🔎\t{fileInfo}") + logger.info(f"{scriptName}[{scanWorkerId}]:\t🔎\t{fileInfo}") # put together arkime execution command cmd = [ @@ -182,32 +172,27 @@ def arkimeCaptureFileWorker(arkimeWorkerArgs): cmd.extend(list(chain.from_iterable(zip(repeat('-t'), fileInfo[FILE_INFO_DICT_TAGS])))) # execute capture for pcap file - retcode, output = run_process(cmd, debug=verboseDebug) + retcode, output = run_process(cmd, logger=logger) if retcode == 0: - if debug: - eprint( - f"{scriptName}[{scanWorkerId}]:\t✅\t{os.path.basename(fileInfo[FILE_INFO_DICT_NAME])}" - ) + logger.info( + f"{scriptName}[{scanWorkerId}]:\t✅\t{os.path.basename(fileInfo[FILE_INFO_DICT_NAME])}" + ) else: - if debug: - eprint( - f"{scriptName}[{scanWorkerId}]:\t❗\t{arkimeBin} {os.path.basename(fileInfo[FILE_INFO_DICT_NAME])} returned {retcode} {output if verboseDebug else ''}" - ) + logger.warning( + f"{scriptName}[{scanWorkerId}]:\t❗\t{arkimeBin} {os.path.basename(fileInfo[FILE_INFO_DICT_NAME])} returned {retcode} {output}" + ) - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\tfinished") + logger.info(f"{scriptName}[{scanWorkerId}]:\tfinished") ################################################################################################### def zeekFileWorker(zeekWorkerArgs): - global debug - global verboseDebug global shuttingDown global scanWorkersCount scanWorkerId = scanWorkersCount.increment() # unique ID for this thread - newFileQueue, pcapBaseDir, zeekBin, autoZeek, forceZeek, autoTag, uploadDir, defaultExtractFileMode = ( + newFileQueue, pcapBaseDir, zeekBin, autoZeek, forceZeek, autoTag, uploadDir, defaultExtractFileMode, logger = ( zeekWorkerArgs[0], zeekWorkerArgs[1], zeekWorkerArgs[2], @@ -216,10 +201,13 @@ def zeekFileWorker(zeekWorkerArgs): zeekWorkerArgs[5], zeekWorkerArgs[6], zeekWorkerArgs[7], + zeekWorkerArgs[8], ) - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\tstarted") + if not logger: + logger = logging + + logger.info(f"{scriptName}[{scanWorkerId}]:\tstarted") # loop forever, or until we're told to shut down while not shuttingDown: @@ -271,8 +259,7 @@ def zeekFileWorker(zeekWorkerArgs): if ((FILE_INFO_DICT_TAGS in fileInfo) and autoTag) else list() ) - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\t🔎\t{fileInfo}") + logger.info(f"{scriptName}[{scanWorkerId}]:\t🔎\t{fileInfo}") # create a temporary work directory where zeek will be executed to generate the log files with tempfile.TemporaryDirectory() as tmpLogDir: @@ -292,17 +279,15 @@ def zeekFileWorker(zeekWorkerArgs): # execute zeek with the cwd of tmpLogDir so that's where the logs go, and with the updated file carving environment variable zeekEnv = os.environ.copy() zeekEnv[ZEEK_EXTRACTOR_MODE_ENV_VAR] = extractFileMode - retcode, output = run_process(zeekCmd, cwd=tmpLogDir, env=zeekEnv, debug=verboseDebug) + retcode, output = run_process(zeekCmd, cwd=tmpLogDir, env=zeekEnv, logger=logger) if retcode == 0: - if debug: - eprint( - f"{scriptName}[{scanWorkerId}]:\t✅\t{os.path.basename(fileInfo[FILE_INFO_DICT_NAME])}" - ) + logger.info( + f"{scriptName}[{scanWorkerId}]:\t✅\t{os.path.basename(fileInfo[FILE_INFO_DICT_NAME])}" + ) else: - if debug: - eprint( - f"{scriptName}[{scanWorkerId}]:\t❗\t{zeekBin} {os.path.basename(fileInfo[FILE_INFO_DICT_NAME])} returned {retcode} {output if verboseDebug else ''}" - ) + logger.info( + f"{scriptName}[{scanWorkerId}]:\t❗\t{zeekBin} {os.path.basename(fileInfo[FILE_INFO_DICT_NAME])} returned {retcode} {output}" + ) # clean up the .state directory we don't care to keep tmpStateDir = os.path.join(tmpLogDir, ZEEK_STATE_DIR) @@ -330,36 +315,30 @@ def zeekFileWorker(zeekWorkerArgs): # the way Docker volume mounts work, ie. avoid "OSError: [Errno 18] Invalid cross-device link"). # we don't have to explicitly delete it since this whole directory is about to leave context and be removed shutil.copy(tgzFileName, uploadDir) - if verboseDebug: - eprint(f"{scriptName}[{scanWorkerId}]:\t⏩\t{tgzFileName} → {uploadDir}") + logger.debug(f"{scriptName}[{scanWorkerId}]:\t⏩\t{tgzFileName} → {uploadDir}") else: # zeek returned no log files (or an error) - if debug: - eprint( - f"{scriptName}[{scanWorkerId}]:\t❓\t{zeekBin} {os.path.basename(fileInfo[FILE_INFO_DICT_NAME])} generated no log files" - ) + logger.warning( + f"{scriptName}[{scanWorkerId}]:\t❓\t{zeekBin} {os.path.basename(fileInfo[FILE_INFO_DICT_NAME])} generated no log files" + ) else: - if debug: - eprint( - f"{scriptName}[{scanWorkerId}]:\t❗\terror creating temporary directory {tmpLogDir}" - ) + logger.warning( + f"{scriptName}[{scanWorkerId}]:\t❗\terror creating temporary directory {tmpLogDir}" + ) - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\tfinished") + logger.info(f"{scriptName}[{scanWorkerId}]:\tfinished") ################################################################################################### def suricataFileWorker(suricataWorkerArgs): - global debug - global verboseDebug global shuttingDown global scanWorkersCount scanWorkerId = scanWorkersCount.increment() # unique ID for this thread - newFileQueue, pcapBaseDir, autoSuricata, forceSuricata, suricataBin, autoTag, uploadDir, suricataConfig = ( + newFileQueue, pcapBaseDir, autoSuricata, forceSuricata, suricataBin, autoTag, uploadDir, suricataConfig, logger = ( suricataWorkerArgs[0], suricataWorkerArgs[1], suricataWorkerArgs[2], @@ -368,10 +347,13 @@ def suricataFileWorker(suricataWorkerArgs): suricataWorkerArgs[5], suricataWorkerArgs[6], suricataWorkerArgs[7], + suricataWorkerArgs[8], ) - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\tstarted") + if not logger: + logger = logging + + logger.info(f"{scriptName}[{scanWorkerId}]:\tstarted") # loop forever, or until we're told to shut down while not shuttingDown: @@ -413,8 +395,7 @@ def suricataFileWorker(suricataWorkerArgs): if ((FILE_INFO_DICT_TAGS in fileInfo) and autoTag) else list() ) - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\t🔎\t{fileInfo}") + logger.info(f"{scriptName}[{scanWorkerId}]:\t🔎\t{fileInfo}") # create a temporary work directory where suricata will be executed to generate the log files with tempfile.TemporaryDirectory() as tmpLogDir: @@ -433,7 +414,7 @@ def suricataFileWorker(suricataWorkerArgs): ] # execute suricata-capture for pcap file - retcode, output = run_process(cmd, debug=verboseDebug) + retcode, output = run_process(cmd, logger=logger) eveJsonFile = os.path.join(tmpLogDir, "eve.json") if os.path.isfile(eveJsonFile): @@ -449,24 +430,20 @@ def suricataFileWorker(suricataWorkerArgs): ) if retcode == 0: - if debug: - eprint( - f"{scriptName}[{scanWorkerId}]:\t✅\t{os.path.basename(fileInfo[FILE_INFO_DICT_NAME])}" - ) + logger.info( + f"{scriptName}[{scanWorkerId}]:\t✅\t{os.path.basename(fileInfo[FILE_INFO_DICT_NAME])}" + ) else: - if debug: - eprint( - f"{scriptName}[{scanWorkerId}]:\t❗\t{suricataBin} {os.path.basename(fileInfo[FILE_INFO_DICT_NAME])} returned {retcode} {output if verboseDebug else ''}" - ) + logger.warning( + f"{scriptName}[{scanWorkerId}]:\t❗\t{suricataBin} {os.path.basename(fileInfo[FILE_INFO_DICT_NAME])} returned {retcode} {output}" + ) else: - if debug: - eprint( - f"{scriptName}[{scanWorkerId}]:\t❗\terror creating temporary directory {tmpLogDir}" - ) + logger.warning( + f"{scriptName}[{scanWorkerId}]:\t❗\terror creating temporary directory {tmpLogDir}" + ) - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\tfinished") + logger.info(f"{scriptName}[{scanWorkerId}]:\tfinished") ################################################################################################### @@ -491,36 +468,11 @@ def main(): exit(2) global args - global debug - global debugToggled global pdbFlagged global shuttingDown - global verboseDebug parser = argparse.ArgumentParser(description=scriptName, add_help=False, usage='{} '.format(scriptName)) - parser.add_argument( - '-v', - '--verbose', - dest='debug', - help="Verbose output", - metavar='true|false', - type=str2bool, - nargs='?', - const=True, - default=False, - required=False, - ) - parser.add_argument( - '--extra-verbose', - dest='verboseDebug', - help="Super verbose output", - metavar='true|false', - type=str2bool, - nargs='?', - const=True, - default=False, - required=False, - ) + parser.add_argument('--verbose', '-v', action='count', default=1, help='Increase verbosity (e.g., -v, -vv, etc.)') parser.add_argument( '--start-sleep', dest='startSleepSec', @@ -702,20 +654,20 @@ def main(): parser.print_help() exit(2) - verboseDebug = args.verboseDebug - debug = args.debug or verboseDebug - if debug: - eprint(os.path.join(scriptPath, scriptName)) - eprint("{} arguments: {}".format(scriptName, sys.argv[1:])) - eprint("{} arguments: {}".format(scriptName, args)) - else: + args.verbose = logging.ERROR - (10 * args.verbose) if args.verbose > 0 else 0 + logging.basicConfig( + level=args.verbose, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' + ) + logging.info(os.path.join(scriptPath, scriptName)) + logging.info("Arguments: {}".format(sys.argv[1:])) + logging.info("Arguments: {}".format(args)) + if args.verbose > logging.DEBUG: sys.tracebacklimit = 0 # handle sigint and sigterm for graceful shutdown signal.signal(signal.SIGINT, shutdown_handler) signal.signal(signal.SIGTERM, shutdown_handler) signal.signal(signal.SIGUSR1, pdb_handler) - signal.signal(signal.SIGUSR2, debug_toggle_handler) # sleep for a bit if requested sleepCount = 0 @@ -732,8 +684,7 @@ def main(): new_files_socket.setsockopt(zmq.SUBSCRIBE, b"") # All topics new_files_socket.setsockopt(zmq.LINGER, 0) # All topics new_files_socket.RCVTIMEO = 1500 - if debug: - eprint(f"{scriptName}:\tsubscribed to topic at {PCAP_TOPIC_PORT}") + logging.info(f"{scriptName}:\tsubscribed to topic at {PCAP_TOPIC_PORT}") # we'll pull from the topic in the main thread and queue them for processing by the worker threads newFileQueue = deque() @@ -751,6 +702,7 @@ def main(): args.nodeName, args.autoTag, args.notLocked, + logging, ], ), ) @@ -768,6 +720,7 @@ def main(): args.autoTag, args.zeekUploadDir, args.zeekExtractFileMode, + logging, ], ), ) @@ -785,6 +738,7 @@ def main(): args.autoTag, args.suricataUploadDir, args.suricataConfigFile, + logging, ], ), ) @@ -800,19 +754,16 @@ def main(): fileInfo = json.loads(new_files_socket.recv_string()) except zmq.Again: # no file received due to timeout, we'll go around and try again - if verboseDebug: - eprint(f"{scriptName}:\t🕑\t(recv)") + logging.debug(f"{scriptName}:\t🕑\t(recv)") fileInfo = None if isinstance(fileInfo, dict) and (FILE_INFO_DICT_NAME in fileInfo): # queue for the workers to process with capture newFileQueue.append(fileInfo) - if debug: - eprint(f"{scriptName}:\t📨\t{fileInfo}") + logging.info(f"{scriptName}:\t📨\t{fileInfo}") # graceful shutdown - if debug: - eprint(f"{scriptName}: shutting down...") + logging.info(f"{scriptName}: shutting down...") time.sleep(5) diff --git a/shared/bin/zeek_carve_logger.py b/shared/bin/zeek_carve_logger.py index 8e6c5dc21..a9a1d9112 100755 --- a/shared/bin/zeek_carve_logger.py +++ b/shared/bin/zeek_carve_logger.py @@ -12,6 +12,7 @@ import argparse import json import os +import logging import pathlib import re import shutil @@ -43,12 +44,9 @@ ) import malcolm_utils -from malcolm_utils import eprint, str2bool, AtomicInt, same_file_or_dir +from malcolm_utils import str2bool, AtomicInt, same_file_or_dir ################################################################################################### -debug = False -verboseDebug = False -debugToggled = False pdbFlagged = False args = None scriptName = os.path.basename(__file__) @@ -71,49 +69,15 @@ def pdb_handler(sig, frame): pdbFlagged = True -################################################################################################### -# handle sigusr2 for toggling debug -def debug_toggle_handler(signum, frame): - global debug - global debugToggled - debug = not debug - debugToggled = True - - ################################################################################################### # main def main(): global args - global debug - global verboseDebug - global debugToggled global pdbFlagged global shuttingDown parser = argparse.ArgumentParser(description=scriptName, add_help=False, usage='{} '.format(scriptName)) - parser.add_argument( - '-v', - '--verbose', - dest='debug', - help="Verbose output", - metavar='true|false', - type=str2bool, - nargs='?', - const=True, - default=False, - required=False, - ) - parser.add_argument( - '--extra-verbose', - dest='verboseDebug', - help="Super verbose output", - metavar='true|false', - type=str2bool, - nargs='?', - const=True, - default=False, - required=False, - ) + parser.add_argument('--verbose', '-v', action='count', default=1, help='Increase verbosity (e.g., -v, -vv, etc.)') parser.add_argument( '--start-sleep', dest='startSleepSec', @@ -158,13 +122,14 @@ def main(): parser.print_help() exit(2) - verboseDebug = args.verboseDebug - debug = args.debug or verboseDebug - if debug: - eprint(os.path.join(scriptPath, scriptName)) - eprint("{} arguments: {}".format(scriptName, sys.argv[1:])) - eprint("{} arguments: {}".format(scriptName, args)) - else: + args.verbose = logging.ERROR - (10 * args.verbose) if args.verbose > 0 else 0 + logging.basicConfig( + level=args.verbose, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' + ) + logging.info(os.path.join(scriptPath, scriptName)) + logging.info("Arguments: {}".format(sys.argv[1:])) + logging.info("Arguments: {}".format(args)) + if args.verbose > logging.DEBUG: sys.tracebacklimit = 0 # determine what to do with scanned files (preserve only "hits", preserve all, preserve none) @@ -172,14 +137,13 @@ def main(): if len(args.preserveMode) == 0: args.preserveMode = PRESERVE_QUARANTINED elif args.preserveMode not in [PRESERVE_QUARANTINED, PRESERVE_ALL, PRESERVE_NONE]: - eprint(f'Invalid file preservation mode "{args.preserveMode}"') + logging.error(f'Invalid file preservation mode "{args.preserveMode}"') sys.exit(1) # handle sigint and sigterm for graceful shutdown signal.signal(signal.SIGINT, shutdown_handler) signal.signal(signal.SIGTERM, shutdown_handler) signal.signal(signal.SIGUSR1, pdb_handler) - signal.signal(signal.SIGUSR2, debug_toggle_handler) # sleep for a bit if requested sleepCount = 0 @@ -201,12 +165,10 @@ def main(): quarantineDir = os.path.join(args.baseDir, PRESERVE_QUARANTINED_DIR_NAME) preserveDir = os.path.join(args.baseDir, PRESERVE_PRESERVED_DIR_NAME) if (args.preserveMode != PRESERVE_NONE) and (not os.path.isdir(quarantineDir)): - if debug: - eprint(f'Creating "{quarantineDir}" for quarantined files') + logging.info(f'Creating "{quarantineDir}" for quarantined files') pathlib.Path(quarantineDir).mkdir(parents=False, exist_ok=True) if (args.preserveMode == PRESERVE_ALL) and (not os.path.isdir(preserveDir)): - if debug: - eprint(f'Creating "{preserveDir}" for other preserved files') + logging.info(f'Creating "{preserveDir}" for other preserved files') pathlib.Path(preserveDir).mkdir(parents=False, exist_ok=True) # initialize ZeroMQ context and socket(s) to send messages to @@ -218,8 +180,7 @@ def main(): scanned_files_socket.SNDTIMEO = 5000 scanned_files_socket.RCVTIMEO = 5000 - if debug: - eprint(f"{scriptName}: bound sink port {SINK_PORT}") + logging.info(f"{scriptName}: bound sink port {SINK_PORT}") scanners = set() fileScanCounts = defaultdict(AtomicInt) @@ -253,27 +214,24 @@ def main(): triggered = False try: scanResult = json.loads(scanned_files_socket.recv_string()) - if debug: - eprint(f"{scriptName}:\t📨\t{scanResult}") + logging.info(f"{scriptName}:\t📨\t{scanResult}") except zmq.Again: scanResult = None - if verboseDebug: - eprint(f"{scriptName}:\t🕑\t(recv)") + logging.debug(f"{scriptName}:\t🕑\t(recv)") if isinstance(scanResult, dict): # register/deregister scanners if FILE_SCAN_RESULT_SCANNER in scanResult: scanner = scanResult[FILE_SCAN_RESULT_SCANNER].lower() if scanner.startswith('-'): - if debug: - eprint(f"{scriptName}:\t🙃\t{scanner[1:]}") + logging.info(f"{scriptName}:\t🙃\t{scanner[1:]}") try: scanners.remove(scanner[1:]) except KeyError: pass else: - if debug and (scanner not in scanners): - eprint(f"{scriptName}:\t🇷\t{scanner}") + if scanner not in scanners: + logging.info(f"{scriptName}:\t🇷\t{scanner}") scanners.add(scanner) # process scan results @@ -338,10 +296,9 @@ def main(): ): # unless it's somehow already there try: shutil.move(fileName, quarantineDir) - if debug: - eprint(f"{scriptName}:\t⏩\t{fileName} ({fileScanCount}/{len(scanners)})") + logging.info(f"{scriptName}:\t⏩\t{fileName} ({fileScanCount}/{len(scanners)})") except Exception as e: - eprint(f"{scriptName}:\t❗\t🚫\t{fileName} move exception: {e}") + logging.warning(f"{scriptName}:\t❗\t🚫\t{fileName} move exception: {e}") # hm move failed, delete it i guess? os.remove(fileName) @@ -353,24 +310,21 @@ def main(): # move non-triggering file to preserved directory try: shutil.move(fileName, preserveDir) - if verboseDebug: - eprint( - f"{scriptName}:\t⏩\t{fileName} ({fileScanCount}/{len(scanners)})" - ) + logging.debug( + f"{scriptName}:\t⏩\t{fileName} ({fileScanCount}/{len(scanners)})" + ) except Exception as e: - eprint(f"{scriptName}:\t❗\t🚫\t{fileName} move exception: {e}") + logging.warning(f"{scriptName}:\t❗\t🚫\t{fileName} move exception: {e}") # hm move failed, delete it i guess? os.remove(fileName) else: # delete the file os.remove(fileName) - if verboseDebug: - eprint(f"{scriptName}:\t🚫\t{fileName} ({fileScanCount}/{len(scanners)})") + logging.debug(f"{scriptName}:\t🚫\t{fileName} ({fileScanCount}/{len(scanners)})") # graceful shutdown - if debug: - eprint(f"{scriptName}: shutting down...") + logging.info(f"{scriptName}: shutting down...") if __name__ == '__main__': diff --git a/shared/bin/zeek_carve_scanner.py b/shared/bin/zeek_carve_scanner.py index 3d4e61a48..35d56f7bd 100755 --- a/shared/bin/zeek_carve_scanner.py +++ b/shared/bin/zeek_carve_scanner.py @@ -15,6 +15,7 @@ import pathlib import json import signal +import logging import sys import threading import time @@ -57,9 +58,6 @@ ################################################################################################### -debug = False -verboseDebug = False -debugToggled = False pdbFlagged = False args = None scriptName = os.path.basename(__file__) @@ -83,20 +81,9 @@ def pdb_handler(sig, frame): pdbFlagged = True -################################################################################################### -# handle sigusr2 for toggling debug -def debug_toggle_handler(signum, frame): - global debug - global debugToggled - debug = not debug - debugToggled = True - - ################################################################################################### # look for a file to scan (probably in its original directory, but possibly already moved to quarantine) def locate_file(fileInfo): - global verboseDebug - if isinstance(fileInfo, dict) and (FILE_SCAN_RESULT_FILE in fileInfo): fileName = fileInfo[FILE_SCAN_RESULT_FILE] elif isinstance(fileInfo, str): @@ -114,8 +101,7 @@ def locate_file(fileInfo): os.path.join(os.path.dirname(os.path.realpath(fileName)), testPath), os.path.basename(fileName) ) if os.path.isfile(testFileName): - if verboseDebug: - eprint(f"{scriptName}:\t⏩\t{testFileName}") + logging.debug(f"{scriptName}:\t⏩\t{testFileName}") return testFileName return None @@ -123,16 +109,13 @@ def locate_file(fileInfo): ################################################################################################### def scanFileWorker(checkConnInfo, carvedFileSub): - global debug - global verboseDebug global shuttingDown global scanWorkersCount scanWorkerId = scanWorkersCount.increment() # unique ID for this thread scannerRegistered = False - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\tstarted") + logging.info(f"{scriptName}[{scanWorkerId}]:\tstarted") try: if isinstance(checkConnInfo, FileScanProvider): @@ -144,8 +127,7 @@ def scanFileWorker(checkConnInfo, carvedFileSub): scanned_files_socket.connect(f"tcp://localhost:{SINK_PORT}") # todo: do I want to set this? probably not, since what else would we do if we can't send? just block # scanned_files_socket.SNDTIMEO = 5000 - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\tconnected to sink at {SINK_PORT}") + logging.info(f"{scriptName}[{scanWorkerId}]:\tconnected to sink at {SINK_PORT}") fileInfo = None fileName = None @@ -160,13 +142,11 @@ def scanFileWorker(checkConnInfo, carvedFileSub): json.dumps({FILE_SCAN_RESULT_SCANNER: checkConnInfo.scanner_name()}) ) scannerRegistered = True - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\t🇷\t{checkConnInfo.scanner_name()}") + logging.info(f"{scriptName}[{scanWorkerId}]:\t🇷\t{checkConnInfo.scanner_name()}") except zmq.Again: # todo: what to do here? - if verboseDebug: - eprint(f"{scriptName}[{scanWorkerId}]:\t🕑\t{checkConnInfo.scanner_name()} 🇷") + logging.debug(f"{scriptName}[{scanWorkerId}]:\t🕑\t{checkConnInfo.scanner_name()} 🇷") if shuttingDown: break @@ -174,8 +154,7 @@ def scanFileWorker(checkConnInfo, carvedFileSub): if retrySubmitFile and (fileInfo is not None) and (locate_file(fileInfo) is not None): # we were unable to submit the file for processing, so try again time.sleep(1) - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\t🔃\t{json.dumps(fileInfo)}") + logging.info(f"{scriptName}[{scanWorkerId}]:\t🔃\t{json.dumps(fileInfo)}") else: retrySubmitFile = False @@ -185,8 +164,7 @@ def scanFileWorker(checkConnInfo, carvedFileSub): fileName = locate_file(fileInfo) if (fileName is not None) and os.path.isfile(fileName): # file exists, submit for scanning - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\t🔎\t{json.dumps(fileInfo)}") + logging.info(f"{scriptName}[{scanWorkerId}]:\t🔎\t{json.dumps(fileInfo)}") requestComplete = False scanResult = None fileSize = ( @@ -211,8 +189,7 @@ def scanFileWorker(checkConnInfo, carvedFileSub): ), ) if scan.submissionResponse is not None: - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\t🔍\t{fileName}") + logging.info(f"{scriptName}[{scanWorkerId}]:\t🔍\t{fileName}") # file was successfully submitted and is now being scanned retrySubmitFile = False @@ -257,13 +234,11 @@ def scanFileWorker(checkConnInfo, carvedFileSub): try: # Send results to sink scanned_files_socket.send_string(json.dumps(scan.provider.format(fileName, scanResult))) - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\t✅\t{fileName}") + logging.info(f"{scriptName}[{scanWorkerId}]:\t✅\t{fileName}") except zmq.Again: # todo: what to do here? - if verboseDebug: - eprint(f"{scriptName}[{scanWorkerId}]:\t🕑\t{fileName}") + logging.debug(f"{scriptName}[{scanWorkerId}]:\t🕑\t{fileName}") else: eprint(f"{scriptName}[{scanWorkerId}]:\tinvalid scanner provider specified") @@ -276,51 +251,23 @@ def scanFileWorker(checkConnInfo, carvedFileSub): json.dumps({FILE_SCAN_RESULT_SCANNER: f"-{checkConnInfo.scanner_name()}"}) ) scannerRegistered = False - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\t🙃\t{checkConnInfo.scanner_name()}") + logging.info(f"{scriptName}[{scanWorkerId}]:\t🙃\t{checkConnInfo.scanner_name()}") except zmq.Again: # todo: what to do here? - if verboseDebug: - eprint(f"{scriptName}[{scanWorkerId}]:\t🕑\t{checkConnInfo.scanner_name()} 🙃") + logging.debug(f"{scriptName}[{scanWorkerId}]:\t🕑\t{checkConnInfo.scanner_name()} 🙃") - if debug: - eprint(f"{scriptName}[{scanWorkerId}]:\tfinished") + logging.info(f"{scriptName}[{scanWorkerId}]:\tfinished") ################################################################################################### # main def main(): global args - global debug - global debugToggled global pdbFlagged global shuttingDown - global verboseDebug parser = argparse.ArgumentParser(description=scriptName, add_help=False, usage='{} '.format(scriptName)) - parser.add_argument( - '-v', - '--verbose', - dest='debug', - help="Verbose output", - metavar='true|false', - type=str2bool, - nargs='?', - const=True, - default=False, - required=False, - ) - parser.add_argument( - '--extra-verbose', - dest='verboseDebug', - help="Super verbose output", - metavar='true|false', - type=str2bool, - nargs='?', - const=True, - default=False, - required=False, - ) + parser.add_argument('--verbose', '-v', action='count', default=1, help='Increase verbosity (e.g., -v, -vv, etc.)') parser.add_argument( '--start-sleep', dest='startSleepSec', @@ -417,20 +364,20 @@ def main(): parser.print_help() exit(2) - verboseDebug = args.verboseDebug - debug = args.debug or verboseDebug - if debug: - eprint(os.path.join(scriptPath, scriptName)) - eprint("{} arguments: {}".format(scriptName, sys.argv[1:])) - eprint("{} arguments: {}".format(scriptName, args)) - else: + args.verbose = logging.ERROR - (10 * args.verbose) if args.verbose > 0 else 0 + logging.basicConfig( + level=args.verbose, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' + ) + logging.info(os.path.join(scriptPath, scriptName)) + logging.info("Arguments: {}".format(sys.argv[1:])) + logging.info("Arguments: {}".format(args)) + if args.verbose > logging.DEBUG: sys.tracebacklimit = 0 # handle sigint and sigterm for graceful shutdown signal.signal(signal.SIGINT, shutdown_handler) signal.signal(signal.SIGTERM, shutdown_handler) signal.signal(signal.SIGUSR1, pdb_handler) - signal.signal(signal.SIGUSR2, debug_toggle_handler) # sleep for a bit if requested sleepCount = 0 @@ -446,11 +393,14 @@ def main(): if not args.yaraCustomOnly: yaraDirs.append(YARA_RULES_DIR) yaraDirs.append(YARA_CUSTOM_RULES_DIR) - checkConnInfo = YaraScan(debug=debug, verboseDebug=verboseDebug, rulesDirs=yaraDirs, reqLimit=args.reqLimit) + checkConnInfo = YaraScan( + logger=logging, + rulesDirs=yaraDirs, + reqLimit=args.reqLimit, + ) elif args.enableCapa: checkConnInfo = CapaScan( - debug=debug, - verboseDebug=verboseDebug, + logger=logging, rulesDir=args.capaRulesDir, verboseHits=args.capaVerbose, reqLimit=args.reqLimit, @@ -459,11 +409,16 @@ def main(): if not args.enableClamAv: eprint('No scanner specified, defaulting to ClamAV') checkConnInfo = ClamAVScan( - debug=debug, verboseDebug=verboseDebug, socketFileName=args.clamAvSocket, reqLimit=args.reqLimit + logger=logging, + socketFileName=args.clamAvSocket, + reqLimit=args.reqLimit, ) carvedFileSub = CarvedFileSubscriberThreaded( - debug=debug, verboseDebug=verboseDebug, host='localhost', port=VENTILATOR_PORT, scriptName=scriptName + logger=logging, + host='localhost', + port=VENTILATOR_PORT, + scriptName=scriptName, ) # start scanner threads which will pull filenames to be scanned and send the results to the logger diff --git a/shared/bin/zeek_carve_utils.py b/shared/bin/zeek_carve_utils.py index ebde15b34..72c7f9d8e 100644 --- a/shared/bin/zeek_carve_utils.py +++ b/shared/bin/zeek_carve_utils.py @@ -4,6 +4,7 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. import clamd +import logging import json import os import re @@ -279,8 +280,7 @@ class CarvedFileSubscriberThreaded: # constructor def __init__( self, - debug=False, - verboseDebug=False, + logger=None, host="localhost", port=VENTILATOR_PORT, context=None, @@ -288,8 +288,7 @@ def __init__( rcvTimeout=5000, scriptName='', ): - self.debug = debug - self.verboseDebug = verboseDebug + self.logger = logger if logger else logging self.scriptName = scriptName self.lock = Lock() @@ -302,11 +301,10 @@ def __init__( self.newFilesSocket.connect(f"tcp://{host}:{port}") self.newFilesSocket.setsockopt(zmq.SUBSCRIBE, bytes(topic, encoding='ascii')) self.newFilesSocket.RCVTIMEO = rcvTimeout - if self.debug: - eprint( - f"{self.scriptName}:\tbound to ventilator at {port}", - timestamp=True, - ) + self.logger.info( + f"{self.scriptName}:\tbound to ventilator at {port}", + timestamp=True, + ) # --------------------------------------------------------------------------------- def Pull(self, scanWorkerId=0): @@ -320,11 +318,10 @@ def Pull(self, scanWorkerId=0): # no file received due to timeout, return empty dict. which means no file available pass - if self.verboseDebug: - eprint( - f"{self.scriptName}[{scanWorkerId}]:\t{'📨' if (FILE_SCAN_RESULT_FILE in fileinfo) else '🕑'}\t{fileinfo[FILE_SCAN_RESULT_FILE] if (FILE_SCAN_RESULT_FILE in fileinfo) else '(recv)'}", - timestamp=True, - ) + self.logger.debug( + f"{self.scriptName}[{scanWorkerId}]:\t{'📨' if (FILE_SCAN_RESULT_FILE in fileinfo) else '🕑'}\t{fileinfo[FILE_SCAN_RESULT_FILE] if (FILE_SCAN_RESULT_FILE in fileinfo) else '(recv)'}", + timestamp=True, + ) return fileinfo @@ -519,10 +516,14 @@ def format(fileName, response): class ClamAVScan(FileScanProvider): # --------------------------------------------------------------------------------- # constructor - def __init__(self, debug=False, verboseDebug=False, socketFileName=None, reqLimit=None): + def __init__( + self, + logger=None, + socketFileName=None, + reqLimit=None, + ): self.scanningFilesCount = AtomicInt(value=0) - self.debug = debug - self.verboseDebug = verboseDebug + self.logger = logger if logger else logging self.socketFileName = socketFileName self.reqLimit = reqLimit if reqLimit else CLAM_MAX_REQS @@ -552,11 +553,10 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo nowTime = int(time.time()) if not connected: - if self.verboseDebug: - eprint( - f"{get_ident()}: ClamAV attempting connection", - timestamp=True, - ) + self.logger.debug( + f"{get_ident()}: ClamAV attempting connection", + timestamp=True, + ) clamAv = ( clamd.ClamdUnixSocket(path=self.socketFileName) if self.socketFileName is not None @@ -565,18 +565,16 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo try: clamAv.ping() connected = True - if self.verboseDebug: - eprint( - f"{get_ident()}: ClamAV connected!", - timestamp=True, - ) + self.logger.debug( + f"{get_ident()}: ClamAV connected!", + timestamp=True, + ) except Exception as e: connected = False - if self.debug: - eprint( - f"{get_ident()}: ClamAV connection failed: {str(e)}", - timestamp=True, - ) + self.logger.info( + f"{get_ident()}: ClamAV connection failed: {str(e)}", + timestamp=True, + ) if connected: # first make sure we haven't exceeded rate limits @@ -588,27 +586,24 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo if connected and allowed: try: - if self.verboseDebug: - eprint( - f'{get_ident()} ClamAV scanning: {fileName}', - timestamp=True, - ) + self.logger.debug( + f'{get_ident()} ClamAV scanning: {fileName}', + timestamp=True, + ) clamavResult.result = clamAv.scan(fileName) - if self.verboseDebug: - eprint( - f'{get_ident()} ClamAV scan result: {clamavResult.result}', - timestamp=True, - ) + self.logger.debug( + f'{get_ident()} ClamAV scan result: {clamavResult.result}', + timestamp=True, + ) clamavResult.success = clamavResult.result is not None clamavResult.finished = True except Exception as e: if clamavResult.result is None: clamavResult.result = str(e) - if self.debug: - eprint( - f'{get_ident()} ClamAV scan error: {clamavResult.result}', - timestamp=True, - ) + self.logger.info( + f'{get_ident()} ClamAV scan error: {clamavResult.result}', + timestamp=True, + ) finally: self.scanningFilesCount.decrement() @@ -673,10 +668,14 @@ def format(fileName, response): class YaraScan(FileScanProvider): # --------------------------------------------------------------------------------- # constructor - def __init__(self, debug=False, verboseDebug=False, rulesDirs=[], reqLimit=None): + def __init__( + self, + logger=None, + rulesDirs=[], + reqLimit=None, + ): self.scanningFilesCount = AtomicInt(value=0) - self.debug = debug - self.verboseDebug = verboseDebug + self.logger = logger if logger else logging self.reqLimit = reqLimit if reqLimit else YARA_MAX_REQS self.ruleFilespecs = {} for yaraDir in rulesDirs: @@ -690,21 +689,19 @@ def __init__(self, debug=False, verboseDebug=False, rulesDirs=[], reqLimit=None) yara.compile(filename) self.ruleFilespecs[filename] = filename except yara.SyntaxError as e: - if self.debug: - eprint( - f'{get_ident()} Ignored Yara compile error in {filename}: {e}', - timestamp=True, - ) - if self.verboseDebug: - eprint( - f"{get_ident()}: Initializing Yara with {len(self.ruleFilespecs)} rules files: {self.ruleFilespecs}", - timestamp=True, - ) - elif self.debug: - eprint( - f"{get_ident()}: Initializing Yara with {len(self.ruleFilespecs)} rules files", - timestamp=True, - ) + self.logger.info( + f'{get_ident()} Ignored Yara compile error in {filename}: {e}', + timestamp=True, + ) + self.logger.info( + f"{get_ident()}: Initializing Yara with {len(self.ruleFilespecs)} rules files", + timestamp=True, + ) + self.logger.debug( + f"{get_ident()}: Initializing Yara with {len(self.ruleFilespecs)} rules files: {self.ruleFilespecs}", + timestamp=True, + ) + self.compiledRules = yara.compile(filepaths=self.ruleFilespecs) @staticmethod @@ -740,17 +737,15 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo if allowed: try: - if self.verboseDebug: - eprint( - f'{get_ident()} Yara scanning: {fileName}', - timestamp=True, - ) + self.logger.debug( + f'{get_ident()} Yara scanning: {fileName}', + timestamp=True, + ) yaraResult.result = self.compiledRules.match(fileName, timeout=YARA_RUN_TIMEOUT_SEC) - if self.verboseDebug: - eprint( - f'{get_ident()} Yara scan result: {yaraResult.result}', - timestamp=True, - ) + self.logger.debug( + f'{get_ident()} Yara scan result: {yaraResult.result}', + timestamp=True, + ) yaraResult.success = yaraResult.result is not None yaraResult.finished = True except Exception as e: @@ -758,11 +753,10 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo yaraResult.result = {"error": str(e)} yaraResult.success = False yaraResult.finished = True - if self.debug: - eprint( - f'{get_ident()} Yara scan error: {yaraResult.result}', - timestamp=True, - ) + self.logger.info( + f'{get_ident()} Yara scan error: {yaraResult.result}', + timestamp=True, + ) finally: self.scanningFilesCount.decrement() @@ -824,11 +818,16 @@ def format(fileName, response): class CapaScan(FileScanProvider): # --------------------------------------------------------------------------------- # constructor - def __init__(self, debug=False, verboseDebug=False, rulesDir=None, verboseHits=False, reqLimit=None): + def __init__( + self, + logger=None, + rulesDir=None, + verboseHits=False, + reqLimit=None, + ): self.scanningFilesCount = AtomicInt(value=0) self.rulesDir = rulesDir - self.debug = debug - self.verboseDebug = verboseDebug + self.logger = logger if logger else logging self.verboseHits = verboseHits self.reqLimit = reqLimit if reqLimit else CAPA_MAX_REQS @@ -867,11 +866,10 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo if allowed: try: - if self.verboseDebug: - eprint( - f'{get_ident()} Capa scanning: {fileName}', - timestamp=True, - ) + self.logger.debug( + f'{get_ident()} Capa scanning: {fileName}', + timestamp=True, + ) if self.rulesDir is not None: cmd = [ @@ -905,7 +903,7 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo 'never', fileName, ] - capaErr, capaOut = run_process(cmd, stderr=False, debug=self.debug) + capaErr, capaOut = run_process(cmd, stderr=False, logger=self.logger) if (capaErr == 0) and (len(capaOut) > 0) and (len(capaOut[0]) > 0): # load the JSON output from capa into the .result try: @@ -917,22 +915,20 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo # probably failed because it's not an executable, ignore it capaResult.result = {"error": str(capaErr)} - if self.verboseDebug: - eprint( - f'{get_ident()} Capa scan result: {capaResult.result}', - timestamp=True, - ) + self.logger.debug( + f'{get_ident()} Capa scan result: {capaResult.result}', + timestamp=True, + ) capaResult.success = capaResult.result is not None capaResult.finished = True except Exception as e: if capaResult.result is None: capaResult.result = str(e) - if self.debug: - eprint( - f'{get_ident()} Capa scan error: {capaResult.result}', - timestamp=True, - ) + self.logger.debug( + f'{get_ident()} Capa scan error: {capaResult.result}', + timestamp=True, + ) finally: self.scanningFilesCount.decrement() diff --git a/shared/bin/zeek_carve_watcher.py b/shared/bin/zeek_carve_watcher.py index 5528bd214..e2b93590a 100755 --- a/shared/bin/zeek_carve_watcher.py +++ b/shared/bin/zeek_carve_watcher.py @@ -12,6 +12,7 @@ import argparse import glob import json +import logging import magic import os import pathlib diff --git a/suricata/supervisord.conf b/suricata/supervisord.conf index ac0d96bd2..dae335106 100644 --- a/suricata/supervisord.conf +++ b/suricata/supervisord.conf @@ -18,9 +18,7 @@ supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface serverurl=unix:///tmp/supervisor.sock [program:pcap-suricata] -command=python3 /usr/local/bin/pcap_suricata_processor.py - --verbose "%(ENV_PCAP_PIPELINE_DEBUG)s" - --extra-verbose "%(ENV_PCAP_PIPELINE_DEBUG_EXTRA)s" +command=python3 /usr/local/bin/pcap_suricata_processor.py %(ENV_PCAP_PIPELINE_VERBOSITY)s --start-sleep 10 --threads %(ENV_SURICATA_AUTO_ANALYZE_PCAP_THREADS)s --publisher "%(ENV_PCAP_MONITOR_HOST)s" diff --git a/zeek/supervisord.conf b/zeek/supervisord.conf index babbd790b..e765007b6 100644 --- a/zeek/supervisord.conf +++ b/zeek/supervisord.conf @@ -18,9 +18,7 @@ supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface serverurl=unix:///tmp/supervisor.sock [program:pcap-zeek] -command=python3 /usr/local/bin/pcap_zeek_processor.py - --verbose "%(ENV_PCAP_PIPELINE_DEBUG)s" - --extra-verbose "%(ENV_PCAP_PIPELINE_DEBUG_EXTRA)s" +command=python3 /usr/local/bin/pcap_zeek_processor.py %(ENV_PCAP_PIPELINE_VERBOSITY)s --start-sleep 10 --threads %(ENV_ZEEK_AUTO_ANALYZE_PCAP_THREADS)s --publisher "%(ENV_PCAP_MONITOR_HOST)s" From bdd28ca3f7a39e69a4f2f13c90c0603137ef71de Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 6 Apr 2023 13:35:18 -0600 Subject: [PATCH 110/239] work in progress for idaholab/Malcolm#168 --- shared/bin/zeek_carve_utils.py | 79 +++++++--------------------------- 1 file changed, 16 insertions(+), 63 deletions(-) diff --git a/shared/bin/zeek_carve_utils.py b/shared/bin/zeek_carve_utils.py index 72c7f9d8e..fd4eb451c 100644 --- a/shared/bin/zeek_carve_utils.py +++ b/shared/bin/zeek_carve_utils.py @@ -301,10 +301,7 @@ def __init__( self.newFilesSocket.connect(f"tcp://{host}:{port}") self.newFilesSocket.setsockopt(zmq.SUBSCRIBE, bytes(topic, encoding='ascii')) self.newFilesSocket.RCVTIMEO = rcvTimeout - self.logger.info( - f"{self.scriptName}:\tbound to ventilator at {port}", - timestamp=True, - ) + self.logger.info(f"{self.scriptName}:\tbound to ventilator at {port}") # --------------------------------------------------------------------------------- def Pull(self, scanWorkerId=0): @@ -320,7 +317,6 @@ def Pull(self, scanWorkerId=0): self.logger.debug( f"{self.scriptName}[{scanWorkerId}]:\t{'📨' if (FILE_SCAN_RESULT_FILE in fileinfo) else '🕑'}\t{fileinfo[FILE_SCAN_RESULT_FILE] if (FILE_SCAN_RESULT_FILE in fileinfo) else '(recv)'}", - timestamp=True, ) return fileinfo @@ -553,10 +549,7 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo nowTime = int(time.time()) if not connected: - self.logger.debug( - f"{get_ident()}: ClamAV attempting connection", - timestamp=True, - ) + self.logger.debug(f"{get_ident()}: ClamAV attempting connection") clamAv = ( clamd.ClamdUnixSocket(path=self.socketFileName) if self.socketFileName is not None @@ -565,16 +558,10 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo try: clamAv.ping() connected = True - self.logger.debug( - f"{get_ident()}: ClamAV connected!", - timestamp=True, - ) + self.logger.debug(f"{get_ident()}: ClamAV connected!") except Exception as e: connected = False - self.logger.info( - f"{get_ident()}: ClamAV connection failed: {str(e)}", - timestamp=True, - ) + self.logger.info(f"{get_ident()}: ClamAV connection failed: {str(e)}") if connected: # first make sure we haven't exceeded rate limits @@ -586,24 +573,15 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo if connected and allowed: try: - self.logger.debug( - f'{get_ident()} ClamAV scanning: {fileName}', - timestamp=True, - ) + self.logger.debug(f'{get_ident()} ClamAV scanning: {fileName}') clamavResult.result = clamAv.scan(fileName) - self.logger.debug( - f'{get_ident()} ClamAV scan result: {clamavResult.result}', - timestamp=True, - ) + self.logger.debug(f'{get_ident()} ClamAV scan result: {clamavResult.result}') clamavResult.success = clamavResult.result is not None clamavResult.finished = True except Exception as e: if clamavResult.result is None: clamavResult.result = str(e) - self.logger.info( - f'{get_ident()} ClamAV scan error: {clamavResult.result}', - timestamp=True, - ) + self.logger.info(f'{get_ident()} ClamAV scan error: {clamavResult.result}') finally: self.scanningFilesCount.decrement() @@ -689,17 +667,10 @@ def __init__( yara.compile(filename) self.ruleFilespecs[filename] = filename except yara.SyntaxError as e: - self.logger.info( - f'{get_ident()} Ignored Yara compile error in {filename}: {e}', - timestamp=True, - ) - self.logger.info( - f"{get_ident()}: Initializing Yara with {len(self.ruleFilespecs)} rules files", - timestamp=True, - ) + self.logger.info(f'{get_ident()} Ignored Yara compile error in {filename}: {e}') + self.logger.info(f"{get_ident()}: Initializing Yara with {len(self.ruleFilespecs)} rules files") self.logger.debug( - f"{get_ident()}: Initializing Yara with {len(self.ruleFilespecs)} rules files: {self.ruleFilespecs}", - timestamp=True, + f"{get_ident()}: Initializing Yara with {len(self.ruleFilespecs)} rules files: {self.ruleFilespecs}" ) self.compiledRules = yara.compile(filepaths=self.ruleFilespecs) @@ -737,15 +708,9 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo if allowed: try: - self.logger.debug( - f'{get_ident()} Yara scanning: {fileName}', - timestamp=True, - ) + self.logger.debug(f'{get_ident()} Yara scanning: {fileName}') yaraResult.result = self.compiledRules.match(fileName, timeout=YARA_RUN_TIMEOUT_SEC) - self.logger.debug( - f'{get_ident()} Yara scan result: {yaraResult.result}', - timestamp=True, - ) + self.logger.debug(f'{get_ident()} Yara scan result: {yaraResult.result}') yaraResult.success = yaraResult.result is not None yaraResult.finished = True except Exception as e: @@ -753,10 +718,7 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo yaraResult.result = {"error": str(e)} yaraResult.success = False yaraResult.finished = True - self.logger.info( - f'{get_ident()} Yara scan error: {yaraResult.result}', - timestamp=True, - ) + self.logger.info(f'{get_ident()} Yara scan error: {yaraResult.result}') finally: self.scanningFilesCount.decrement() @@ -866,10 +828,7 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo if allowed: try: - self.logger.debug( - f'{get_ident()} Capa scanning: {fileName}', - timestamp=True, - ) + self.logger.debug(f'{get_ident()} Capa scanning: {fileName}') if self.rulesDir is not None: cmd = [ @@ -915,20 +874,14 @@ def submit(self, fileName=None, fileSize=None, fileType=None, block=False, timeo # probably failed because it's not an executable, ignore it capaResult.result = {"error": str(capaErr)} - self.logger.debug( - f'{get_ident()} Capa scan result: {capaResult.result}', - timestamp=True, - ) + self.logger.debug(f'{get_ident()} Capa scan result: {capaResult.result}') capaResult.success = capaResult.result is not None capaResult.finished = True except Exception as e: if capaResult.result is None: capaResult.result = str(e) - self.logger.debug( - f'{get_ident()} Capa scan error: {capaResult.result}', - timestamp=True, - ) + self.logger.debug(f'{get_ident()} Capa scan error: {capaResult.result}') finally: self.scanningFilesCount.decrement() From 36a705e726a8763f978aace9d97d7dd94844c96d Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 6 Apr 2023 13:47:45 -0600 Subject: [PATCH 111/239] tweaked debug --- shared/bin/watch_common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py index 2e5f55226..091bf7139 100644 --- a/shared/bin/watch_common.py +++ b/shared/bin/watch_common.py @@ -70,11 +70,11 @@ def on_any_event(self, event): if isinstance(event, FileSystemMovedEvent): fName = event.dest_path fNameOld = event.src_path - self.logger.info(f"↦\t{event.event_type: >10}\t{event.src_path} {event.dest_path}") + self.logger.debug(f"↦\t{event.event_type: >10}\t{event.src_path} {event.dest_path}") else: fName = event.src_path fNameOld = None - self.logger.info(f"🗲\t{event.event_type: <10}\t{event.src_path}") + self.logger.debug(f"🗲\t{event.event_type: <10}\t{event.src_path}") # This is a pain, but due to this watchdog issue (see # https://github.com/gorakhargosh/watchdog/issues/260 and) From cf73d6e67557439bf3cb70c07699f4a6f6f736ca Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 6 Apr 2023 15:09:09 -0600 Subject: [PATCH 112/239] work in progress on a little bit more of the python refactoring with the inotify stuff, might be slightly broken :P --- shared/bin/watch_common.py | 69 +++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py index 091bf7139..2bd0f9392 100644 --- a/shared/bin/watch_common.py +++ b/shared/bin/watch_common.py @@ -10,25 +10,19 @@ from watchdog.events import ( FileSystemEventHandler, - FileSystemEvent, FileSystemMovedEvent, - FileMovedEvent, - DirMovedEvent, FileModifiedEvent, - DirModifiedEvent, FileCreatedEvent, FileClosedEvent, FileOpenedEvent, - DirCreatedEvent, FileDeletedEvent, - DirDeletedEvent, ) from multiprocessing.pool import ThreadPool from watchdog.utils import WatchdogShutdown from watchdog.observers import Observer from watchdog.observers.polling import PollingObserver -from collections import namedtuple, defaultdict +from collections import namedtuple, defaultdict, OrderedDict ASSUME_CLOSED_SEC_DEFAULT = 10 @@ -51,9 +45,15 @@ def __init__( # self.deck is a dictionary mapping filenames to a list of OperationEvent of length n, # with [0] being the oldest timestamp/operation and [n-1] being the newest # timestamp/operation. - # In self.dec itself, items at the first (idx=0) of this OrderedDict are the + # In self.deck itself, items at the first (idx=0) of this OrderedDict are the # oldest, items at the last (idx=len-1) are the newest. self.deck = ContextLockedOrderedDict() + # because of the pain in the butt that is the fact that we get the modify events + # on attributes-only changes (see below where I set fSize), if events show up + # for files and we've ONLY seen open and/or attribute-only modify events (i.e., + # the size doesn't change) then we're just going to hold on to them here in + # self.modDeck until we promote them to self.deck for processing. + self.modDeck = OrderedDict() def done(self): return True @@ -77,7 +77,8 @@ def on_any_event(self, event): self.logger.debug(f"🗲\t{event.event_type: <10}\t{event.src_path}") # This is a pain, but due to this watchdog issue (see - # https://github.com/gorakhargosh/watchdog/issues/260 and) + # https://github.com/gorakhargosh/watchdog/issues/260 and + # https://github.com/gorakhargosh/watchdog/pull/800) # we get FileModifiedEvent triggered for metadata-only changes # even if content has not changed (e.g., file access time). # So for now, if we detect a file has been modified but the size @@ -99,16 +100,21 @@ def on_any_event(self, event): with self.deck as d: try: + deckInserted = d + if fNameOld and same_file_or_dir(os.path.dirname(fNameOld), os.path.dirname(fName)): # a file was simply renamed in the watched directory (not moved # from some other directory) so remove the old filename from our list # and a new one will get added - d.pop(fNameOld, None) + if fNameOld in d: + d.pop(fNameOld, None) + if fNameOld in self.modDeck: + self.modDeck(d.pop(fNameOld, None)) # insert or update file event(s) if fName in d: - # this is a file we're already currently tracking + # this is a file we're already currently tracking in main deck # see comment about fSize above (FileModifiedEvent only counts if the file size is changed) if ( @@ -130,10 +136,38 @@ def on_any_event(self, event): # otherwise append a new history item d[fName].append(newOpLog) + elif fName in self.modDeck: + # we've seen this entry before, but it's in the staging modDeck + + modifyOpSizes = [ + optLog.size for optLog in self.modDeck[fName] if optLog.operation == "modified" + ] + # promote to main deck if either: + # - this is something more than just an open/modify attribute event OR + # - this is a modified event, but the size is different now so it is an actual modification + if (not isinstance(event, FileOpenedEvent) and not isinstance(event, FileModifiedEvent)) or ( + isinstance(event, FileModifiedEvent) + and (len(modifyOpSizes) > 0) + and (newOpLog.size > 0) + and (newOpLog.size != modifyOpSizes[-1]) + ): + # promote what's already in modDec to the real deck, then append this new history item + self.logger.debug(f"𝦸\t{event.event_type: <10}\t{fName}") + d[fName] = self.modDeck.pop(fName) + d[fName].append(newOpLog) + else: - # this is a file we were not previously tracking - d[fName] = [newOpLog] + # this is a file we were not previously tracking at all, in either deck + + if isinstance(event, FileOpenedEvent) or isinstance(event, FileModifiedEvent): + # this is the very first time we've seen this file, if this + # is "open" or "modified" with no other context yet then + # put it in modDec until it shows up like a real modification + deckInserted = self.modDeck + + deckInserted[fName] = [newOpLog] + # move the file to the appropriate end of its deck, if needed if not noop: if ( isinstance(event, FileModifiedEvent) @@ -143,11 +177,11 @@ def on_any_event(self, event): ): # put FileClosedEvent events (which now have a timestamp of 0) at the front of # the deck (to be processed first), and others to the back - d.move_to_end(fName, last=d[fName][-1].timestamp > 0) + deckInserted.move_to_end(fName, last=deckInserted[fName][-1].timestamp > 0) elif isinstance(event, FileDeletedEvent): # if a file is deleted I guess we don't need to track it any more - d.pop(fName, None) + deckInserted.pop(fName, None) fName = None else: @@ -157,7 +191,10 @@ def on_any_event(self, event): self.logger.debug(f"🗑\t{event.event_type: <10}\t{fName}") elif fName: - self.logger.debug(f"⎗\t{fName}\t{json.dumps(d[fName])}") + if fName in d: + self.logger.debug(f"➊\t{fName}\t{json.dumps(d[fName])}") + if fName in self.modDeck: + self.logger.debug(f"➋\t{fName}\t{json.dumps(self.modDeck[fName])}") except Exception as e: self.logger.error(f"⨳\t{fName}\t{e}") From 195033dceabf43bdd21713d04b958722dda2b39f Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 6 Apr 2023 15:16:15 -0600 Subject: [PATCH 113/239] tweaked debug --- shared/bin/watch_common.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py index 2bd0f9392..0e08ee7b9 100644 --- a/shared/bin/watch_common.py +++ b/shared/bin/watch_common.py @@ -53,6 +53,8 @@ def __init__( # for files and we've ONLY seen open and/or attribute-only modify events (i.e., # the size doesn't change) then we're just going to hold on to them here in # self.modDeck until we promote them to self.deck for processing. + # Once gorakhargosh/watchdog#800 is pulled (resolving gorakhargosh/watchdog#260) + # we can get rid of this complication and just ignore attribute-only events. self.modDeck = OrderedDict() def done(self): From 695e59bcaeb4f5e698104200e85b0775df1b074a Mon Sep 17 00:00:00 2001 From: Melanie Pierce <59747276+piercema@users.noreply.github.com> Date: Thu, 6 Apr 2023 15:41:56 -0600 Subject: [PATCH 114/239] Enable htadmin for idaholab/Malcolm#169 --- .gitignore | 1 + kubernetes/19-htadmin.yml | 17 +++++++++++++++-- kubernetes/99-nginx-proxy.yml | 26 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 3fa947016..e2afa4bfc 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ __pycache__/ __pypackages__/ *.py[cod] *$py.class +.DS_Store diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml index 992cbf91f..b988935fb 100644 --- a/kubernetes/19-htadmin.yml +++ b/kubernetes/19-htadmin.yml @@ -43,14 +43,27 @@ spec: - configMapRef: name: auth-common-env env: - - name: HTADMIN_DISABLED - value: "true" - name: VIRTUAL_HOST value: "htadmin.malcolm.local" volumeMounts: - mountPath: /var/local/ca-trust/configmap name: htadmin-var-local-catrust-volume + - mountPath: /var/www/htadmin/config/auth + name: htadmin-config-volume + subPath: "auth" + - mountPath: /var/www/htadmin/config/default/configmap + name: htadmin-config-default-volume + - mountPath: /var/www/htadmin/config/ + name: htadmin-config-volume + subPath: "htadmin" volumes: - name: htadmin-var-local-catrust-volume configMap: name: var-local-catrust + - name: htadmin-config-volume + persistentVolumeClaim: + claimName: config-claim + - name: htadmin-config-default-volume + configMap: + name: htadmin-config + diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index f55e8bea1..aa47eb436 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -11,6 +11,20 @@ spec: selector: name: nginx-proxy-deployment +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx-htadmin-proxy + namespace: malcolm +spec: + ports: + - port: 488 + protocol: TCP + selector: + name: nginx-proxy-deployment + + --- apiVersion: apps/v1 kind: Deployment @@ -36,6 +50,7 @@ spec: ports: - containerPort: 443 - containerPort: 8443 + - containerPort: 488 envFrom: - configMapRef: name: process-env @@ -66,6 +81,11 @@ spec: mountPath: /etc/nginx/dhparam/configmap - name: nginx-opensearch-curlrc-volume mountPath: /var/local/curlrc/configmap + - name: nginx-etc-auth-volume + mountPath: /etc/nginx/auth + subPath: "auth" + - name: nginx-etc-auth-default-volume + mountPath: /tmp/auth/default/configmap volumes: - name: nginx-etc-nginx-volume configMap: @@ -82,3 +102,9 @@ spec: - name: nginx-opensearch-curlrc-volume configMap: name: opensearch-curlrc + - name: nginx-etc-auth-volume + persistentVolumeClaim: + claimName: config-claim + - name: nginx-etc-auth-default-volume + configMap: + name: etc-nginx-auth \ No newline at end of file From cf61a4e476be2642cf45d0af0d772347f6021b06 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 10 Apr 2023 13:48:54 -0600 Subject: [PATCH 115/239] work in progress on ISO install document --- .../iso_install_boot_menu_bios.png | Bin 0 -> 102900 bytes .../screenshots/iso_install_boot_menu_efi.png | Bin 0 -> 110288 bytes .../screenshots/iso_install_dd_linux.png | Bin 0 -> 193045 bytes .../screenshots/iso_install_etcher_macos.png | Bin 0 -> 85762 bytes .../iso_install_malcolm_iso_menu_1.png | Bin 0 -> 91882 bytes .../iso_install_malcolm_iso_menu_2.png | Bin 0 -> 177856 bytes docs/malcolm-hedgehog-e2e-iso-install.md | 124 ++++++++++++++++++ 7 files changed, 124 insertions(+) create mode 100644 docs/images/screenshots/iso_install_boot_menu_bios.png create mode 100644 docs/images/screenshots/iso_install_boot_menu_efi.png create mode 100644 docs/images/screenshots/iso_install_dd_linux.png create mode 100644 docs/images/screenshots/iso_install_etcher_macos.png create mode 100644 docs/images/screenshots/iso_install_malcolm_iso_menu_1.png create mode 100644 docs/images/screenshots/iso_install_malcolm_iso_menu_2.png create mode 100644 docs/malcolm-hedgehog-e2e-iso-install.md diff --git a/docs/images/screenshots/iso_install_boot_menu_bios.png b/docs/images/screenshots/iso_install_boot_menu_bios.png new file mode 100644 index 0000000000000000000000000000000000000000..9de7b2108d3706fb885ee08e5d87edc5f1a62ce5 GIT binary patch literal 102900 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 UMRwA+4f5;8#LeqJS(jn>3*fVp(f|Me literal 0 HcmV?d00001 diff --git a/docs/images/screenshots/iso_install_boot_menu_efi.png b/docs/images/screenshots/iso_install_boot_menu_efi.png new file mode 100644 index 0000000000000000000000000000000000000000..95c012fc8651f260ae78e2e2c125b00156c93306 GIT binary patch literal 110288 zcmdSBbySpH_%=F-fJ(iHbSWq(9g-3%AfkYZBHdEb-HeEolz?;$ihzhT49(CTO2aU8 z!%#!R3}=tN@A-XyoVC7x&bQ7wdo7l;p4iWhJFfe>?``OFH6`*Zj8`BK2)XiO1$797 z5DESiE)jz(8CB9xz&{tAA1c4P1e(vKHz5$nb%?UUgI6A@8z^^=*ZbJ>on@I4E{5o~aM?19af7^f&DII^=cv}>k z{o>M6Ok(2L(0<6@mO6ap%9Y2;%8eAaEyMm^`fy*d9xm?4^6$%XQdf}w{=BjDv-e?z zox^)nWUo143b~CauO&fzpqaK&^4a_a!xe5Csy|KgX{xZzGhG9Z}?AoSbCF!6;DjMi0TzWQnb|k+HESc~OAB z4qQ>;;bPyttv5Z1LJ;Zg2EsSy=EO@V=YE-RX34AXPB&|!d`}NVJ)l-rR?^tLLCD#% z+_^FC?3jGYT-Cfkz4v$-_DoHU!X4#0YK81i>M}1C^4ha}6ve6Xfdz-wLq)nNym1UYuB#58s8kseOYxdVNKw%lw@(VS+m9H;scgF2btRpCszq3 z4;~CM%UR-IXxE~hDKpj)BjdQM_%S5pOiMCN4Gd8c;fIxQS@|bQ4kvMXfX;-T<0gF% zQDlsFLn@~IQt+Q&IpH$!kW6~YU-cou&`)yvWZsRTf<9- ztAoMXrRG$utZH_4{LV`~H}2g_X$_+r%+nOuaJ$zWOt~EUOg6W~ptbee?yl48d>tB> z=?F*sXvu`aBOw?)D= ztY-`IqB)dyms0H24Gh#u&3ox39E<|i3wvfA1dSRXBL(_l?en=}2>a>!o@CLmq9Q>j zCnt}+-%6dGodjp?-ral^|4>mic0_aJeLz#NNd3xIWrD=>>C(C&|I9*;)53J*$M-5u za+&z#so0Zx%^WhMaqrxUgwu5rgl=#@`i8J%dL+kb>2jFa&HW7p^+Spc-Ljb7RG+zt zim#aMdS>mqz|?)DAhRe6I zu+hBKvGZ1fpxcJlz`($5Ufu;vzua;FrR?{T$wVR3%A>h3tI+}$&{b+G^Xme=PVcQc zB~MS!x;3(FvD4g@3$T=wZW1;hhpq{pQY6}cWVE(cT3A^4N6$GJRMl=54?&`!@xy?o zg981UmB}iROrN#z87{k1w&m)F6DvW*Qdk7mJ7u|gn0Drr(Q zU@l;f5=AW3u3HjT>ha?T02X|8D3=;&DYLZ4KN3?v$Z?0?awDH9EVO@hno1V8Pj?!* z%#Gm6xpKti+@%CP7G&7%=vQ7t(KO#(5j4J8w^c_0GZQ+Rg^4V!lC0d_XV+U?Og$Xz zX21?%$@~_UE4#V`Ew#E2fH>_phK1a15FOQP7Vz641%3B5Q_A(A+@~YD7w3YQ>}I|M zv+l~7T}YI4=8WZ3ZJKr1ZlQj*y;0h~8w)$TxguuKlh|mMp#Mlqi~gDH@lEI{a++S; zPA}lV@W!oMfpA--Fi>OJvu%0xmoKX}tJfvY&oG4DJ*kqh_qJ+dNAh(sHPhHg{O`Fr zlWdiwrpzeWkAtf}ozohJ+~izyu?k=vT3T8l4oBO@=Y!JRl1>YcjdUvPP;d3U1<$ct zPy(o?bMLMdCJ(`;*Q zSnp^EdyTa#`*b*=WF9LJYBwBC>pLh}_Lf7R9}&{=y7L?P*3*M(G93yj*pT0-ymd%7WVurV(G;sq5O^h^j+ z=e7U!8BDgNz1)O}F zwX`T4Y@(1e1*|*le1b;Sa(Ta z;XTa2zt`MiYN+ODyqbut`rQ1hZuYkN$*nVP3!*duwx>&vu>RCkk}PUsjGlq~`qf4i zr-iwFi9!JkBId7)f!8FI-*d8L>&=U>o zO-)Vr4{ZuZnG#uPX^D>ltXsvbC!j#WI{NwwT>r?Sq3^y%zBOgG)9r|mkndvCb`{ht zEaVp{=bxVv43IU?bPFKfob0XYx%^hVZ>j5B~JSEBo(b@la)W&p}kXD z{W*B#PBWZl^}6%wm0h@*@L7d>%<-|ho>|s6L(j{w$z#Fz=DCKh{k+4nJXVFIr&2gU8L4d;y@C5w^nw+{@HUaShy zik2IVJn~()2{{?^n`t2E>go!KjLg=^dp*1)Za*dMzzZg2=MZ7&vqPk*=SZZM6I)Uu zOe^tO4e2$X zFCR6?gHHly+YMF#Qn}qk${_6_2tmSa=eK8@Z`=dg=-k^`^6IXzRXwR<%Q1#`cu`C8zG zxF~u3R8U(iO*7nMP>vU<49cnolWuaXR_@=oXseSXhiX@IEcq$7@(m?k%q?GGmq zQ0UC^=g*%T`g3!0{b4*6dz7rwiQ(a7A3uJ4Zzlj0Rf)~_?mvlKDv1rM;;3sb2pYb9 zIs*^mj67_oPC3ue;*o;F!iH;gbq;Lo1Ue@(=?XA{ErSHLi56z*wAOvSuv4$(eQFQN zXD>|Yc3*FEQPPG1{Nu6sbh|&fuAnWgqGXki$DvK+#&?vn+%uOsf2)kURMFMl{r!~T zA7o5gE2mv5r1@;%1WI|ZuSH-ZmHbMn?7U`ZrtAjZtwi*Q+Dfac3C*@?U z25ti95E>RndM4vmrRM_>2*hP=D0FCOC?oTeW;_hm51ayHrT&-f2K}9>*JW$obq4k4 zxYJLpu#k|)s_!G%UbB^Z^uYuDLyeCGuUskK5Zk7ofj5{Fk`^_*RKFm`Bu1)2CVk6J zdMZf&#p?!IDyu|1*sJqC6tzU&3=DXob}V^PS|<*B z${=pXTx!wZHtTSawPt_HdyDA6FhPo`1QxV=>?3`=5DS1z(2LARJ1dz{S6NtCE+rS% z?sh3}$>5sVaL;Wg>JF#8yZA?Q)OB>a0A%Ti(=<>9pYuFf%_$zYxkgJ{@}~3Z6!aW> zzxx2Jb20DwXJe*Fl1(6Mp8?(!GH$vEjC5iB$y=kOtfvXW0Hoee*dNZ*e5s=|kueXD zW*{IzUOG$f)BDT;r(&mis|2DR4?u&r$ zbE^Q2Vr>p0pHD!X-GV^R=GnB%-)c~6UYi_Ow{UT}iwD;Lf-cr<)z6Pxz{p*!<)~#q z#PK;UZs(UgrKsy2fPpP8<_v15#M@G{5hrrGr9@18vmM+OcNgE21YwlZ(&{uqZgq9l z2fIJIu(UK#Y^qdHwZzyU7ot&Q!7CnEn$>3nY?Q}bC>Q<@j~IiDwxnuKRpqThS7bOWkKDo=D_wO^eZ{B+Q?(W}UF#6NWG1|XBFAD|Q z{(JGC|INi>onUlF$73D1CDk6!jT`Cd%lk8M7)r%MOUo$DXpm9<$rIr_cRIQBIi;kM ziEH)v0_QLgGCj^qV>8v+w!U=@<~FiS}8qnc{~U?VL~wCpB<*AmHaUzyDyF z&#*WKVDo{BXZBML_2dQuklfrm!`Y%iq10_6mNO966Ep{qUn2dbpmmE+=~HPWtf~4r zQm1=W?~LVxw|C_XT9KF9Um+*Bp|S$!7>$vi+L~tdAc$-vv7n-3YL8^O=ef3OA#Y&a z{hDtDj9M&(yt_oL+U8{%ldGdtf2N3k^Q_U@P-i%+?i^m3=%S+Vlk zk9EoOie-a0nE3F9Juvm1Fz?NqV*K$4nL~D^C|29>5=8~jetdTwD-b-dBYK0LdTFNP z=qO}Yk>YzwIpbC%|60%27hyvrPfTgDo#F3;YyJ8bGY{5j93u^Y8aaPLSo!+MX!XI+ z=WBgi!=-H_^VzTU)O&v22*?XW8`Sl2a7qP%h=lXU&mYyb%c_em=}H#wp9-+FkCC=- zFMcgNa{4`bjfRGrjZF#6wsPjoKvmzM?pt>Dc9{28)2hpELJn-P!9PG%KL#&NshMlc za7q@bo&x=(;cTTxkJO((cj%(+y<%)^?6UGJJu%U8cXxLjaehC4R3?t)o}1H6uHY~7 zBqZd@0mS4Ds}3NHa)M0SJ&T*j{K_60c`!;tL!+D%$pY~71`_&iWyKPbEc%qJ%!W-y z=bcqD@Ez1tR4?*x@d*n9ro?g=6ZKm1Q4LCMeZ=bSQzs zU@w1Q3FTZ?jGQpLW5v#zzb4wl8Cn;VsgK)v3LUXM3<@8DW7B*;M5KuA#1_*LYN^DTZyJN#6LF7yf(v}dg(+gpy$eoIYgPAzZWns^O7VhrRtjGt1r_RG=JRuPg zvq=`G2sD(SP1d4DX*3ljlWZ@XPijwQyF@H{Yn z!sWEExA;Cvt~od1+0q3nNCyZTk)*99m3hjcy)K$p{`+-62}?dEdC_4`Gjz6Axhh{}DApKz+yem0gT z(Y&k-eApLP9Y!|av(-C{pWpES^6K z*<9FL>TPa5CT^svsvrsz_m`ZdeVPCw?p^jO_Gf3sYmGBBXyINEPN zpS(KwmgTB-DQ?_>bvV0ia#C-17X#n{-#_rd8uv^PcL39)t{&6b+uJrTN~wr=_O6H;)-lb$AK%p4JL8t z4^8?wfB3j<-ny{<v@lM6gY%r|I6GJ;`Ck_mCt6=#31gog_ z03^B%?KYZ++)c0Z)~?zOZjc!Qkt_(dVRJDiz#Ql=qcz|@<4*>3%D)21f{?KYCu+$3 zq;Ppz@Qj6;pXHgO@?`-mmL`(9v$2G?tGipf-o&i@v|-x&oDgn1?QcxKfvkYFKCRDu znG;#gnpGpV4kKTFVQW1Jx4j3@F!|2};KXQnG+*ecs~m2kAdxIk2_zhSsngN_{LG-< zgoZA4It(_wBZ50?=Q>8)`}MY5p3jTe)*n5r5rfSJnm&q7LvOWhy4CD>Nb?@em{PFF z>gCMSyYFZ*yWQ(YY^(pu&`&r?;60Aq+uPK5^abERrrbMsc(qUT)7Gy3alyK2y71*X z5m-+TN+UaNXq$2~6cCYV_gvXUc)|z>Pc%FgUa7~NQ^{Ca5rDYKiNE8OdZW6 zsq;2$_`&vi-a&%?t^-83!nSbOw-D+6fq(4cVD3vLVu}@|w{N+Pjn!<%E|HRQ2?-TH zK%z?B1%MQrbbM;mia(c0J8je5FKD)_7%VpR1|Uj5@AX+4*W@59W!auw<*~{oFE(5K zQ8}i*%<7>^TcleMIm<)LQtmZoz6{gg1E1qXO~5=RPnV5f>6pIt#&4ks!bbPy($AVb%8wF}uY`^u}0K?9q`2=d<*8z2=!phnsIUyU3*I_2of)x`RFJKE|Ccj-lNO zlEvbLGL?j!S_El*_f_krwq8PmsK(F-8H76;tk(*}UKgDChlkth$zgN=N#~aCy_v=B zU&Fs&11QP-6aOy&lKpOnQL)e{ZjtsaBMg1CZGZj2tOFFqHR!y1}N&I^0%|&RY~%^$g!&H=pfUt{bu?Yk-Ns2-KVOti1S^RsI80JQNR1 zg#~>}YpX^I9UGhBG8#bDt10B>2AVPsqvbfD;-)Rz#(VWMJ~1z2z!Li$qIRW5Z{qR( zGW?ORR+&}u##nJ|x{UWl@{FA3!|vBc6-Wm69mQ041F1Yet-*}f{U1-$15i+(JxhWY zFJ25cJ#6)@h-+ zF!nPj*>zf=(yncIZ^M%nghF@i)4Li7uvI%j>w+pAf>K`kQCaZ%vt6E;*qF-M69zzn zM1LL%Pa4&ifA|ok3+Lh0t&f4$?b}zoqIGWl5s|Qj0gk$Re97V#s{>o>`cZ#Yt%k9j zN%Dwf%j=}D-?4Xi@MuwBlS1XttIh@m&o|)khIN&g@mP9mFj25dwQS1l*G$(ofhgv9 zYuQlnO^4B=D8^8f9i_Cz>GrJ6l&?L3rb^nwX<1x!t!ANicS2Zw!9hnp^!!vGgeoUJl$eKgrV{HAgc|HbT>)n zlxeVTze#>oF5e(FN zsOC>RE9w3>qe^!X3g28E;T4Mh@WG=?pFeVmEnn>8_Bgk+bSf=h@=HBEl?M-uBf7`Y zZh#NEL`lhY>fjd30@Yu)oRW@P`F{K3$FR+$Y<%x8L3nX#_I zf{F{kz4W3^5(r<9zB>Z4_^k{{y?sZhdZ#8jEF?8I+dwu6)4!#h7teRRP#Sn;0^fnU zJwDgl4x&O;pv9+@%s+*nr{UjxtdEcSnM>{Td;8V!UW6()>=AS?0e=tR(x|lmVFLaa zdVo6P=g+d-b&zetx8oO&pM<~e{*Ooa{=YoXJ<7OkK8^VppC4d|#4eZv4BoDc?^RCsxb!<~eFm%=Z zAV&kaaNXJZO8>Bulw6rWlxKe-r>TbD<8^9DmTH&HYlwp7|7rm`5`{P}lal6PstpPg z?CZ6g`t-GPz6k&AJFFeO)CZd;ZU_^=1#E|ee6p<>F|Lshb_1IJ&?kiW84oxAQb)8N zFgdz~`>KO)*3iqQce~%{HeTUwDkn!4zh0X!B=~v11Adl28)wjd_o*3pqSUi>bPT8bx6p z&a18&Zl?V$yER55H6oDa(zw_LSV`XIAhYoXiPs^`P3EOVk_{y)ygm0<_=QVW6XU0D z_aBOp6Lyz?obg(<0cpJ1?7kNSK(tZspz{lWH)M>$u5SI*si+!RCtpK`jUQrp9SEM_ z_jEzeESZyP)>!)Zbb5B@2-6meIA<9ws3tD#zog7zli%#7qC%#utbDfQ4mCSoBPX9& z{e+|2J_>NBqNX0qKzopj2O0bKHU+WN^!m>h4|14Gp^^F@vrDIjgb1=XHD4xmp4&fh z@RqJb8BDTL@>is6X%-Zg)Z@jnqpEA(ZD&KIpNfHj>gZfTSonPo?(Do08(9+){?Tq* z_ALvXyt!dnM62G5P+ZS93HtSNkn7L9AI2tHoU$-~tD|j3PT45`_T`=P`K|p~j0XY! zNpQ!Qp8Phd$=XAZoHV63$ZW!LmAP$f^IYD=!zTmzvwgm?M1}d_QjF<@5IfHN#523t za`Ywh9v@Ln@3hxjk4tWHhpYcEF?BD#1m)Dh>|Cgs+<{rGeU5Ns8w#Hx~MYYPqha!55W*?Lkj4DPEj%8}bNWnAX zs&C(z8`u(6*@BE`F`hdN#Il9{{oAvm{QgZRMjyZfL0y`>}V;vodU2#$aR-|f_BWUxo*N`*%h@1paHNlejA!;4aG2iZ^Ryz~ zr@+F4)~C%{kpe|vIrXbuWLKR)Drhd*+CuDmU`-?+5(gcKqNP2_N#e{lHcrO9ezR3v zgx>eu@g$cvU}G($i)xQ(x1MqHzJSCql3S0%XSBM-6ugmw#peOIQ#e|q`K?aM>&mP5 zs2OPV^ScjPFsus^!%3e5(XG}Vj6Bw+$vCArPia;_pa6a-)BDhl*P!I{#s){kRC#Uo z_VBeYJ$K2|z1E^`-r_1Xo_}OBwq1$+G7%I(&Llnnk}vpMlYZ^`A&<@)?6)6zz2|q9O7)0tT*gq*(zbng+FlAh_oEfi{@6ZmTUM}WP*|Zh z_>8W*q;jQ62q4aw=;An#JN!^o~&Pxwt{yFnf#tC%OsZJQP8B4^j_X<^QX4w|?+ z$EwM@_M_o1ID)KQoc{QN>%jQV8mp!IZ5HAe4dfd%lFmDt5m}X$l@spomSLwnrMN=^ zX#>MFr8|W$yWNh9zv|i9XIMfz4vq;cW=YR%=!s?L$9yxhL#-HJv2N7V*4EA*+MG&P zc9WYl$L0|oo;BGl`5j!;9$#nhZ5-BfG~RAtVM-6LnQUMh1nIYwIDV;D=}Y@wR#UZ- zn$KWuZ4oKjHE#Nu;W~cVA8ZE&>F-)zZ@qP^_F!V2o^xzN45Xlmj-X;H9_aGXs-E*<*hvKlDl-MO>G5fyngU*%fLnZoOzGnSi z#~jy0Fv*g^pb$~m>^<6&2-Ey=+IS5az4Xf7lkFqD&vYp_TeCDsH-AU(OnUDd9`&C2 z{gk{Lb*ByepyD^LSj4Ug^|P-$D`$hVZ|E5Cku4I9N7=uBzu?8%X?LV!UdM{uvSG?r ze)#YLYeg{jl=7mEC7YL}802zjZ_&8T#xxWXg#F|(YrDbxo%fRF!1(A>s?hb z+ldwsmN-f^|Ijy+)bIOTgm#b^D>CK?AfaTJsbjJpwfjC2I90+sFjxaPcW}ovlY-OT zWXA4e205<196b@ou(I=faAkonnQQQ?r!YtGfdytn|EB>Wsg8u=Urm+dQc!rbgDs)? zsa+axIDL))5g^GLD;!i^w4GxSa&u1JAz@Fy@8f{1qA@2cO0wA2Z=9AP1TgPk#O73HIx2_O(ncv**PlT zPTYO+^ocgY&o@&J=JQ)~dWMXYa0a3JPZ}|ONVH&AZ-1X7JObtFfDvjhEK5H!o%}8 zmZbRBlF_)zyZ`^q?tlN091t+S+Iy)z+# z@@&JYYDdx+l))XaNc5CwtZUc#J-z;cX=x|@HqB7UpB$>3IRJMb0t0tI*%DYLh7QxG z2|g??$Djm*#LsUz?f9nH(}Gcs;Pq?pI`WkR{XsFNrg=wWV=C6I23xb;3d+sdpyH_C zQsvZ;-?Nt|9cR18_)A^@+)FOoIq~ zit6QhUB6DSZ30qNO)V0!?yksDWJZ|c`fzLAbWq};mpFdWu=U3ZqpkMB^76b>&ANW2 z8GxOQLF6gDH>LTRDUWTY>ORLS;~EE^bcX?wm_9pdqt=(Xr`M#pAnOe+|1Ef+qQd*Y ztuf10TH5$wiTo}=1aWeh^28Ipcp;>UOI;DUV?+2MP_f9Uk>qT<`K{O9%7dB%G-RyC zLV$sRVPhHrOB_4m%tfoX%&jbx4&HvhEKLho;%2ti~l*gO21L+N|{9NEQXrruo{pJiB9^v?{8DOiw*LF3!%DLoLs$)>?jd zt~V`XjBo#`5$KTQlxB~S!>$S>27$yU4*AI)-L;U5kfttatMd|nSz(#}@ai|U=F2+G?lgsZ<4;1xz9MCcJ!cH44v&77&fn$ z@O@B33KFE!a<;=Bgj=zk?s)LPQA^j|qb^5Wt5kfriXTtuzX4MVsBWXCZVwZ{rYW)F z_#guHd+sJCa}P%dtEP;Rb^%RI53i7tu9vBO{}3ptjc8Cp*Q?pHKg*+X&1st^qE-c>U6C-tX9V)_>juU-_DGXtTVy zNcayZvMjVX90@k22lbY*39#wgryz@tZ<6}&Ijy=&I(mCKG^0qaaH|KDD2J@3{vl9~ z9e+ct{_X2V_J6e}S55wx0|yt!%8XrKj^6nD)NbZ4PTN}iiGV-tVg7*SLg zdc=pHXmdqz~_tGiBsdh{+*>l zpE;%68KD3o^#%UEn@d@2JYOb(R`J{=%o6&Fuj_xi5xP2fPsGE)xdE2+Y-zzcYP{?Y zEGK>9*LK%`9jPy;jq?5jf2R09^w$4U!}I@0$o+qNQ8J|(0+>U`(2q_7752iRp;TP@ zcGweX3g+9k-+ug9pAhvgE3+KQ@6IgxH8_KI-AaGF+_wYSnAGk#KnMDJQc+Qy>?w0f zN?wKazd=5~B-J_gb{8ebkT`?KcXIrsy9wgX9FZAl!z{?$s{ ziQV3M!b1}i35wu|B0*H^Ex-Vq<{%E(*{`a%wi--%3xsXegCiGkS8D=++M+-@gR!Or z!_D>*-nK~FULa;1(v!cx?`LH{azdBu(@O~Mm*EeNyfW`u0SU|(dJYe+icgJ2jO3fD zA#p&27J4%OM@QElfk51e@!K~A$E~{MJVl^M3(SAk)Z#;BrJ$5sI0rAUN5yV)aQBaJ zgH4x-)iI&SqoeJNZaUiP&MxjYL0%J6ERurK{Zo4S<2T=+Y|+saUVD!(Hmr+w^zdeX zYhj9+S5_gjSn4Ur(JL@*mxzgB&r_YQDE!tHviRbK8vYC1U7<+ZPjMOwDk}80$Ts45 zA=R*-KiRKbiHs^p|MZVqd!*y=rm6tneoUN3I8uM#?|9qc9?QKpY9=Py#Q20|ldY+% zpdZq3VT8B4kf$d0;m|&JsyOOkQlVLNF47Mo(h^MbZ2x682E&Mc;Mx$ zg&Q(5(Q;U;+kCo@s2El=G1;MP}VE z8m86BKSev=0OK&S#tVA3l^IrQI;zUrTN9Q-7Jcf)r1xtaO;P3qaL` zg@(KQDlxk&A5?Z7Oj;{Zrl{6RC!GHG$M3h#_gLn$zIx)ic8$&QI6REB**sve8-IW)z@v_nnhU<1ol;G>eY||? zxt2@r@9Mqx_|4IFJQ>?mj2qfSyPP|CsxB6<5s>W(Ya}=r1Wq}kz}A8zbCjnCaocB@ z6hh?2s6e2QsZMk%v$8>YN`hgZ(f-;mxH(>Zpg55Lyruuyxj3v@@f!hsTfR8}*{VWW5E0h=${rI+yLF25oh(<8!kyeNPmnTgjO zNRqCS8P%JIPEMR1Cuz~zOdE8>J}byC>HqM-A&AVXvF`AIUA!W-HH?w~SA@+oJciNW zW`n&iY^^_oPNZoXFc3C?BZ<35^VZwPDx4~o?S)WOFnE_%K(@sYSMueFCKNCrE5GwA zu?MWzXgtArENtz`ni$x6mDI7w^@8=T$tq;kPFo=Xy?6mRaLnL{*Hs1)&WI@433*RR zuoV`!@L`H1y8d|SdX&r(r5#Yw*-$QG$K^B`yj%sd>iljQPGb$GOc3!t`0)uv=*O>B zwR=f?a$8i434HT5-@d4;GU#dl54g(F2dgS-?L@FLAxiL$f@N-HQnG%cL0^v6uA*@y zpXeQe)K`_i=BLc+YN-FHe)Y|NSXip%`qS>Ui&&|j-gkF;M?m=SJbXvY)}(Si5_6kd zYKj~>Rz&MM@#uknaJjX!ygo>@KaPElulKdO@cXw~Oe{y${s^m{kE8W22Bk%-pdj&( z79JbJPA^$PgE&RquQd?)rJkBd?{o1m$Py(Lq-&?#F8Gss@NnzUou`nw33%#0qCap@ z)d+}FPdX}UZ_oUvL0Y$IRDRpyu8`4vZeWsfv`a&Xx8^r%yoG$dEWi@iZZ{Fjo>;oa zt$ry3-Y-f9o}8LGBd)Fa!4B9b=RkZ?>oN6X)rdC>9j_ri=XH2bsiK_}6>^Ehz~1uF z%qFlW=`twXvfq|^Ae@dvE;j5dYos;nWq+;0FnSt6v9BNd% zxe{UmG~~cGXYHZL$rc){gi6XtD1%7#1)Nee z76a2wsbmLzw*B$3H)xoc+B%~77$k~3jdT+DamxmTWQ;@1K%@q;f{#zu34zw}ynX{p zl9)M^;NPI0NXNee2>EG4K3f-M2TV+u=nAtsE@avQoK+fkjO6t=D#PcChbM0&TqIa zuZX!z4Q7=0|vVgBOA{zqtG4yBAj{r;|+P887BtF z=g&t+3$bRu)aXAy?cu>q1Vvj+xi1#?t>_Kd8yP(GNLP2;S=Umb_t(hp}C z{D>cqSU$BD6~=B&l{OXF`>;fXQT~7#V}Q3>m*(Wfbh(M~H|>L#c79t|pO$ zRqKIOZo+u!DF!)xKsYngsGja&qB>K*R4q)#<4_y)0-J6P>u*`#hib>S0vE$;nBL1| zuh+7-n&yc+j)w5+xV&SsKT9?|uckbl91P=vF{=RE{105Ubk{F3>P+9Y`ur3aF=K#J z0FE^VSg0p;)!qy@eXie^8HH=Wcec=pUY!t4UoSlW7$r9ajxTujq5S)R)* zy4CQl2o)N_z)29^rEv=-hf}o7(L7u5D_!%`gNLksM7t}}2|GKZ0SI60;?OX#A9wm(v#G;a*?y?N1%ICK z+2a^-uG?*0XiHjsHrs6GucS`|@_V!l4DA)#2Ag@i)de^VEbXW6GW3zlqqg7Nm@8K% zrFU8wakxV=A6b#@9>?zY7BEx`IKn{K%_^y`X8a`>V9-9Wb`YBhms$;PAguu1Upyj_ z$=lu^B}o!8rGfwlV#LxtYdx~y)Xg_N_DyG{GL6Vy*vpmlkcrSR^GH2F_pei5QuP3V6e*MF`ZARiomoo8JLa$ixA)2AMS z+d8j{d?uH#bp4<3It~z>zxWeRym~np7D++(|{8lTx|f% zFWGO@7pS#T=zIL>k*X-_Z)w53w6cVrf*l$70G?B+5nog?Nq6a@ki<%whdg*HRGIp} z3XYbfKktEy(GoBJ?*PW)%6}}voXVfqK@X2p@8De*zJ-bq{rA5fIA$CDM-%j2`|s?_ z|F+W}v80Hr)cK&sJQ-bv=T$)K(G3lawJ%J6RfIj7zvXd`4H#7W^!HZB!wN^!&2JSE zj5gD?<57px2$qD4|CXTey0k}Cpw6*Ny|_f=BXT$;n_b&%bMMa6xPRZ}c=-SIWX;b? z+yy?R%O)uQT|O%9#Gs&e)LcD?EfQpu90jk=T{`7IoDUV1_Lz=tbAYWY$DcZ){}-0} z|H%IR|FYBZB`E^DAz&{>AK1>cgs!Z+Z-J8mJ`Ss%T6zeIR7q;@lv_{V5t6)XX*psp zt!I^RBO9xr)T;M6J1MpB&(rKhh=};Nvl2Qv%{lu-cdGwY7fKlzB|R%~a*BWB1Ag6F z`p1uFN!&5TQ*K4Y31IGF73K23x>vmlPgppr!!QtU6Zww@u)3DbS9 zdMvZl#|v2eI-T-v$k!FQ7e7tubIP}kJtV8m%>8k}z$cn-g56p9uhL8JUA+paN#iRF zYEdv67oI5}rT-ufMcncYB4Vn+cwF7Cb} zQ4Kt-qbW=1uYJMXe{uz!?`v4OZB%@Fj8=Q)UlXI?h-Q4Q%jBuAeznMyfBltGZgh^@Ce&Q*@-t`M$jT!d)}79pAQ# zW)KS#gpcIz0WE2TVad(ilX0hG;X%=nuDFJi{iSJE%IEsM$|DyDM4$wHQg-3i(^=qWkK@%67Yd_xvRva(6cuV+-0kkAep6TEw| zQjcuLMog{Eb@2+J^}EJADx1-f3++*u!=9dwMmIyYiuKjmBlF+@0%OY}w2QFn@Gw~) zX1A&R$>IKv@A&vePxk zy1$z+CU(4QdZMe{mna~0XY$un-#SDgJ#JD^xas4ka596!O}mVWg!YbZ)AZg~rW}M` zkTHZ|BmC1}PY^9sUE?1mYtn7?ns$4)d)9v|Nu*)akZ{_EaPN>%pjNPSKHspv-PCuF zim>7_BjdX$IrEH4525X~yjlObXpV;5mknGnh}08?6J*%{5uX?5LTK?-wuLe+g1jA3-cqAfnH1dvwU0jn8O#bfPHB~tF<+)%T>RZ0-ktgMuU0-L z!(tnjVZkKGFQ&nC7ZOO1*7COFjdMhB?Zx9`fif<7)&Cc5?-ke77JUl`k)VR0sC0rI zrT5TLQ9waZQF?C?>AeRK5$Ph*n~H);?=AEaAc25LF9AX?2|aYa#X0AHfA{Tv5BKt< zBxLWbJ=a{bj5$V=P_6ggLaz-4e8(q;_JU^u*gXFn+}G}gn5AY!(D3tk7c3;wB}Xh^ zyCPAw-`^ROAv#Pc);BV6cH<+M!l`(HV)y#@=V@DmgLl_=Ez_XUyAS^7@+>U%VCO2hz`(|Yll^oYKUJDnXv1!eH~U;ow-+t*sNFUUGU$B|r_6Nk zzhI&*T)(CRolTK)?XaC|PE=A@{ofmmR?@T=a5$9e?r+t}*UFz&9lvB{OSQPT;|+*1 zI7=7^L0=i6=sEZ{yWQ9+xgGxz`*AG) ztj77WL!^l8*Im{m;xkR2jlLPv+A4jYx&h3JLhs_%dz`Udp+oZbn|T{v0efDCb@%)| z^$oksD#rA;zYN+~4yi2x=&7vq?y|S$T98Znf z<90ZZ;M&|<7J-JmA2);|)#W=M*V1C*DD3ObAME2Tv)9sOC-)=2XTDz$f_2WULGV6J z1ZIOz@%&WdUSV@RqqGMz0;+XiGXimo$UQ4)7qEjc0pWRh*HE(`3VPN1`z-Y^nKs0; zV4`(Vo*@--C%`O^0*Y6P6dY5Ry<*4$Dd;12!gte>;VgH!ZV4Gs(>V+c71!4tQ7wqDWC)Qny* ze2i(*x>N0YQ0wh|BVEZ$3SWn;sjNk%kpHSrbb>X|Yxw$)F@*e^@p{QaU zynN=rS2oiBpM>VNd6zCc^_F(n=AGDhgul&q{^##gMMW_Ess~hos%BRqHh8+5H?cN; zNO|%=e`{U2J#`$f(15!uMPHvkn$jGxF{+_~a8nIg?zr+LFS0J*fF?)ZD$iNUlf%n9 zVA|X;>+Rdh$5|lwR#h{*;c(=$Y`ea(>o;cSCusFMcZMhK&kN&`GU}K#WiM|XA}G?L zWJGlkxKo{-2fi3ajta-71L<2qyNU4#e(N`56b&i;2QUJWqx$tTQ}Roke$z~rx~v60l_w82dN(gWDnLt}{y3fT0B?7> zc7Jcj-4IsbQK&PUa<)7fO>ekZmIY&YoG+(rhqJ3h#o?+ThO8*38b5$JG(fe9 zGS5^PUubJHHbfwjt%QgXMZ&Z0$fHfnP-ka--Ek-0ec3q0fdq4yJQa13w&(#^p8GY? zn&~)R=l9UE-o61u>+{4!8B)mUKvhhswM=jdyKxgVjj~>`E)Axs53W!W87>GDo+|P< zJ^FQkwBsmZ`?X0QhIjRcFu1 zT>bUy;2wE{3zL^rS|%Dr$a%x>(1#v#v7n_bxOWC(~pdlhh$&ZABq>(4BM7bLI+voo})yQ+`EG z@ueL=*9R=a0uxVZ-JdXW^XBSC%;(GFd!VXc=yVkxKQKZ<{Ah^d!v+tDii*A?9BW8F z1IQx3lbI6Fm|#m~uPV>ptRF%IFRvY{Rp1F@mWl)~QBvku(Lg~)RNSfv%b7Ge(%9bD z+3o2Q8@LM+-RNIV5`(5Jtjm@bi58g8ml+qUs~W4taPI~%Um9jiCB-kCF-nhXJ?DJ` z@>JRQ*|V3;ag*O~)($LL3~k&`e!SbeVmZM~D~yXkkwB1bV5;8L^Bn4w4Btor{2j)F zgKfq>n79SkyLHIaeXG~Tf@Pi}Y)`|jjW=(W4tazh_=x!0mG35XNgAQffH=h0ew@~3 zxY<=l$47_vVB~}ApFbYCml(9SNyJFma5ru!b*F~~H`&EczSs2o$UMa}jiwE2E0du5 z*-qMtOAH={O#B70LvJiZUH(Xf7FK8$}?A|xWOkWNjtT;s9jj(Arxg_D43dhks5ZI>2z7FT9UQ|$`IMrlV;?V{9yz4-?nIO;TH(#%d-St9vC8XeWhhQ7w7)hu~O zY!EiJR&JtK{nf$=5+$Y_o$--#j@x;CiXxLo7rwDLjU;qs!Z8Xg$z7?zt$_3%vvua zz&}fZ+a(O-7dbf*9DzNUI}t~P<{Vjggrbi}oSo76)oy&KWn-5ckxEO$gTBE})H&rt z>@+S>{(9Y?57)bwZv>xHImZ{+-$w!Xwea!$5pqcB3&uKa%uMO=h%$ZkU>sUl!(IRr zurv@%%CAngxLKFJD?Jux9@e6o-O^ZOgP7aNJMIw`FPbT@OONF>>jm^`TsT_?!S%hy z#{K3jxy;I{fdmzh9 zi?{PgDCT{ST=1$Hl@p64iJh8@q?@QF?WE0MJPAfs=8+Z-)w?a=oDkwyd}^o==t+e1 zPPjO^HMWFR`Vmr6)sD>e(DFO(FBtqHBi9v=_pBp}jZg`kQ`6?fjo*G)95hp@aq-A7 z@|muV_BNg6tg22tI`Bb$O_6F@k%eV)-3UBBV<~m`YLCn_R=D>O`49ag_J zntvkOH9@J4WAYr%`M}}H6OS*;TSY0BpC6%*NOfMZQQpEGcCoPpE@d{5ov_#%`&&@g zrCa!yh#qb3R@B9C4LR9-zH;6UPI;Wd9#;vfF^91{^;$*|^3>^3XrajHPmRX=%Q|`V zD?M6T2&gGFy_#)NNl3+*s>=7oZ#9N-u6YIoaYg!o&zFm_ySsDP;8)pL#iD15mE+=J z(YTe7@VgM_7jub%v%ykM?`Fu#GlWuWoj5waU`kryC=Ro5)6aK}e4+{cg!Y~Ahhg5j zx`UX8h1248qGr$=hw_Kb-Xgbx$mthtaPM$vTvVrJ>kawi%C{?zC7oMyVwYAdx(`O3 z4~!*KQ4RS91g6$r^rUhJ zUkTpe4`p{6sF%N$xbXpWz`>mFAk*`)v=c4%J5##jVxtb$?wYb4t{ceRnN3VV}(+!X3ObuKlV>wfKlgGr8rhKskV~ zM%PadA({dU2xu0R&w)@>o#pxRI>*2f*?k+2Zj;XlznuUJ{9GDAVl;xS@@Svb8G2+p zBH=79cvx7`lOm6E`s0j?FhZ1X95_3(RQQrp6Zevg|qZ~ zFK&LLS=WU{Q8P{+hxeyVlm=~?02E!)}{b^j$)9X|U3Ca|^5ok2;PFxM^l^|o{S(M?T@BCPT z+~y!28=09-(pe%X8gFmuD4NyuPlco;*5Z9%HMJdQM$-5UNk?aL;MxV_q~KtdL3-L% zl@e6SNY&W#aH&2h;r$o>60zPsSzD_5|=u{v2J!`fYXHAw+E=q4vM;PlOZ z{Fn@QBlvpT3Ddz^NBVXhI;oL(xswFPWCAA7pSgOS>jBSUpeo%ugK4S`Gpgxufr}^M zZ0+8dCc;HxNN4#FEHgu8;%<-Wpu4^EFJP=cBlvjG8tU}DEW-}wf-q8v;|)w6+^*ok zHQ6!tPvy>8Lr$zA&-;@igsS0b5!0D~y$cBOnD@1Qbb?QL)JckBD zz7mxA;AqviY79&E*w}BX=A}BNA>UQy>eHfOdE0snf;4Fsmg;ambG{65Bmd+Sw(Lr! z@#B5=h$`6J!d%ce!lEU#%^(iZXlg=uD%fTW$N__T!31EdV2w5*Fv78%REw$ad|j_o zPUo@JH`oN_VY1p039Q_ST928B9)-yJ&9Le zH4Z6hVG3NY!s6NBJGb7Pfg%Ay>rw_XJq$c?k?CTo()W7hwq9XrzLsR?qUDKZHbQ`oQd{&!xwC}8M==4O#ipjG9 z@Pn!QUL{tGYJ^5#g$Di0s20mAo8jHt{mf^ZY){c>t({v{a*7;vNL1dtfjNl$Ix?;y-2cwv)hh`PU$(1 zjm{n7#(f?EY9zTjQI`8Yun?H7CXe?HRUo@&`NxhcnmoPM-({IoJooJJv(2>-y+*tj zjR#3q?aKkk1LYNI%D6Df0`;3}pJ!F#C9Vn7j{~f#a4`WV7G`nai_pg`ta}V95cXW5 zf2rx6@a`n12OyuGNOiY#&)m@l{X)N^UUul}_Q=}#vMfQ1K(LmeXIj>my*nNIC=AP0 z#~fqP2_}hXWjOs(?hp55uoIa{yYHs>E=;J-;eFADRMd0@$?mAzMBOu*IS}N&o*MKu zZ5YZKk*85w@XJCvGAT(S4>}P~crd>%gQp72v05a};eY%nhNmIK#o)<=c~^UINnW3^ zv00R`q~w~fmoic=_eIwQ}1fIr)fz zH-zUoR#wYZe*5CyPx||XyF0PwiKKB^Vs%biaW zT(h+Zv|?1P^|H3;R{jSIz&3Z9o6kaPQm=e)MOr`rg>krQ?B)*j2!DMkRWg;ATwYoj zw)f|U%xoaTnblD!^{zX}>|p4)c=VFP+Vzc+e4NiafqE0BD_3S+LB@?HE)iT^swLTt zTh5Kwjg4V@zg|Ns#_dUs5)G_h-0?FKZxSnw zKjJ(@*ZVxW_8x5Xs?SR5*GLWg0vj(nu?jhMfCrq-`dPCL^D9(xho9#T$aNy!slPul zxa}I2N0N^B@k1Qd`BD3F-f8?A9M4h6&-c#>YrQKr`mH)z&%V267G> z`}qYEzV0+x_POX3EPc>e7eT+`!ja6I_%M=A@%t!)JKX2gjXWh+>yDV@cb-0F=Xqg- zV@*8VZ$gx%fJsNk@TMqv;SVCW6)_!C5l4imoEh(9Ut@gjuYoE{%XJJ9%hE5!A&2!* zSDuJ}smX}AEARE0P);UMZxd(Yr`HHR=|XNY!_4bq))Fz3YlM_y1=$3Xb^NA-_!Hn^&%+tgNCrbyUb%@KnY657-qA5wbI^P? zF8FA>d=;FzMO2Q*NZ1Wo4iBQ_m#U>}=3hsvjPbL6i9z+D3<@Bw0kSE-$+!GC(6p_Zq)g>7cX|GW?YKO z?%WL-3;**cVVh{T!Tv;L_f|h1NB_XjZ$~@v6N9r`ZTA5muW0<-%$kh2&4hQV8nR-4 zFTviMT!ablaUHMN<>B5jStMH8eviIgF1_v<|5@-SnE$(l8C?39juSu`RPRjMzA;5c z)xe?DP3dBCm&`VX(vR zStY7s5apMvIct5@c(yn5sf?SwK(*5O^PM=fz{v@5D=OF`s8Ga+Z6?0(&maHJWF6$% zu6E+eR?^anTio4pdrr>fcSLvz&w87(hi4mM60c~cteZ5%Cw(no=7HI#-{G`zTyDf^ z&%gca$H<@He~G_;V@=%I#0&HVg&7Qy5bZqOnk0)cWgIQsQjxSUDI^O^TLQH)7gJDL zv3B?vakq8!!_Oq-iOGzN5w^rY+s?&T>LkH0USjLBLv$@*cu!Zp(LiP7|f_$W^qn9 z*lH0xpVOnDU#7L)BPJ(?VFPOVM z2b1$*t^*PTh4c66#1s61At=4(Lu+gD*b}?y19418_hHt{lq*U~iS0d+F$rzthXl7| zF>y%Uh2`yq&S556;g!~-R=JyUqPbt^U}qdWUw=tVXfqyNnce4QObafo>vjpA4MCeh zXC6PE{eHo0qi-<1P{4m*yw>KrL7hO|^dN_SX^sHu0+Ohxd!um3LniFjo>-WRysB1- zkKmt0r^M1kw`!dYRP#61mM+CZ>fd?DjHnow5FVxg#C6%h+Ab5HAE%u@UhB-R? ze33!~pHUF2o}%cxxawA-v}Wb?5`AV}H5O~B8A72&*R$x3L^F(H;X@v0#S!vgk^5^F zp%=*DI7u#UYl=xv_Npl5nW5JYdS!>42F!`U+USs*y1Intny_RpGx-~4q=pb;wX#*J zstiNK#ifTl-slc3g%YP7)snKZDSE#hO2|a_SA~soixJD@@yn|rA(s#~cL<4Xy<&+; zIqkx72R#o}viqcvPUjR#7`I=eAFD6Xg477Kg^+#q~Dlgu2|)PQn*+4Za(!nGD+RlT;J0_Sc%l@|ebI zTu&rcQWjzoV=nS=-YzDEbbPqfLK{ALd|8^?8FopWWkh#(LbJe&+p zk!eWy*|jh|2vG8qgJ(df0D^_!*tfPJ)qh{QarhnWu9@BW(6JtM_-^l#n0rh#JIYKrai~Y$iLqr!0i9yyTgzR*>_)@oSH&= z^LEmbl2YYSBElbZ-X^D*Y zt|w2sj12wwxK^LC5rmsh6!mL+M4G#s6P}-`z?SXVi^hXAV(DrBd&UP_G^agaZqmnT zM1qR}{{)t0Jl_r;f9tkzI>b;VKCCX^h(1TZ{Yg*^mrlb4+I2%G2}pMAf|Jy)2v;!K zNr~EFN03Y)Zx|MgbmD@5gMyskx?eG4VGQ<850^hs5>~`mEO`) z?-Di*w-fZI_P@eYK@zBifHF@JBAp86jaAin!(C1yTCRc|vEHicjdg&!+@$C99Az>b zXIF4I=3XBqf9mj%wif1>5|Gw+&c&7D_<#$*Kmtpd42`e4lKD5Jn9Y}tRa2y?0n9Cb zVYn1EzOqtZ*z0GB^itopWZohF)g}Ymz;OhCBA#CMULk{8%pR>1lY9@b2`)LRLWf zVffPio<}MKy2+!NaOEMH=(xMRQjhcNTFKQco>SCe!HaN=jR62di{@tY!;{aSt+{w6 zq2@v6o7b<;prbgcAunIP{AuwILKcPuK_E0=--Kp}I9A^pL7PJjt;oxSwqZ~A=OlW? zJB0<2_yp#x39YNfU2iP>d;409o0lwBO-o*giwUhdN@XGKKsP9F#!XufXzLVs z!*OOF3Ftb{nAkfD3!{F>qrcnf32g_$+z7~QY_w)|YOEq?Vl2_nct%9@dsU~BJ?>M) zledNO5&T<2LkB)TGS}_Hf+%Vo!_#2=B2`IQ-yxXz4G^-gV^<=^&&8}H!5%A6_Ij+a z^3a8Q-;l?n;QU77@=B7Dz4DA-{sTUq0eJ@^&SOwG2zSd~25cYFbXKqMOdP#}07&*!EO_D10Q&Y|cZDV53cniqge(br|g>l$W2l{MnIo@d+F1@6s%#4;mrjQBJ zq=)on5r>q^;>Un8MlP*kTXl~6!=wLzyPqOC2&XlpZr7q6{1 zkuABuOjdVx2r0CDaiCw+AuW9B<*UME56ju*6pfop1I3T_gEuwT0?h+5FY61P|ODOG|~xb?@v;z zsm-jd)~tDLlj|XYC6B_cWz zdC}f(Gn6(jn~{-YEa{i#H!z?+>U%KtsF?bp_kJ@lw1)e$*3X~&q(|62`*&kSeXn+B zX?~bA$SW3m|2L-Pao*w*%S(?MTE+t;bV#pU(Y_s2Zo>#cG${_hiazmWS@&aQR$6Pl zy%PHW{Fx`1)6lSd2Mmu=JUA zDz7-KN_GZ7D8tF)W?{b0B6<(u_L1sUjNI9=#mKp7LHkHf#DxV=8eBfy&yuIy4lHpO zAQh6wDZ5gHa3**;5cm(y6_`@3wz3@@PCX*thy%@4==!?$kYLaD*|R>a+wTD|QNS;8 z=efIHncx^i?@PqBOEfK~DlAUo*@sj1Btz(RvucUpqM?d$Qxl$%`8)AkH=EPG?gBlf zZxX_d{mlS&sVkMJ#QyYs&p9iZBksF*w`=(rH)L$Zp>e?T%=Kj0%mCcOJ_y@rN=IyK z>k5VB7Q|De(Z*m@s>s4q+}wP%@a5NBax_3&3bGCtS&vc{<`{M^={+-6d&sCxM~yIm z->wV0y@^a+lmuBVVG(5e9w=?+i_GMgPQsK#Cc^}M7#n)9E}gHVP)_Nqm2O?ym9uNC zhL8d@x)_(FaBb@8|Lj`LoCFUxvy(btZW_RRuaDyCM{7IRI)D9|-BwW0_am}bzY)-D z&6`~sKnsl7+S)|JI4mZfYl=Hwy~2Ghz+fmiIN%c3pzP43v9+%jlisR&nmNQ&CeTY5 zr<+Eb6|?JnBonyui2zkVQu{}+h=NM@DfCo(AG;BOF$ z_e&pZ9~t(C~G^)p+~Bica&^J`lBJ0j_3M{G*YsViQsZ?EPzH2+fOC$A?9u5g8ZYz!55 zsdQAWzs7r%+;%3Ixp_%$cTFjCV8v3!Lx&~GnaEB_G&+2v*^4|bdNF%}I-su7=aj{rF3pq}`o^ZRF07&59Feb^!s^6i6r_N5MQ865>Q~P-! z+5?&RB0K$63Z6#DTZ$tv$?aLOW9opCD&aJ4F$e$m-n~>CD zMteR;K3F>LmWqVt&ZZS~4!G{2&Kt6=Hbrmmr4JBE!p%ORJmju-kPvdT^czQ^ z1GNgIh-ve2Hh?1bHg855acUyvNw)yhLohjND~a}hfo9baJVshflqq{&k@=;%l{T+G zG+5lHK#zOL=#K*+4GJL6JWGZKQ!DDyDYU(%2P`Zcq4U{Vo+vTv>+%dauqZ07*A~U zDEUke_#HCoDXq9oko*w-FZHo@osS;TCduu6pwI*Z5r0&C+)A2E)J|{pY1PB=f?vGX zTH1ePR3)MPR!V2U=cmDD z-3>UZo9NFMz97uWBfRnqDCKtF-M6*4RSvo$^#{p#2uOg)8|8HrAnt^n%151Imu#HV zJtlDa@B^KR`gkKJUauDn)st)@_Ue6^Pf_wmNA8*vF0MibH8nQvoUEoZ?bi8g?2Aj4 zw)O&)Euhd(Ry4g{4wUO6amSk)a@PC5vZj(|Xj->dP;l_Kavfb2a8_22mG|$pA8)T? z)+)6w{qN~wtIgA}5B&WhyLD5`B8a?;m_k)d6g7q7v+RW20UkVBUbBP)^ENdID!SoNegn1t1@y}X= zQg&iiuBM^Zja;NWCHC{>Odh_78j7T3&y*@Ds1}eqPQi3|gtsah2KPMAG{M1~-N>IW zPqd!jNL-GM^^s(^5p$F9NCn9oX;^m_6F^x%TpF$SQNYmr2d2h**0 z(YAEyZa2y&aly_?s5(PB6`QSmMQLkm+0|h#a`!|2z4{b?yJe9+t3o}@bDuAlm4SMz4cN}-UcG2EwqMabszJ*3 znzKq>DZFne@JD~_J!spp?J z3#`YgI{WG-Re~n+2$?K0SmPLoHmpmFiFwpJYU5^`|J$@yQFwc=w8WwE`LzpD$w+WU zYo`mW*O<5@Q!^{?`(XEvkj|c0>kZGu#*PkX19YwTeDT!MVnSN?<5TCy#%NPwK4HyD z%8Q-MmipI5q5zsE3Ozc$U|yT2)3kH}U;_@z$~S6<3UBO6kH znukpJRm3jka>oNG+X8yUI`p?+SS^8*2T%m5)TcFkU||s?--wwbhy&v!fa0`2@a41* zHD2r-)z-GlUgxPX}eupP5#*dm)q&D+y>ERvAI?FvlR37$ zu(&&JJcJn0?W3$dCo3&J*0XIpPl9vN(8fkzth^<&DZ;@00!A54lD@h=XP;9@HgMZ+ zF8)Y=J~3VX>UxdPtN(oh;^V&rX45@W4yp$z!wd`Qq`!aH?!Qp$`upc%Znd4LTlLTs z{EC&;B*oOjPca$tus7d?rW6arqro6ufQ#wW-#>54mF$AV%*|ExL!v)1NDGF6%spV@ zC08n@D{c6+1*q#@8e@xIH~GEL)sZxufxGUjXd2>*e`;PtHu(7qd6a4P>C*ClGJb95 z{~h}O7e;Zh;sa+BLT6_!U0hs7k4Nmvfm{>&-NLj7oC+Emm!zbnNyn+5KD`C9;XtH? zCF|8mj0>Z)gFBGYQyin zT)1lN?Q(f%TW{jet)s(!J&+i{Txqxm$7_A1+3BbOH|+a>0E*Lr4J<46Kr~hP>C=`T zAT0xk)$d&u{6VG;yw5J{b$M%R3*rwna!i4SqR5jNP;UQ#|6{kx>x*^7ynXx?DmtgP+=rD&#dyK$iD zq+M)z9!QdA)kI0V$5>?3{|^>`_@%0fwXd&lesM8WrRrqC%pPQa%k0NY@6~vU z6!=OPm{0~T6(fc|jr}>L&3n({*0bjgWPA;VynpEI4ZAMvegdxf#|K_@ai`?;Z8nyY zX7gCM1s(qNtDZ&xQtd_oqX*?zhm&r;=EKM+CpzH)>Bv*WF-p0kgaA*AnbDNs57<_wt2i#llh8mQ{h6K~4a9>G0soki5)O2(^G&YM0>u%T6SmI(x%*r_8w;p^1X}=n z+&HXsviU6NoY%=8`|PL^u3zsJBAp;>`6mu`H|Cd@n>%88o^yEz{@vsu2g~LJ<&l|0}2Omag2};!&8ROpHn1vVi%*MFP^+vOiYYSHUQUt z_wFzJTaTX$ZN*RRA_7;9%us45La-P5qgd*E&>;0BR0}&U0!&_Oa`#`^jU1 zoeyJlEL#-;c!D7`C#AGof%HzINQ!~X-4MVDJ^08cCm9|f?e72kH+4GlqK?O`ZNZ9D zWp`g+5D=pMR#wIcw7dtaTzNvn!k%kri0}_?d&o%m9m&|?YtI8|3jo0|Ss5-R4;Z05 zE`r@ufjpFg7-Iki=Iu&Ji6}rR(mVoOsLRU_ZEg8j+1P{x1woZx^BvU)`1KuL-iHbb zO^3$PNui`L1%Q7RF5fVy?@fy|2^Ba! zZ9abKO`4$p<|#3tbwZqMYD(E$t|mzg={O048TDn|S~ots(8$aR6CHMy>9Udk3`mS zW&u+7RA3?k*7o-F$>z)Vf9K}50JdV%@rsL7&%gkj%j%?)=lN2H_nzf#V{9y&Fc<%y z-hiB(K~jE>xz8#ffzb=y;TTm(U2vItvGVed|>O9I<5|+EisfH>q7EyRu5U1IRl*Z?)? z>ttkzNGpQj!k08;BuPj^a<%edS*vA11l!pbV8Z?t>( z95~%BoN;j5m`fk{ZhbxKx6icIQmWN9Lq-{&D}1(JCsR zL$U6j2B5VlPk;G}knz!qeI*glMdbTY0rmv#Iv8jqO$2lLZ_Ew#`5Ys`-04J^z>dCM zHHXKZRhePfhj20bz!k8{K*W>@oRkV+mDSQ@LOy@K3^qf5KUUTQ|_d7a*3$%t`l?^QeeZvrQ%$<<71oQ zT3vE^uqeo^#@ad{jA`h%@1fLy{n)u2J?hJs3vN}H#Vx(PZ@l#eIvEn;?A~=fhU5MlE2{>e%I+x72XR;#76b3# zHL7I?e9mncOcPLahGh4JlIo6O|8(KNPM!h^-(uk0?CI}+4|ZOGF`zmh!z1P&+MPXn z)p2 zMV998--3~y@fzCJN7FsvvD-Eg$Epi~kXynCxZwD)@77}93BCI_JG~b#7`EL_Os*OF z9lGxL)Se}C{Qmas`jjKkyh6LG<3W@KgwouONx<*{5%>*0zVn`*o{$UuPKJhtNI$%l zm6h^Ay8QvL7}V};^2xn6P2PLI_EGs{dxTicJgL7^^@#hy%21O~KjfJq&`CCRsMMX|u}+IM$tF9)DL`Y!yXk! zhACI9QO2M6sS|Tj{Qu7f^nc=$u}oe%{ZIt#gUCoaU_XtM4qtx$JR_c*I66LVxVgCr zffN-N1HFVG5D!)8=ba^=#L?gjf|tJ3)%i89*ow1-hK7O@re|nKaWh`5845Xq~`R2l#fJMVB#ht;Nzgn-g>yj#IIiul;>#x%Z8u)HjwNP zs5@W)JN^>U8^p;#sAAwG*hrCbebczo+uOU=%4Yv1JDVmQbhELxJlx!%JZ=wIa9tY@ zBWK9_Yb)jWDX`-q{sTsS9LeIgRduzwx#u~-r0VG$5V{z|1_bw7mWM z_wSQ934}f`wX`zw^XWmf1CG!zM!v>|Kxzs|XJ@B`_ckx&3+Q*gdiAP6Hy7|sKC@HxN=UB#-ZtD}Jr{Qa97c!m^dcRDV$l!|nE&85+BA7*y; zhu|$xDD-1eQYg6H;BVgmQyBQMM56$@y2GyAY=3(NtO*$u%Re#5Z2i@M_$R~dfe_)1 z9JZa`ud+}4E4a7%87Dwm%i+Dfd>?4qdU<>IfU{c<=vmNjG6@)lw}dfKLVjmw1DYqW zlTIY-r^bI&XJ$fy%Lngj*;*QK1U#^3@dUT(=U`32y3os5z2eiGZfmp zGsP(#^Wd@$JFAnAZKZ*W09FY=7m$f*)2}fdL9EQoKz<{#f?r4oh=H+~6xXrsN8a8z zE_O#ef1bAg_P9<=ie!gWrX5t^O`*HQ>9b;sA zvT}{OEQ#d1lv}!G z($mw!ObfkwAg9gC7_LZA@U7J62g=7FhJS+ZeWgSn^Z=Zgx4F2oA|B@tTmp+&MjV}S zy4yF$DY~`$w1xZKg$HM@z0;OLGPZ96a`bz5E>8i7oypnh3?RG?1bYCdBm$&4IEjwk zwO)03V1kk}_8C3Oz~F|4hUiOWL1q9Vklt;x;vv9E@oX?@dU#m3D@9Vo);mJ63FyLt z;H0J3px&$saNmY64Vsyn-r?Z^x`b3w=q5PcM*)H0@~TM#3b4`f@k-$Gzz&{l0eIH- z(|t26u=+}mwQuY0E-rwD$pU0K?@MV;pGuMQ^=kb^1ZU4leT?^cog)DX)aZN5fYK(A zO0>D}w|w#BdtG=SlgK1BL|j>!0TJ@wfdSkvWuP$43^v?NZtipOm`55K8iR5JI6ZLQ zvPQfIw+@bdA=Ze${rw=q%(4aYkV?Q!!R^PJCmoE%lQ~&yV9d;4%F5!Zs-(a=14B2F zzb7gt*7Nso^X|r+4yZi0$Jv`l^}K$6{~3;Xp2y;lnNnn| zj3Et9k%S6KQbeRlnUX1yLZS?(k|dSNP@<3`Ni?dEN>WLhr~7fq`F(%)TEE}@$NgFB zvz9}>->>1i_TJB7@9XKfQad}l`&iDXqMnyjX{}iC${l_bMyf{;2kI*Mm>QLl8%c znSJ|39!dguz3`a0`Y1PTBv%pFQ`v%R;R@$ieP7`h&~D|bRVk^dl&3kjx20g6V7HJuPx;` z46!es_NiPj7FW;Y{P(L33QB@wytNxLGIVo+SY?9T(eXq;y&dJHi;X80#PWRHj zefyFd?a8wn>wXqIjz06#vrof#inGQFSI72`%@kxZlrm1NhpZwNOFWH_&)+?J;LN|p zGUdC%e(_zruBfQFs;a7;oEC(QIoRRQfmg580A_=S4&7vAbeR7Y^(+1L_oz{v+5Xps z^G0(ujNJaqyaBaF3#}o=%1RvV~?%Q zE&KXa_l~u(8wcVlJp9~jlwE3Nng6_dR(7^Wx}Kh%_cr_1g>ba4ic7KxzE;U`Cz1{O zII5Vz%Yn%wt~q&}IWu2HrGsPLO9Kdi`i0kPs13Z5RP5~=wKZcLB(V(Ea$ZlYpIN)a zch;9RB71F69B}^SGmyoWs3VyX0e{xoBqbU9(8+$0o8nJxow#H5%9U^4ykTAyV@FG~ z6JET~-T$?aXnck`;NTPg$x*Q;SC{DmHELG}Fv+9CxH^Z*Ynq4rb^7wf5T1xt3G+NcnY1 zQS3^1{P1Ys(0Q59OaIxP^I-Aj&CKD!T6lPW z_T0XGiet_$o0Utg!gHE9@dOBqGxSI#K(HlSsk0YQ9$CmFL=l1y%$v6?q8uC%2i0snFU#XfV(}rBTb`8Qp@AN;FaK#rdUK}JP)sZSX z_3hgQfY*%OKX}0O$L=l9eP$YdL^}J>p+gtc#!%x>#UU;pY;3e|dq9GteWA#oKY#9C zt*)+4d^^iIXgWG+eV-}LoVnD}GOW+u5xuTpZaj9ZGqqL1t5=*|u?Hw~J;ek@0xtCJ z+qbc`wKFNPFC-cJ#tfaYeZ}h4OQ21!uIO}GV~M`LB<1MC91XG*%S%EvuOvO#r$WA@ zv~-nUIN3*nPX2+ixE|dc=gQ9d4eUgB2|2wZ4OpX_K;y<|ry&j%mt{DIn+i@;(F~%=F*r3R#rOqd=zEg)oY_E#6wL8whxvKhJyLGEuSf-kd z%`D32PF`<^b?cB~BKW`)drHz2Q*Kzn2eP_-`$VRf6g(_{r0r)N)A;ix$E``asI(I~ zO?+2fV`cJv#tHd2)E6&I7w4Z6cDbBWKW({Esxtf9PtK&*Ygz%K!sLCetoPQStcNqW z+{fo-!RNr>N3jLF#C5f)QSm7+Z)mE?a~$XQ0T<%1SqnI^c7n%oAeNVSdgOs!@UqTX zLtRv=%W__Rdmkv(;N7KlzYa*drXI0sLVvv=LQ2G8X|)>pAuglGjJc*0IMq{)GKC|o z6_T>{@{z=8id&7f?xqi7H%O2$wB6D?7cE(`Np_I2%ZAyrFP~y#d-5FG)bz=3kRChM z5=oD3WE@s^waW)mmv_X3nx=+1nmOsiA#6?G-`tuHCo@_>L1)>eaJ|V;r+k`bw!W{{ znX_jrU$C~269y}6`}E|IrKM$Dz4wD7NjRiuUX<+Jd;YFnyL5jBbCp9}#N!9se$3r6 zd+i5t6q}M#^VM`tc#f+-b@b>8hus%WJ=-`E_5S;#0Rsm*?^CKtH4Uf4PA-{k{U*(gp}r2?C>dv0rfAZfV)LgRdze~p#k@tx@A`6tvJ?p3MgX5RV%hkI@G zUiK%UlIdQ_FN<($jkF7n+C-QHo~fy+v23c}W3QCA!1?dPy^__b^`xbxyN2$&^84q| zrxh|E+0{%@Zv4?s(sj$#g}oOXEgHiPSrw@B=aWO8CQRG)|NVVoi|4qk3r;_K`t*Lz z+%2m7w0m~v4u=ve7%0r1lmJ=)!l5etQd*F9T8_Dx8ebs_vT9jTrhg{GKxT8Bs^%nba#kha(PW4JkRNVP6M@>VcPyhZ- zI)SUo;+jgnmb>oRwaYCeB;-zWVXVnCrCo`)Qi~aXtyQ-+IDJ!y?uvkwAQfbpj3xz7 zwM8l_Z7uB-{2umn-4>DD?LyTohZW=c#TM_TCjBR1<)X!lH^>f>;COS2=>=N@Q;WHR z8}CgU9hA9>c_V%-O5Yfw>s~qcMR|?9g2Iu!@+H?(3`2Aaf0@0izSX9E+cs=$hZx5! zOU=EmQc*o?Qj2%dlDnX5w285#-$I5zNnND+jA0OBDR>9 z@47!uMrQ1kDW^#4N}=Wid1$xzQmf>way3>iAmYu*cugOrB#s=E(2a3W^rf%3y11rc zyo}6}ARWuEZ$`c|oj!T;JlZW z?>=HUT?`Ueu3kNmAMdJc<+fUvA+YNo`Y8_R)`1W8zj}4P4P$#e_|=Vao?pw#3JHS1 zrh-~>Yi7u%juNYb^$UI$y!@UW>H6x`tF6f?A3hN1>a?Yo0<`$s@-ya0va@L6J(*l_ z2-pkQBwdqbGNSDS7?YrcJqWt+HK*q6*i-W9#t^7`O@ig9QKK|>Po{qJzj4DPYZKKj z_jL($&npx|{uI2rQ#V!Q#}|M9zA~$@uyEIpkMkwSI(RayGZb?}H+LPXV78)Ja@eph zTsFX5-L9;-Y$qQLw{S6ncFeZo3r)i(1arLcG0URY>e>3&Q|@d_pHd*%F4bX)WWV9V zn_iS3{k_?0ZS?LYM5LIk?6V~}{e;d?Nz>iC@4C&MuAuO_rUAxlBn2m&fyA5i^d-~= z67XI9`t`e_6F68(id7~%ZQ9c}Zv@u>CGc_A{To3+o9*p~olRIYe|{U74Cgqy;oJ9c zK1AA<;K*Y_Uk8}#qo66$IZ0k~?zI~!CgaA9Lx;RT?F;?-wY+@wskK2moGItg-uL!x zp{mu_*Yh)!)bdlO&Zo;3jv!iJlkZ@QQ3JBEL38{%D+O>F5;E0v+ctJ3m&$R{dIgVi zk&;O7s;XVMCs;x;QG0-;k-DEZ8wZMFEMrMR8=(=Cz}jf2{>d}z`cecA;PU_}g;20(5@kGH863}FBl%&%SR{ImYVY<%0*?LgFv6w-;C& zK45&4ufM;D;`}X99{hRc^yw?>RxexDnfvC-$XL%=yr6_!4pe1iVNoZEAU@?gdF8z&9f zxN#%bKMJkjR(V>QPC=A*^3I(*r&4Z1HeJ;T96xE25j&Fb!9$OGv{!sd(#7dtm`t0O z7i^!5(s3fW==$y3Q@(e+2#*XQryFfMoixjZ>jdj3k%c|VU+hj+0q}{OrL638;lhQj zF{6H`ozF#EQ^sVwzRa(%0J9x@a>dPTua(tLnUp=bw7!+_~pO$79EjUjse+r`BaOX{xDN ze2V?+_x z#+*I&{J9F^uT8P8PLNI3Cqfu^Qw=%vR-av`Y0B$MOWrG7VW9!EE3If5kq9zXsV z+nl&~;nJn2{MfDn)m-cR?!;nvV^IUc6liN{#llE|tusok_!r1#7nMUPCjSy^avttg zu8JP`dx^<-`=}qd%L{Iv8XFL3_3 z2r9Mi*B~$1=hj2MT~f3=C(fj=X~;yk;T>E&siw5BCNG~Fy#i8U zs)E9EIIgS= zFA(}e2uFlN5lAstoV<8(2-J7o&zCaytxAU&hk5eMFH^!A$_~oFh(fLzi`H5HYwK%o z#pN(=SRU;8Sip?P(#=f`9@7geDk_8r=oX~Xf$|%_Q-kg;{R?MGGohWJdycuf#+yug zJ-bV3WgS((-*R$p2?+^z#``aS_U_l#Lr?bW2XC>q=1!hEXYti1VJ^mAv~8R;>$1kW zDFZic+63H!=DoMOxDRlt((2TsM@lRJK*eZA9758TPTK$Mg=siPnhh{h$X?)lO&rph z8R`Umx7Bs5v@~0$!-uukb5P(JWV(0ipl)2hmX?-#L0gJ0&-DaPBGNzgKrKPgQ&NSR zWy9K40?;(_xw+C(UmPm) zJU_`*nZbz`Wd5kL@5U*s2+*EtJpY%EGJY?bzhAw5F~R%o%09)Fb+LYrloH=PNGHo- zn`oL8XE%mDHpHcaPx6v&+X6#7SUoV}ROK%=&SMMblZ2dmO6h-Jc>O@=I-6QhGCfmE{9XSWA2s7+&+)4L75dEzA9iv^EB2b4~A&^ zHYdj=K7S;Xika)zqOGPKoFEQZu6)q6A?g{57fxlGjhQiH@AJ|}lK<)ilA@&hb#YQ3 z@92(wpBZvMh^x84OQ8__D zr)!d#f~Yt5-%)*=df&3B2coZ!4Q8OFs;Z1qQreL0t>|W!x8-*7x6TIsrfR=0Zl)C$U&o&>6`>7Rt@6+sY!fm5)AmR&axeoykDWS2etP}VJ+COC zr0VUPH}i~)MpIf)1+2+S&CIMD_V-#_iYtK)R%JCgw4A)2hna?|M^0XzQmD46*~TUL zlLhKu_d6zTrB|BgID7Aq`laYQ^H>EfAuI&DwSN;K74`xfJg-%X#2I{ee(0aCX1et2pQG z^K9C#dy(m7*iY8n-$fc5wcL0%T*Wgp1?7|%*T@nGNqIdf+D&9CR47*bv&W!H$G;~yf-G8a(R zvgpaxur6`WU52tP3fbG@D*z!7IN&@ydY;%K9H7E?)&1Pe=fUWWA>d&*n6m!CqHWtI zqOedsD41sn#i605e6_a|IZ@QoyyN@!b#m0wWG{|I+yAm)BZG3NI;p=!a)byG3>nCS zG9lpR&5M?2rnf%J0p`^lNj7-MkXX}j;0tuTO2)6XV3p*eO%@iXk<>{!z%z7)iROZC zO-N`{=2C`++!tX^m>Ad*|Ni^$xMep8&?G+c_tu(%2*`{Uel4+t$AZ^`Wes_bhN|?> zhR_w`7U5Ci2cf}`500X<2k8iAMJI4E;Ezy3@r)7>_+Hi2^rqY6t>JpOR@%r8aS>1H z)~#D(_3ge_*$NyfC)X!PXE{nohTacS?t-hmK!4r3b({Oo{QF^aZy#p~C8405CMAniSWeR&RAQcpHh&*dLR{r$K5@p8nXKw z!Z1g?B*Bn3eFq6O-Y$rBsDc5WaA*H0Yeft!_`$KJB3ky4|sodoL z>C>k>)x4&ts$#F7R(Z;PpRY>4Dc+;L<}4vbwh{)ZsTb_n zQGVB)U{?I~tD(L9B@!0czqqW-JG`8?2yF>wxoq_`GB9vkR!vn+jrKqPRJ?fPPnmpd z#kj+yb21aIy>9!l`2ul!MHpK_8#%|d`H(%TfxQt{%{&Z{^g>^)osG3SKjwPB==of4 z^=jvr1#NLBI^i(N*^c4N_<8RK1SxIp)JxUByft?02zFbc=^qf#`CiR^k(#hxAI@Wl z8Wmn|)*{YY(BW(-&2hQD@Q zH>vn{d&}PXE>GNx6YVI83bEUw{Gt5p**8Drv)rlq0Il{W?H@gSxYp)Wxlie*$~xen zkoGqG^?lux^t(O=zqx2F1A~?~yQ_~J>2OJ-XkohCbX7-W8nrQJf8r-pM&>GW|DTlJlDnzPG8g0A!-oIwSrl_ zE#LhO3kwP=D6R?cH%^Xr02vByl@+Lz5@EdjABbopxeW?l{8_~ADbyXE&<;pALfjsZyZX>R|a zq2;U3`sqJ?jc;DuvuV@ypILtDUNfzKeC!(Lk-p9*TA$625v(!Wv0=xoouAu78tQ+& zqi=#2T)0CMm)cMn?RZo-=Gn7nU59zLLZ1`a*Dk9}DPuEU2@ZDUSB|LPCu=+_shVPF zlx#4f*JGprUZ1Jy=kn&V@>=d)Ky=8suETS?CVa9GWCgJ*&QA@2LdI}qYY{lf!Ocvj zH~D(Q`_qoM{Zt~O^<@wbg*1X6GIT@Sk-_OkC;ZVxV*|AP2h8sFr|{|LsWRo9d%z+) z)5Dm$jL&5d2*y_V0X$R8DWsjK*+qrMy1mI_|@39UC>jq7#dF&Qu; zuV!qPc1tv|q2))v3B0Pzd?h zQ!V~2zkF()9cw#+1kv=m_VaCh#-EE&c|yJz85@TJzm%G5H*qj!Qw&SsXTn$?3cr5> z-^1{*+@Bb(_)iaddQkUnf)&fTyryp#Cu8%#6q7e5(@;MSKKXd7)W1*Yyb<$8^tuqf zBd9uIyEdz>s8aFg5W<{Z(137T2h;aX3r03N#Jnv(L5b%Kxzc<1aCO-3C9aYM;t?(7 z&tPEqXIN=Mr{-mokImuQc$4D7XcJYj%2C+OFca(gi!f_}+?k{GIifidLjvoH6!cm4xgrQKQ~mmNYQV9iyq2as`B}4;fe9UJ_BzJ5H0QN=2@XsEW}lWy z$LFP1XR*}2D?%Q)h9UstHEF=|jEIe7q=*OwgfLhy0hq#7E%J!dw~ zp&#$kF@%b=e!xz-M^sC51KE{Alz2A+r+`G|IkOhRDar8UigCYUrL#eyE!BC=Ll4Z` z56qyZ&$F-W-tx_$X<{==#D|uoW3jOdNWyadz8%8^nt(}wQW0H2`gWz$3o;-V1e_&I z?cTGeB3`-q3<{30m*7C2hjB&Q$5BBh`d1%V{6zWbUVIuWpjI%N+qXLu{%`BVdS>TU z0V@-?5orgxJiin0utv)9j_a*klN^{(YuVgbH@Ly^-F=^Tl{4d;3P+`^Go$<}kFJqK zFCh|b?)jOw)0`AjH-i}AhTm`BzNRSez2EeeYS-nolsA;uRDLivd$48Sl=A=ldr~K6 zXF9m!I>Gb=Vb$5|^PIjIZE$vB#ODMPg#2WZ<=*6q0bkGDf~-13grI(RLTq)%2rk*3mf^dZd+c>ojt} z3F-4YPn|r;NjCE_6^rJks`^>c!GVF;_PT&gk$PXtpZb|V2t!CmKJ37uRpwk1{}r2d zaYx3T--=mH+BMBpD}8LG=80tuL)8W=^QtsI&463C{*8{_mo;jc zPdFn(H654s{lL}xNU>>w~ieD@1pKP5zLs7htgVKI64+4jDj~b^{{8^MMT^k3 z;Zsy&EXpfO%FE+7hFn2p&ooOmKWW|_g}YC3Kg8A)BV4_ z6yEnlli=i{QG>cEz5L;QQ73RS!J8INk!Ydrg2T%?(xd0J6AO<RR|y4ye8djbO+i+09qDR^;aRPvsBM~Ky! z`uD-Ll%O;BmJP!*ByAc#gguWQ;y2vfaRtUhZt;`2;#I45{`&flZ=WVSgBoxTJX6uq zuwhgNs7E*eaQFb1JZ@J12?4$vmPu!W_Qv8W#3cDDU>~Upl0|~{4-3HEmchB&+5szJ z(xmHHdayFA3E4s2APMmO2)+q;L)RH(k)f=_VV9G@%t-@YE-ZrlX4k^>k?l@A>vPVm zq{C}_`QgNq&!NJUfnu)RBBX3Z)ASBZOP=1xt$)U`eF!y?ZE;ylOe5*QYiUyYN?^u^2ocmYf0O)R zPl>TT;}*QeO_>6T%Sx|kXhPndN=B6(w6a-ok862Ll%GmN&i0y1j_dlgKAB09%O{8& zhT~pvN2qGX=J@tG&vKx}4%Kq3%$SwdINvuS=Cc3XjfR)0=Qs>g@G{qU|8WbibI=f> z3@@-L#q6hxZ;j5^qzA|X+W*e@pK=%fID6kld~q{}fHE`t zzA2`>xj5~!Br8lfRMvI8eExjRy3Zm+p)kTVP8x7RtM)eh3RU>r^MeW#jpTX*aWwqn z@+;uHP9_)G-Bf~upz%EEZfbKIvZwb=$JWCCX=UUWx6+2XCWr4<4JS*^KD#bWPa+xm z&h+fASEHjZ(_t*JV#NyK-ISoOFY%ysHlQ(}OtOD}&b?T~Tc(?aP~~V`^heW~Yp{HF zruRh$ugjN%ZY2%asD$NaP3CfYXaKV66pKawEp?-WF`Ruh8870Sw{How>%UqIkoHNA zCx(mWnM>8u&UQ{;{L<8m3JVd(LbnVn_-w>xG0&HbH2GkUtoIpM{y6$(JqGS%#SVY& z=lU?b(E1i~ex1AdO)?oVCnn59)?y$H&e0)dF6|RmVDL5jZg^BN&2fg>sGH z4}ZCk;7)~)8cxO}!>~!wwc>6!ftPs}g9UXQC_QJ+P+D`dOLel;)YN1N#5;B_S+vLy znf#uA*)YwkfB)wNh$h4;DBwfiUn z@~2G>F?}Kl!@G9%L48jzxvUMzs8V;U?ScJ2=c)w+1r-pJcw;cVzgC3ORSnIj9kH;o z&Yp;mYIN5l#a}5bpsD~y3bA_Kl=J<%_xt^}J`V;IRYQo)-1+&i+=EUQn0HAeZ3v6q zw<%M~2-$q!zUZHNFoB{`^2LF69no?ezYZhWYNfT$^ZAPXb>j7htWhIYg68h-@TvAp z9>T~Z9r71fk}s^CxtVyOM##%o9(%nr1~-VPJb~0$g#_DAl*SqTTKI_7)Y^ZOV8{~HxVeERrY zg=bjU06qwloRqS9nVQU2=bk)yGONnx^y$Y?TS+Kp_mH|t6`M19E}%pI(38ix z(RTiq&j=62ZT;w*qiZNG?181Q!2Laq*@H-*P(~`gHZo3zQS-Cz{Ka&A*;9E<|V|Yz@|})C98uef!42RMT60B2?3o zx&pCq!?K(jT_^5rSu5_2VD^vFl6-`u$&jhN2kRJ)syOf6Ul)iJF6Y6z2cOn-CZa%%};d zL2>#t3}1{0<{}^t5FBi7Nc{;KjoOR~ov|CXD%Y-D5m8{^Y{}Mt!XKJ|An-}kvA(Ya ztkhz~<#zx5%YwOM{@-rVF$ocWVqmAK$)hs|w7#ZJBMUu8XaD!-r*qH#5zC>vFKlr$ z`%R1p+WY@tNiAK_QT8lYmwFfO192mCPl*n++O8-T3AQ~6Zg81{gAzrBk%fgt+9P^I zaYjHDp7ZnbTa%4_SXd{hE}^FO8D;a%X_+|k#PC9RUPod-pUW+_5zIa>wV)Q}nSz+O z)QkQiv`JEHNOD*P8iGasbC<2I0V^jE^>M;jzy2qB5N=KJ#r!WTpBKX#s+zS#x)q|H z*a6`KaRodQLgdTPeH2BFpC)Y*?DN|LK<*lv6q?h6eOse!ko&dR)F%}F0fWlrUD_A#V!Qid8k(uxov>}$B zrG)NyS;H9>6HCm?>4b67(VA;hG#nJ?E+r-T3`06@9~z36fw9V?DtpisRM{svOiNdH zDK7->%9qAm*qMpJQ3tb23$gu+mJi`vdp)Q8%KIs=M$G{O(!@R=0ghrjs`B-m6ApKy z$-G$edqifdvy6nac*`}di!q=CB`XttFu@7ZZ$_gc<A{pvqk z`FN7?D7<1=e&EDsS{8Q0CDhf;tYr6W1D8aPQ@M>@AN7t`8Qo9r`OV}pD&6!_C9hR6! z(8XJVpebp(7SC&hWRnAUhi;a>g9ishrDbI$qC5JTqo$9i@;44k8vnXZi*6Pn4a7sA z@V7YEj*x!@@XC3(7dDLFKI`tj2%YZ11HFLeN5)APx2A8HF>M<3_pL^AInP!Q@w@Bt z1ER{1qZE2 z-i5|+md-}fhjZ0hvt}7CGTy?5eJp)QDC(xC#$i{}OX$$$c^#tP5L6UIS7rM}KJT{5ksUD?XRCdU}F-qr={qj{mR-DM`t~V8^D3z)A!w zs60@seUCF5J1Fv~fQ1%Hs-ZC+#?b8fg01jVpezA*1}_Q1*E-Yc`yjf2Fez)pUPUz5 z{whwTQmh2%SK>Vi!;s8ps4!FvggT6agCV$mn`kBxN{cYuuy--CCOwdB-da^%O}org zbQ_#i^!rSjG&l(IivA>w^4;FOiR-4QyCg6SI|*nKN_|GvdlptVsxTOr(Sz~ToW!jf`@$^_fNE< zne_C%d-rTg4E}7UgJZL?@?x9T%8ayCKvczt)@ShGW7(zN+EwLSSoo(f2Y@4ZIzVbA zxijIqlkh&w-KF>-KL0Av4oIJ3GFtG^2OHoP-Jeb$IdXnxB>WMGfnd{ZdBno2y+}h6 zbigQ`-*;#1m@!|Ai;@07C_IfWY}~}1@L74~f>e5#)BLXJ#Hmv{D%ZCN0N?m{gPiA5 z2#SXTNwH{_=JbSxK{`+I09h)Y`$&VZztb&dv4S+x|?VV=-0ph zKY)EJ{nniJw9)$N*-{EJ!L_ihMZ;)>>#0-5a74nVR#UcqSdaLqQqQJ;32bx#v@dh< zR+ftfOKp9}%)vtW-@SQ@;3K}V%aY4YH8-!%EPJ0Q`+dDNtk=oZ@nqK7L4 z0=u9*p!CZ}{q+ZcFJLZ@biW9DFBzdacI#$Q_-e3N8hGA$qc|gzOKg4@DTjO2M7>E+ z_>diDL&%nJogO`l0-tV+Db}Hic~pwlH930`)pszu;Y-V=a*gb358gVtwrsmL==g-^ z8{Os`bv4!D}}cD79l;QM_d^fao;icR`lwi=<2M8_o8a_8uW{jeGGm{+9Ak5ZRxs8 zeBUz-lp$s?~47i##$p8dPtwc=~asM`iHuc_RaUm!$IDXEy-@qfzE z#$ba*cbewTchc~;8|9xqq+16NPX_a}+}7MK3OfbvyhB5Kx`Sfzl`A6%TW$N;vkbB8BQp%6YdhUfs5q*MSvu-_l5MdW?-5St7>rcVZHs+oo{@HBeA8}&L%i+gnWWnFv zryv?%=XBk zSe%}1++b|nfp|!WVBMpr46S{Y^+psCW87=TwU;LbK1ir z+{hExA?~8hbuz6RDJ^B&P_w|56VR|Gcyx)$b4@Pl+UazYgEId`aZ62FUNf;^z&Xzr z(F7_CBgB5*hvAg2clps5E6b{E0SzbIjwGeeFldR*e-)`7q%*NJvs2gPy&JLzcxxgi zGN5HJzeauzrRv!~+M;X2o((Op9b1Y^-XNllUh^_Jd90%1zN9A&yGpmyNAOL^Ny;si z$|=RWsh1|!ba=Hs%qcH8IC!<+6Dgbw!r?C*6ChoDr1u1|ob=vwI!*M#YpPe>^sCO& zyXAG`zB_jeiptaLN|_b-@oF5ksc1O}(y4^nb)!nP=%HD&jp*OX*=y_S*2b25%e;@5 zjU3QKKWHM*dUQrB(%LvYI^u~H7B&*?CvYH?=?~HfHhp(W&89&tC`J$S56orwgj;T| zeeH_zXoQJyylr1?ws)AFaf5W>=clqp(+*o>3`b*$sJ3Foog3<~-J(Ca(<^I?NlCDL zEy%Zo#m}(9L**7n)4hd<4u98y`vu+(wVyf)@)^xc_Pkk%I2vlk_79Iu!+$akri0Pe z^3qahwj*)QJ&D+d5{y2Jq^F}pF%-WyZ{NVvFSap|kYnUvl!YNy`+1E`Pbno~LIGv8 zqi+c_59A)beKYNAWwCH+ebZ1=6BXWPZVwf0XT|4d`_*y@%ZHtvC)PZVLYfnlJj?=c zMg^A&ZEEb;wq-&7?->TMW=+7Pr0I%^Nr?9^p;yLDn4tBo8E0xT!X%>V7obGvP7%Ot z!so{ypW=keOmz+OOOh4NY-a^>RNW31s79yW#&n>dqjU!rdZ>oqC@Y}$A#=2i zm^>GU>@-9Lg3jjpn%JgRcVZ|vDt#lDIfh|jVQoegcFm^MXpw_Mv*lQgd~o})b3 z_Wpx`8eaM)_XF+))|Ht~HcO4&`n)pUM>*c9=+V2qt4&i)8}>{uIB!~_H+M(QHbrOO zN0Aq;Vgi~T=*Rz2HD4w5X%^0x$Mp3XTE)VN&>GMDI zM~T|O|LMrRyzEXZ5zc+zlJERK9l3kPJo$e(mXOzXhvw`rfELrnX_Z|%hO7;laPZI} zk0weLjFJ*}XwQJ|z7ZU}1(xvH12npX7o~s>yNPqasXRm`w^FK3oFCEsX?3 zs}rs^&x;q^j+i-f(Djr~`+v9P_KC^UB-sdx2bq_+LxF|BgE+||MM(@!e~Dj|Tts6d zl^a2ZlDZv|4L+{+g$H+DPdSR7j1d%JRdMy5(hBPvOln}Fm1;E8R|u4T{{CCHZOdn! z;h4qBKmnF^%`m&D5-4#XyOdEYo2Cu%37j1Kwv{SUeMlp^%5~vc<6**micqjGsy9tc zd^sqX?8QGbXk(=Ln#s&7AmY!0Lldayw3pX%o)bS8tsiqBsQ>Rr^*QNh zR)_3u{f7)mv@4HZf?trfrcn}vaN%isyw0MT!W(C+dpSG;KI114y-D1>7{h^i0jxn+ zNG;rLF>RWOMPI(qv)@%n+`^_H0Zq@aGMJp@78bJfj+X4w5VLfQ!*){RQ`D<7Jc13H z-1z$4yMCg}0?Ck8Aq)N$Wj$(2sm&Teq!Gax1HeObrhcLfITH8jBfPH|SfEuuKQQOQ zTB~LUV8(MJIq|{ZFol072kvQU-p3e$v1AGsQxQ=$QOP+@+<-c!Y$3tVVi4A>S+j;_ z=(W}Yxs!IN-3fYcDh$Q5XX|AS7%)HuBXelp_Q;NE?FdzWD=F!BX5QFvhWwyVF;GdJ zW;XP<#Jk&h&?kGm-f`&uJY{L=%{fbEFHunu*Yvvydw|8O0(1&m|Hu!|4P{v?Y2oTJ zG@prH$;1~osWZ$Jc8Q%MyA?s+1sW86So#VS~ZYL+z|n z70rfb*J4iw)y90$nUahZDJ?DHW~ff!^brpqz&*;z?MgUVz%(5g6zLLNU=+RWqwJc$ zB#7DB=O+bg1wc=a95cpvRp~kz48G8zlkdyS6dQP0Y+M}ILTeO;RPn##<@s)eTP~H2fh!=_G}& zNsOV_#;rh*1mGo@1R52vnZgBh?UWX!FGB+l4l_$eeJu7&RFv!q6Uq+7F1}yoI#@M@ z={giZ&Kmw*zb>S|JFmP5mhpm`-)gt31fNedY~N(qIcBY%&cS6(2UQJ z0g^7pYY`-P9k^ehMY}#my+7A2V(8GJn3CfPcG2r7+9WXTs`@q5IZ$c{dnoI9^0$sq zLwb4_-Bwe7V%xv(*rLQxujSQ&8vgls)dqDtGO%nlE9dmEH_SR=~Ux-w&S2K8~ijALC_oxnY-+EkJ=ghhauNZx?xPDo6&{cH02EYaxMr3);@NyhTw zDG}Y8p@S3le$SXs{^xsvtdrLGVSa{r5V%!C!w-^R#K+sv)f+>8M8-6^(7u8gKA(nL zS1IK&lO}Pn@7w*;gGnM{4hm{9Q@SAPed^|IQp8lE#b_3A2;Q@|Z?#xO9!+?(X>>jX z|91pi6?U_V1@RMUTsM?uj7!A#8&&qoukyChEZe4ma3{JGd>or+(JlKo$w`Xiq>-P= zh2t2TcVY04AD@uR0+urGQ!uMAQXDLE>JI-TJ|CxjPi<#>O9@Y zcP%b>X)ydjbjePhT$}NqRp_`#7TwGipuPZDkWBzqALQ`FRyyy3#;0;)Z zz@~)SvtX58KTa_amj01%-?~`etBBe?d;Q~*_LU`0$;r(>Y@=oD|F~_Rw%A@jB)Ad! z=i+=?8jCjj>&t*No-y%eDVQIiD10evrf#cyd5*(hSi#(RY88kBs|1xX)C}&S%t$)= zDB|5K^|v3k*C&n4`w`vSTgmzTx{}pFm_pG{3oQ5U-TSz?Zd=HYO53am^PyP3w3U)y zz50Oz7XEALk|oY^;rgvxKSU|_tKrbXwlC|(-`i#*^S(7(#m2_!>!d0yf(gU-*ph@i zfB3zrnEbQ;oeV{^;PBa^-AxO=b??z*mp~r>R5dp@cL&6~xVly%Ed2FKR@c1R<_|Me zQtZ-`!8PwKrDvv=R1v=%^4uhBR@811ZM2Uvk`7TyILy#(T}%rvT1V&Z zx1^#^e7lZSG4%bd={EJlhz9Ow3TYp8F+JDHc6^={UGXGBH;Zz{h8~=O<}tXSl$(C_ z728nE2|_U(`sTrVd9S3#c*n;0Q0*%KQ>%wf{Uc4yS zQX-1+mMya^KJItx^!@zO!1eWSlVg$scwf5oFPai)9#VJw_+qEf+ceZOw4&oYYwMZZU5P@UIoQO= zsAp?KhxYl;3vl+VY7X?_M28(lMvH8UW&t;FthSbTA}EtTNd5#rNOWH*9qpP*hDJU% z4r^nXnY4OiSf=}z-9QmS?sO?j^m5j$;cY1$%XNP)w!W5&C3lE^^v#@w;PjK&AEL`u^|->1%OF)Ak@C>Nmk= zhS>}JFJsWb5bp5zE4%2r2Hk+LCtH*CIN|Kr^mWdE-hEvZ6aWq_>gtg++(2YkP#?BG zxoGd6J-sC*HK3ue>EH-ILOUgw)hdLiK#q4fyVkOXQdZiS7erpFl| z)0W9W%$fVc!#iR^ZFexApv0;jhew)TLcvPFk)w9dOj$_CPW&!$VQ9!@PtU73o#?eJ z8X_I3JL$K1B8Bux{2h!G$#~~)3PW5-&NN%j+E+h~2PSTo_L~lKWwFMzEi{&aWo8al zr_P-}1x4n5CSm^@8tR7A4;2b2ZVRxMeY-jAy|R5R(_=-mUmMclI@mex8esI-tIako zc#9V@jExG3CpFdZaNfp{UPFec@*wFW6a_6-WAf~B$S0Ck;_MHydo^hdE@*a;L<~lC zKwP%@@$tYepSTaRv*BTQrO1lomSwbJTHP(d=6CQ&CE*ykaQU+Dqel)Yw#m3cSrN!M zl)9UEmN-A=N@~ulu5c43y{u()Q|WBdc>fi7vH5dtelFUY z9((KUwy9D}G2XSCq-Qm0^ytI*bWv}pI!@$PT)uP(vHUbb4Mc;7InYqmvra9Co^ntZ zyk5I+WO<^~3N0;WzKxbZP@|gRH48-dPFZxA;jM-UQB1d?2~bW}w%6$cc8_|}m>G+? zqn%tF1-90@b+5jEpHx$x*VFn(8_RStqK>dEy8cSJ;-}A_(_Fb2@WBnUUVgH_6vOtR zeYk4l3MNO6!$@Ky1`yB0WYD=9!ie0Zf>#Qi=cJsP@VY|);zb(yLYd&St=)k z+{@pE*;E$xZ?G7Tc*p|d#SR`cNcV2UUWVIHQx`BoHF;IcA5qNaCOmFK@Vfi4QZ2Rd z>AO5fV;+k8q`S+9Cu}H7aHM_&22v5NzNO80S4qj&1O~S~&W8`P5+0a5$%?2H8Oj4{HbiyILqU_a#OLU1T%~p ztA=NK4;(Nc!sPJY@hE+C)oFe69!OwYP(CBrNa>#5DU5C^UgvpN)FUr$p4u!sD6OAS zkZ)PIZLY)mlBz=<4c6l+;Fl}e+j#6zmSI*$f|2dksy~j7bJdMoZ~Qk(tW(J7|6dm4 zKmHO>^nXK@Y@P9-&C3u*&`%lw+{Ti|#-VQ}GzK{8?JX(sNA=qnB0Fo={f#;B(MW`% z``*#9<#@rNg9qu(G`6xj7ZDM$<($jWqa$#p0n|4}YhnxoH!z~B`qeJcG)&Ltep;6i zj3tB%YVu_D@kLe-Lv$x$?!L~bgx9YDlXQ#yXlR-kfz8N?v^?D>J{6LW9|9Nd3AP)L zADPcEEXx(R4OSK4j|$HEkf7aP^>lC)DCvB4*BtD3Z4g|yC%Mw_AZn{?zo3srUJ9v2ptOZ%0 zWLHZ!Y%nw&@o@$LQv3=?>z_;}sxY|8JsA!}d^&6U%Tz4f^agg%ICuH7x|!LnpEmeS zXjA;Kz2~kq_M?<4zTh)9I%T3m1o`f4?$}fslQJ1)q*ZShP9aT! zF9uFY(C{Pe+SBrY1=KK?Pige1p==J0ofOlSnxeAbZRWz9M+%|DA`-8=XVC^QXpuL$ zfCif79mIUs-+j1S(6x{y+8!*e%M+s?0V>=MUj^`@%0+~}YqVBM+2Iv>p2t3NIzk-G z)z!Oucp?<2r=W@pl=Q6~?V|My%Hud)CJr1OG&prlIE8Gw5S@%aA;ZZNM#cQU`aQ zub7*_vloLTeh(9epOe0^MwNn)DJsHN#&Uo4svF#F_d&&%f*ZI_)p_>JyW9M(_K)s@ zowJAJ+@<>Zxm_=pkfPO@0PohYV8H@ePc>Rt|Iycf4Iuyr!Z^L9<7p&E$+;dB8z3BVR3R;U6|(h<&Cuadrv5WVcpd?W$^7uLTErr1Du z?$ACQ%WWaJKe33~%Au*m;7=ou1h+jF+b1PI^jICN%PgW76fAAxVzX0ZaTCw-D@0ty zE=C8tFAj7uE|v~^UnvNHPTji~;Lw_D`|BtXp4aU$SiY4eeil7dFi~Pc65(ZBHi1@* zMwFf5v=>Beczheoau4T)o@qme4O624L2tro0VLw|!mD!A6-hpx4&N4?PF+!ILxM`3M+{Qk~uLPE$=krEEq!$ z0gEweLj1`SC)VyRtE^#$sV}4!FNvNdj`-<14sRW)0f5{GlAmaI2zNZ_kS2YhuC2(0 zjc4zcZt_=^^^H*HiU8L|HwQKw1A^=QbN?%nk12iUnL0XV7*}LQA zds9C$&1sd&PbS%WbUXmLz}=D5_#Z9y^jEf(eEz#qJUX!EdHd{%%hc5iYGPUxe|yAv zVmgy{OdDcDa`H&=F*we?>n$afjCLG4r78!C%X90GU}GXU>y@LQ7LY~+2o<_5KBCXW zg3Y$JdYy&BBSd-uCpgPvieVQkrno?(l%xd*p7HU~L<5{SD~&Ld=h&h+;6#CE0mmG$ z5hV(Z&k3sK@j4&r(W3=@v%tJD1ZJS1FH*{ldh=$5#-*9issis{hnobWY>Eg#+J4!hRM|@I!6>mGm-6HPT6MvcqM2u^Hyf}huhj} zdzc+{Kn~i<7q$!|+IkG1EzS)iQutBbpb-!??`_-|Y$LMGoSQ8Z1f(Q)LhK{yaYT*x zVC(S6$jTz+77P;A(lx7Bt>XB%IAj?fB&W{#2>z(-1OE0RRF`W30mqo?q1JiOYB}X+ zb;ZR+pWic|Ganaypk%nCHs%r>T)g1!oI4=u4Iv_GX@#b!|L(H39u)`D<^elEy6jGB+0I?Nat4JH zrQQDVKTC=s91`Ds;(_+}*L8JqQIs#SW8k1%^bD+`JnA-<*H7TI`4DY)o*I@;p5y!G z$jqJF=vnjj$B#R1!OuOA3gdpx+eEJ>0mhZ*hpBv|wAX{|6|ZzF?s75($A&k!I+f#{qW(f|C9hdvE^D^&Y;$S zv({N_uf6sz-tX7&eBRIdzOL)O?((_@l*N!|oEE%sx-u`{BUBx&q5WN*H7A1bG2)*3Wxm=Fm{V`2GI8 zxQLDdLKNhs>YH2Qy6dDK=#Y6bwBAn z1#J~lsH9>Xa!v&V+}f~=ra;;hdnzdG#{vrGmzkBYikm0(960dNr}0!&m)skiFkh(y z#Uxv1DIkFy)9Hf-S)f3$lwkxp_qg6|Q_n#vY|5zu^OR9(VOH=42M34zT!jGx?u;q@ z@%_ze;1t$33Xpam)NLtlLA`iA6m~e&kEl=5;$f{_JBWjdqH@IBiqeRs^qSBDa8+v? zeW%@iwXkpOLIAYTMgGSe5vJ8a63?3TB*0GwsE`EtG_2J>vUtoG7-_oRPr!!H;J4+v z)&%2tdamHS0AV?uhIJ2Wy_;SNGGaEt|Mc#IXm@B@z^wNuMs{e~a=4AhwH8b0;w}9j zs>CRbOnKY0!dq?l%rB(cb7rV5B%O|@&!5HrB8)3aTNTnU7CM*3ij($n-5FSj3O744HwXE`ighT(8S5Y;nk0kGvYGeOAS8x0Z%Y zGywB1h2m-X^4bp;I>NhIc<=}Skt{ELj5Gcz?iq`N((9LdRFF}OF0<1aFap0v7FT$= zovk>9qPOi4{+`U&z+Wk9#LTm3qoFwd#$1{90qR7bof+$FjBbhgrE3Qb#orj4G@#OI zz}{^!Ngw;6L~!YNJ+SMrI5eRV^^W3K4?lB`9!Mn_F}+H4^`=H*%4d2W*jgfIea$!Q z6Gl#e^eoB-zWqSP=FT@z`gjQaG_4JWhJOh)r9+qRPOY8d|Hm=h9Li%2pccvjj_59- zpWdyyzuKGTfX$OGWm--_oy=c_T)LSuAutWMDK)bVld&=!d@39(c8Y!SB0iRN1@%aI z4R9>A)kAFa!3)aHLPKa#n~l4Uu_4K1Z9;yqEXlt2HmZ{Yu{5rNgz^vu4o+ocih&Ez642 z4{o-y`iy=PtaUHQ1_t|jmX@PH0EeUfpZm#Ylg?+2mER5mbMi+% zL~%||N14TXc!)rA8$@>f?uB^SVBS0_UOmT1%>%hyHBV#%;KCR%vaoUNv_SQnEPX@q zVx}dJZuvm-94iegjliDA6OG3)fA|G{TPKH>%)7kI|9L@f?!!;x)%&H%5Bs2}uW#a~ z)fqC8)3dTLehK9u=^A#?$eo*>?D%7BqnW5Q$SVFTu2L}!oQ3aU8wn>Ua!%Oh;TjsJ zNeeR==0X2^d~=1Z!J_l`2a25M^N12LaERR`g$U&Oy?ZMQmbtv-wE6WdI7E<E~qMQvXm_NSOL}C5*0`?)__r2KZXqM(L7kvGeB6m9l?(ne2n@zu+>2r*^6TM#7=KutCl^jxK$X<#XyUZ;1b(GD#WYd?@v z;lJsnZWa))4Ad}3=Du*@Ix0?(ftD0~*{IwK{lT$pO=i<^Ir^x6{;yXFDKL z@b+HDAUYfidGWS$LgNa+A%&_5=_kuw9DVdT`dTCqtXSXAK5j!e12rI27teOCTpJ0F zN*rguLD=v@ge#3jf&=50+xoXOhD~(DkbQ!+h%jJppd6f2Mia6bU&K^ZS4j~lyOhWx zlQu?-tSUrgtyMR`#+?syCD=+hx|1k&B5sg+cLwkm9sJ90(g!PDQZSTO6yqN~O@sR& zM+W2}3uZzL*U7X>Gq@{OBBE$|=isPuv+O{!2oHjP1Rq6e#7DLNK7ZS`(39S+{|J}# z@T^T6H>yq!J39Pc$dH7+eOpM`g-LZ_hr`zweJ4IdTY9 zB(Z-5wuSg}qOql(-p)ib_z=2$2MHI4KU2OKr}q*w7Ao40!fd)aK@PN3Y{`$ zOMbT{rZsvs$k~V=qP6eR#fwbVe$J+lp0|Wv7~bQ0exJ-1`4v#j(rX{v2}_YL$$iXh zOz_U^cl>G$l?@@yqA#v-Z3;EoKG|emA2!@yy~WB(rbv_uKcUO8$3UM#tsFrbJ6BKh z&VCASRLYpMS>zIh*h$!*4Pa-7#JsxRrE_OC^o+G@gTn9Jyt%MryFt@;F)87A_>$mE zbB(~4DJd&HXyPTX_aN1c=xL66J+Yv95Hoh}jIwKDd$Vjw>q;;6mjXwI zNkyw7VpwJ-v&Ju!59;6lDJvC_8azwEdpX~$niE3m#GAeo-ShEs5v4Y7W-63^&il;F zXWRvt=n$SMI-$+pXFB|^769Fr&$1R?Cn8gycw4$1L_=2_H=zk~$N@F743Dg+6~F|) zeoY2vYYJufe> zw5yO_iQypM93JMAwI84}YM5sQ$=%iRx0jE%F>ikia84thB$6=lB&oD+WMRbWI~?wF zOYc_?D5EQxu5W@0G$8exFy9NGnKg?)Ab)3qz{Pj?M5jWak_N1e65?{Z&o6rL;9+%< z`GY&Z0Qm56?SQoRUu(h;9q7-$*sYt?4+}Vo@v%AG#$|;djUXvP@8a`xEv=RU;Q^7N zI;HWav*TkX!M?(B^P$!9XkxZSjus5b`EoK=|***^GL7c1d z(K{7ULej2$3Rf*o?_+e&0=Bd8)kX#4M7Cwi*YL|Pzys0$kNWY%`1swdGU2ks_fvI? z{Feld!i=-7{k!Rdy1zm)Ba*eOG9hU7cug-mEV=Z&oiu0?EX~1~rnD3lU8ud=^wwoi z@7{C7FxR;A)IZ>b?L3UnJPZ*h50PA|U8OgIM|U}SdD_m#Htt0AHDZTWbEoH2z`!Cv zB67y@4e@&fV@!#>BDt~I72uX5bj7?oF4{qM&1hg#sSKn|Z?dg8(Fd`o0N{Q_xiv-& z%N^NvPMi<=#&&L@EwCDEukD#z^B?K5R+D{7u7CY^DsPpf6#|0jb8Fs778DbT*iU1P zc5+IctoqGtEqJop)CVM=Xn!_O3h0wtbFabTKAM& z`}e05RKPAvW&y;To@(Ovd)FJ+ts9gsryuOU5r7vTnAJY_cYXY^W{G?wZMh=zCmf>k zWo@Nyw4d>ivyzX&|8I1b?e5*iMuW6awV8dZc-xF+pmaOChxxBC-o4}?DC(;OkaC>$5ZFp|Ss`@P{>3)+=JBC^*YVDBw$Y% zxu|quvA+J;PF87r^Ak;!j6fu`!dy5D`CK5q);eGQ8S^<87L*hQn!b#19V*}GxGsO3 zUz!UQX;g09hBydcWb0odVBO(SjBF~P!gnrdomb-d$GEAc35ulQ|5o$2{vY|hWB&93 zRw1^x*8Nfc@U|BGuTmD(z2^U$eD%YOE`~xvy(*Y0M30JzQx)>YqLsT9ZBYUy8!*5- z(-MpM5JTh~llcL9);8IUVOpw+Nq?vDr8*A7ozdXN^~$QeBoc zVE(gkWaQ%dXtAAo4mT{0CBHFBcOHid%5 z2b#?2yVq|1zJd!Di~}A(U4AXuK_fTQWu!#Uk7#QZs&uTTc}Mij@FQP1LVf)FLJSK~ z0?}YesfsQ5n~5nKEiABpH0swBu5@cN_bYbU1ELIp{v-k*@P z_}cM;i4t_IQ+}MCV$U#&B+McW`YdJ8V6zgr%9=VSEo&q@%zXABYqiKF_K2ZY@>y{{ zz$wDhRw969fRqMc1$g_pAzAiZ1?CsGe5z=aCc9&%x&|j^Qor}SCbRd|!trR$6iV$n>oFG&sG6ZFjdBgW1h zvK@GM)J}JHSAqmFCVaGZXuHYViX>vLj+7up`8`S!TerqFpy590gwWkgqe=F>d)?Q^ zr#i5(sl6yI#DIZ5xzgJ(G9f?s1^eE&e}CifMVtbn?DS0smcoJu>L7kY1BoXW;qQJx zR^DwWEx0196^n#vTD4^3>vNi!q}aM}=F9}WDQ#UhPv<#Xds?@(=02uevWroWRULS2 ze%}VDBl0>D-I+vi5((9D^+Bl(t%F%oWJBnObX~~U2(Uy_(kluSgi%LcZV)oLKY$I8 zGwLc3cHh>sR27&EN~Owb{((+esLZfT-|>nWOvscGw=CZYohSaXZ9b0}lEx%*GDAwl z89G`6RPZ!`fMb_Z$zY!ZN{*EddGG+3Eh#Z%264MYl%TNMYDR^1Xh@K!xSx2Cc|_}O zCfO3%yOv|M1k8`bkG3z#(%%Vx#?VqcU}N}+buWIO!qwe*{YA1cGAJH35Ec7qw>6%6(g&*O{UlK0vsM61PMh0D6o$h(vHZ@r*Hw}oiNse zF(j#!K(4%`*Tp@i$X0s<)^n)Yui5}`Z{Hp^;Y~OD%T>r6M0&@pP+lojX9?I4IRv0; z8)sQdsYIlB%TyB_t~E(od|l5~MInZp)X%&29Jn7^m3FE5v&q6h`LW3q}D4xPilNI7~f$GKj^T@q9Vn#RAqMV8gaC_Lrqe zp@PwO7zsEmmXFyqtTCq4<0uMJwE7 z5>ubAkIF^&vXNp~DhoMj4J{1p6sG-RJ;@Z_1sD&E(|q`mj9x0i0lW5|R*89uCwb9y z`udvfXt|h+hJ{;aWz(igwjf#o=;=-T$iC@Gu7junorM@ZNC%XQ%XG3`6x&D=1Pi!# zujrW|CXpzb!xNQ2te zoQG9Ehu^*DRnP^CLt{iQ3Y9U_dgPOOYmivw|}(@#m6N zc^}MgvRz#pD>U!^Ge_88FtBmo+pkY)1w24`HiV^%4)^2HheRmK)lVFfNhI??H=Pl& z(ikc72)mg65;2$H6U6W=Ole)4YUYcEGs3#zwgyhaIpEgBm#9xW@~ONG>IU9)bGji+ zi;rQ$Q4;sguxpI*QNWU!v;}>7<`vD*L+TcB)EM}#stj&O3f zz5RU@O`gmyA@^c!YzI{ZT9HOQ7Hq-DKg4EoYB!gwB&hr&QYp2Z54uzOA00OAj`K1U zXuwA9nU-@2g#_#8WtV@)VsgbCV>RlYPK_=AXt1i|w6%|Nf22Qs&}bjmy^5bKGQrtM zj2V%H-W|iHw9E@$&&;_XZo$TtXNXWt2pU56S6Q6Yy{b0-Mb}O;?T}=ce7~yuU(H~@ zl$b{~6Z|`O+SiU@DL8nb@}ez=d(3n2U7!-%Qi=A^JnYQY4}+}sgm=HW@-U=<=;6c? zb8W_hD_b*f=1y*L{wKed+hDky$NRS%Wv*oe)IG12RJlgc!7HG;2dR+X?vG!`Vu+E zc|f2(PWKHsfH|zWOP(LiSyav6PxdspGl!lH5?vYhLIa(Xg8o*rpNtT|c;{X`MYrki z>(fb=LjnThP)my|0+hS)jlwfYLF+X8o@FIDJ>Ko2pKLIYrLdEsoq*NE4@9qDf1jh} z%=z=rF@PEDtGo84*M-bc>;M7>NW*OLYMI-;s(oA8{rOqkk76sii9ZVH(KNkujS*cM z#+Xk6VyT7w8=ORrL+|NzC;!);Ik)3cIyM!yHV^U}k^{XBKrY0k)fm9fzW%nxRRDh^ zzf>U75BxbJicM4t>3lky}Kh7W~&Qb2MTf6XN&zo^pY+cF| z0ux*u8+hp`Uood{;8KeTebFWWw(a3)#$zI(xMqe*Pr6jLWoSR)SW`Z+d7+D`ALE|D zH4>}p?Jc^bJ!+Z?d?7H|?%3urR0uglyFHIRiVc>4jkjBd6Y+iPKBLZz>^CDjESff~ z`sTW{A|~1ysQT*4-d{ak%^i(0c`p@ELREY#mNBvn`t9JSj@A2LAAU*~Da9rZpn6>+>CeDDNh-8+ z#YLkKW>qgWG=zyf!n(FoJLf_6M%ra(Z;!FigHW{|B+tD=;#$_|x+dpl}4 z94LTC0{SX#9`+aMIHnxcho76dtsu{${X!HH*ki#ih;&;)P3%vnt#O$NKM{}$l8`!a z`@*+4HhsduLl__u1{lrt{iI&>l^dP@3ShZ2SWnfm!CC;js`U57{P%J1e0n1ZidN#=4J%<(n_neV&epjVhsMFbq@jt z^hVYXt5xRC?Pavo++iys@d5~#jF{vyQ+*`S+vB7Zo(g1?eY|_@eJApCJ)8IFG!K(Q zRu=L?9Ue~qc3+IZ>@jhJ+^N@dF&XaBtCuwE5D4B${}YX@B80NQM)d9^bi0(ju_{&^ z$msOtPp22x2p0u_EFvKLmc4TP6_`~7_+z|@oXW{kUl*ih`}J^Yj1 zZ1&|g{Z>ESn=28}%SpCIg7b4HQto=+H!<6-vTMQ?sa4Wrdv{_%Va+{7t!g$Wo46Fn zWmGg`u$^I)%>Z$Bjal*E6ch@IRyHly>=~B3Ws4|08G;##=OLjMMyqPdw| zqLs$%i%Y8mUhmiCzy(boByPYJqB=-bYq&l%lmyu*$iE3+VsyvAcKxRzq+)E9stg6~oR3?-fYaI)0 zd*RwlD<;A`MG7mCB>=qpX^oN*kn4*;k+c*#z=NS9#6)5i`Ykj82a%3&?^F_kJR^4y zHuu}u)5E&0_@PWuz_h+vry#k&SM3;V_l%lgt#itrsB=I`NB2-VV!JfPQa{dDcNkjE z9vV|j5%D3)V=mh^02=7{FL!i37zDQLpMUmo`0!O?RQ;%#O}WB{N`w)T18VqcFtobW zU)p3yv1tY8uNMycR$jh0w_+YJ%hS~bnulI)cmjYcdEZ|>yR-4|Q%u4Zg(!;;ft}vK z6^`JqsF(#OGp^B2g#fs2&<(r+qT}g}xinR4CBk7bD;bIo`3wZvMI>hbWCoBSgE0=p zlP_!Kl+D|=b%)cIU?g!{(RDmx6db9)GM`g=kj~Hq!9h1-kU$e105Z7E;zU5uxJ6)BY>?TCFB(tD4Y$#G6)pW9r{lpv0GT@hz`U@zVeBed^QSb^%SV{jqPa>59U z1}angmuo}7l!PEP8YK?v9;2itVJPy^!w-xTH(i@&i7&~JnpoaQ`=lj|qZ6mLnsFs8 zviMWcR7ILE{Peo)*ny7q#fET2(|I>QA($H?X4#1{6taR{E)FWy;(8i%1QY>ZJs2eh z!I_M3J|;J!))b;Og0pY{BM^~LZ2$b?#0`~tuhUq)!RvjZlV#axKZJ4+apk}<-As>Fv1PDNVH z`8!|7i`6`OFXanIyjoUgMG4T}Qu@%{)ibQAg)rFgs9;8;mh;ae!D$h>72tOBC<(Iz zk`SEOv|LQV#Y9{K`5}A-5^95$DJAud84OY87$}G>`}S=qb{oErj$+$GQ&g-~hIqkF zfq{WeWy5`SF;By6vMWo2ABXX=>0HI88Z$NZ1Q!Vo3N{dv&TbRo6A#C^)-Ns-!35<( zQqEbHH#y^C->9_;_g8n>H%p3o4%V3_1$yQWrN|UQ^2?q)VfvH)zU}^`e|1iU4EGi5 zqU?v5RL=rir=LaR&iP=QS@87=lq@u)XNO|JKULahB-)H9j_4y#`Jrot?;tN}spVk+9xOVED4`$OCGbsQa zc%s$|D5}f>W}9L8uPBTg>Z^+ESSU53UZKSoBHrmzECY_Y7(|QDANZBfs zoF!&UknP=3-8*zAlRB|LhJOfDq|#6T2~;NW&cyihT22#e<7Nf!V4sUtb#cn#z;J-| zCtwM^cYTVjQFg>)I~HKh}`yuY%Un8m^*W2`ACXbJkh%lTE#EbMa~|_PEVlv}k$6ZgOZ@mEYfjkug|nWrhV|NbSC{M~`3Vz*Y8**~ z;KaeK!NE)PquBdm>UW;4M_KpIEl+wJ661fUC7VTa<5k_t-x@fVXoCsY}p(!J`)5UuWrW#}yx zqn^vCFjQ5yb6W1b>i$H+ zsR_hnX@YN(7^C@5PH(zQJGA#4QoeKf&E6nbq$kymTD*b0T4> zlJ%_18P%3G)EE2|D&nYCNT!$%t))@3LlxfHM9o&|$#jP5=SdoYY3riGW?jTB$?4*w zXHycR8K%Lc@cT3q@+ ze|u;`r^r9^0Qrj%m;ayrMMjzG3>9)c&^vSitP5!PgShTLiduaK9g9SAb@?HJyg>hq z@6Ih9>z8tZ<_n5^z^}R2j~zNBsC-`LqTz2n-ZE?lba3_v;}+*5WJR6{60$vssHr(S z>f^u^GdAx{FZEnGYA%I6VL7Iv`=MDE1z;m#L}B9QNy^Z*7_wM{J({TEReb)0r* zj`B<{l5~(<59Zz@Y2I;TRkRnivCXA+F=l+gk6kh8Bmyf(WyU%of=Zp%dR}L~LSPiA z>FPTrEdweIy;QzW{p|1%cR0tou6+g#I<|MOOl2WKk-MVS{S=FN#o(TYiue&euf|bf z>y>lGhx@2kpJKFHg9wS2ckI}Fw3(p9bozuB-P`*9 zb^=2yhqjlN9zh{a#+^@M zW@aW)R5f-ltU5N?g>8tXlPAm=!Vl4q$GP64ZN>1VCgyXg@#r|0MC7EpzzS&hpZf3S0xceq_~}}0=a&8*F~txwY)suB z(a6+xOvRsD{I_6isJ+HM=t|7x((=6Gn2kwK#r|^+B&ovmy}pW}$aDl>j*FXK{+%vS z?r5*>RS(K_Ua#4>zZAv_2uipOeMBJ-NVz6^3z~+dMn*54Uy4MDUp{b-jU$*g*!8?k z7fTnox@z+VA}($1-&SD$R7+?ond(P^S1Nk|r57@1iG;*vHopu3$pB7ZM2VPR z$sG8;5`=tqbzOIHVoVtkwbOJU|+}FAw4-tn`yo#(+x8V9ee6! zbiZl-i}s_5p;jZwl}M=bqa~XVWP=Ec!Uy^RImO!O=cBuKVCYWW`_UX(2_Js>s)`xX zTqDG#nAbw_&K3g(O&c2zV9n;kCV&H+jtzkq0;Bcr^ei?226~x*z~kZuP@lv%|5W0- zsZUCMFVZ%|#{sWHK#qbdkrD_dcr~ho1WvP^{nh6G7e;d5y~g4o196cd>^yq!{(X`w z=Em202-Ph(elZJogqm8ZM25f+ znoKZjL?*lfq1|M0v)N+n?vh1|TkPL|4o4B9K2(`VMiRSzPm^r5$rx^LGudaH?$XkG` z#22Uoq43CK5L=-M=k{NusFa|5@iB`mu)9n*`i3&`dOQ8CIOUHeD_R^&) z2K)p*qJ8z@bE$HRm#4?Bq3H31!@%N&8ApAgDE2QJJ{xHVLKQitiEui>t5L#&Wsu%( zPjI08g-so&p|M~5+Sh+YZ3|gT-6oI_5Ca+*qy=`vcvlbWwmeQYRz2aT`5&-f2~ow| z@=bkpaRH^lTJUs=NA9d57ok;S7V$=KdlF$;4sjudScb;u#JlNin9x`7^dO{c65+Ut ziQ4mDzkUUe(O!a?82vQ;!kj8COK7j9g@iJCX!j0gtZ$CFXdL|pYB@?MCsAk5y0}!3 z@96np!rW;oh2U{gM{ISKk-I(W3sbsl&6oVFwY=)`%=X?+R7by z6z}PX{2V;%`l}9bVx8k8T)?DZ3GoJD%;lCm|?Q9n2w~SOwoVa9H;3=@k2KS65VqG4+ECd99 zD3u1icIzo_*BYdoH_L(Dc{e#%vyY53GPQhutU> zWBf|b;aFxrck8_)(tF}3?Xz~Vtdnyk9DjQ;3K85s^2&UtzNSx12&#I*G388 zigfS-g`wyI?cz|a2_-?oEVj;%n_cqV)TaH{rPKMLA%}CUOus){{9g^$c?S@=R6YcV z7my93Oh|25?IgUZujNCH~=`jVfr1+}-eQ3Ti2{{rQBwL^-W3%hEPtsCg z@x%5sT8e>QDJO7~rH>k*9_~#@$M#Gp2Ff#`%}OWfTA zb+NL$KZ*U3;4rv*QNX;U=g-Y4ef55yE387{6_oXNn=+%g=bGtDM){i4=oXvKj-_9^ zizN2ijhP8Es^V(nEBxkReYZX_ScO5Q^M@lvo#ZoZ_*fZWR75rboxjQ$8VV5PQVeJ8BV`IQ$^F z^=_$~bos+W(3>8OLcP*yN~cwxMH;GO$C@m=&>Sfgz2q)PrK-PwT>-_y^xdVQZLm$x z=RwNZEktHwO34pj4z85q<*@X`a&zwS`{Ef!%pxNrA*HruDsNNtUnRYmRXm|vmoEDTD63AA-ldXo#g>=2bVP4R z!u|B7>m&K3x3j0lL{~67c1yGyt?}eP>mieXijalrRZfMMioRVy%CGm^d+~Ko`Y3C^ z1EL7(hXW8Y0Akc>j{}?t*#&c=ut#ys&7?%Mye|SWYc}uK&yVE<=O{lrs)K zwGAYl(_dhha12;bTMWB0w5?zYdB0|W6&Jo;%JRyPL$i$TtP(RUSc-dQ6y2ALx0r+u8Q0l35Nb+&RCf)$-xj`UBCMwai(gl zMy8%Cx-9Jej=8bq2(&<}ZTWrTL=V;R=h909{gy%^{NVKOx_9KB>cN5wgm`BD0giBIh!U!T53gg2L1*kE9Q3$Q*C>cM$g3hqns3fhL|np z%2)Dfk!O&2GT1}4VW(OOOq`SYW1=VHPTGe=Htt|W2|sH&2}m}WohaqV-~I9y>8wO* zB@j4PDD_q`t#1H&Uji91}tt`Yqs5X=!OO!%c_^yhm|IRgFA8@qz$JFt`w(T%S)d zAikfZrU{F+CwLD$87{=U1IYu4MWF~uQMIg3P_d*sLxnE%`FaZT+m5qUK~Yn)R|RJp zlEKoW2OD%reKQg=UWrIz(>CI#xZ2J-JB3Mio=~y&tqNYxkZy!b&*ndj&BO_U#~n4B z(=-3uOs^BgduYFSvtBLaeK!5LIeODLgKGm+;zv#JKO#R*QR(y|m7ZzxgXdoQFUYdf zKJ$cY8Hjvv-00E>wv_}@fFO5WL{8}Xq^l$z2^CM*T5_cSc1oDv4Z$|AWHh&HI zF%_~H-U0-0Z1svNh`IfDycs?;Uo;2{WrAob2VJG(vb*n>M>bU9ZcR0ElvtGB|BGo) zI{Q}}NU)}SiK11vu3fRu7_6pNb#+q3uoN_3&$nJv>K$-KmsuBlAj@u<(MIZAHZZRl z-4GEU^|{BeVQ-v8?EJoK`cw1m*~5Qp1a_fT#wigcWIykceUZmFP%-Gs`ZxB<6`9p{ zG;Z|`sjIC$?&qgjRVN6VDzX*Rx8<7W0LJ+_Q3Fr#M>%oLNx!zRH!q3mU|&-4%vGxt zs2QR@Jw5p6bwA8JRol?#=H0u%B$}qq*S6R0>eIWo7(6kTL!`Z&Tur)L%c-(n4_CuZ zpiUZEe%wT&cV;IIF~U6VF$QmI9dAqm#TE)9*2IhCWHA@{{^yV33pViof)U{q0z0mz z(;8Qj>K%$(=i-5Ij8Pa}D3rx1K&yN7l`DJx0CebEZ{^c`Onc*Hq70|I*U=dLRW@WS z#AA-Sc_tv+ywTKB_FI4`4~VFVPpsNWWXb{H;0@hD=$0t;5jLN0Hk$BG&Vp)Kd%EqGYO-+%1 z4H)nYvJimNZ-5nZjyX8Y@jh|>*fY-!c@Y6*F8zAsm7-(Ux>%f!8$8WW8D}2mgkH&A?6S_dLncUrVnfO$Or`foe4#4?e1Azye#u2AB8lQ6-wL%GLQ zOSHhiMI@4hlBtU?3YnA*JQD3#qio>WSpvc%mIWutOQqmx|FLr&F=ME|v<~^3V2Lqw zJH)KN1A(vT(nK7Xw@qK7w24`)orUo~7-r(>k+hQJx>5hjNCadnyk{nj^kGmiT~-+k zf^)f+RaS#fdJ*n-#ok++l;KqvDDMtra(242LVZ;s$l6mvWFOxKH5^IjipGKS)4gcB zAt`TXmK3#el(;SBpKH6?tatBn=tq-7mvcCGm&I{a-CW$`?m6LG*0f!Q};N1N_TBbfF}nO)5EO3qB=A28au%Zw+9DsVM6JRwlbP zO|I{mfOiloiP-V^wDVUB?abQUHL_W6iW;2cqP8OIvwbMrPD-4=h?T*M0GEpI1meky z5im_k?L%g#^ywf|P5i!q+k~?dNUev+9V#41Slg@*Lo#P_OoQ(vSLh)Op;|?D~cu^j!-N5{M|_ae^XfvH0EIJ@} zA4mgxc)C@)n`h6So$D6gADkCM8Zo??Y7=u3WCDse+7+o^md(!oB;Xe+eyU~q-7YM@ zDGchQW@J|g+v2;=wf?oTgr_GX5ZF^}fQ5TSJVO8us{QSeJNcwpPJvr%vr}DHxTN9| zfCuj-+6@~D^lom0DHz!%tll zJvUfT+CDIQTT{9a1jxcdbmhcu|7vevQiy^iI=I=ruCem5g5V82DYmPEJ~Kn@=$;yc5{dZ*4$LeGqhR|2b_)NS;S*F#Q(n?ii1yH98y$i^0=b?o40$K8&T-S zR0RgOKcDxr{<$~8%8<)GDOq`)|J_M}BN;iesVdeq^3|^9yiGANR{76985kx1*Kd$q zWxn*rjqX=eA}dE_mZ@f@?5tOPouye{b)<8o+WPftmpE)p5Xv<~)_ecM%TsbPomg0xoG4Kg=E zHn|072J2)yOwEzsTe9&&l%H>XgOcOm$EK=_7KI*IBiR1O)6cJs|0~nsViz8}8B)=I|Q+1a&hD5kb^H} zn(E^ppZfd6^&W@Z>kHQ3m$E%vU|RIBD%&l}HsV0-();(RM9PPBHtS}ls2X}8?85u^ zF;4VMd`oT#uPE%+U%Sb2^{^wyq+9FVtdvY2JoR|+<;_?3L9S+X*xgKjH$V6M^v8`= z(i4onmo^W7{nfoHev_J3fr^vU$d=80l}{INbSodg=y~zORDh9z3|T=6#VyLqwZai#2WC9}nP?d|U*=fVzDoOGlj?!@^LA7>WIvd8uE}{Ik{BIL z%g=L8re~9bWAMndR~z*cQ&J2x;y-WrS(w^+>rX6%0s>RM zSn0%;%kB2_x^pb$*-^s2j%}C8Lq4ZnS{mwnA!)Y$3nV(J7jvbIB9C@`uTt<)qG8=m zyV*x}!^QB3Jt6TMZR_RQyW5uA&R~`y`MzhRK@kLA1^! z(6kn1-IHxLmA%bN4M_fntbgTIyOw*(J@oRVL&wxUX)RngWcdC}iB_x99(R3%RnG&I zwRb%|>0}Zuq2{sdWcu#$j9;#f{{B{dFRJ%QOS5mb%nI3rFIi1g&4>FRx1Kn#Kl|Pu z$+SnN^G+rI-gV*Po&oEXG{d#e-n^NyFmjlmw5;son=5Yr@G$L$Q|*IJz5B%dYD(N< zZPB=K!`|ulY;raH3}0e+m-uh?R@Uw;JH_^2vF> z^RJS7e;9T7>M*>)o5Urv7`V>Y$m*J=PpZO^r!^ z{@l=C*~BY3*-+8vLec#Kvkp2g4jO9|_qba(&Oc`8efQafEQ1_#blIDd{@txLdc~|K z$yxSO>c6$PI=IMW*W7oR)`+iE-uEqAZZBH?^-X2wgua2$zYp%(sNNj;_!^D;t|@1~ zJ8j3BU>)5eY`5yQWy8en#Iis@oxMs$hURH7u{iE(lc2)kUvQuSB;6-yO5RYbZHyUb4{0_0+2Cr>1Y_zFckU^I{Ji$kErsM~?&K?=;dnd(HuDnWp zt6^x|8;Q!v+hZc{YADGcwkh1$t>^NQqg%bcyG5>Un7v)f3aj66YyT7D>tCCX3pLwh zdALH&&ujW;!!hNCSkeaHT%vE0G;i=@ZMUCCe5XzEkK53py+HfWnH(-^q`?#2xANE5FHg`Kwy!YVWmiD6;-K{8`zMRi zEG?4iJmtnjFIaT8QPna3FQxTT3w1lWWk0)6X%jhp#d1TZr{)?#_qQ75T)UR77rZU2 zILGLxixn;w)*2D3Ce~Mm{pjK{L@{XabN^6au4PLLNoqb)ldfd{LBeEY{y%M^^ zX6yRKA=<8$!$yqTd0RGX+OT~C>JqlPRJSPhj(z^6Bz*l)b>9I=($er|HqO(W8`qko+3ec%D)hj;qQsJiRqdSXU+LRU3jZ23 zeZ5=Ilti1&DJKGw0}LB?m+yD`*zV!Tq$KIT7l#JM&IrACahXFEi>)+0J#*f&j;%_Q0l zd*lAC-nFODZIjfz!IIdzuFl!N8%kpiL@8c<8)mU)%UWU84%zdCG^h zydKgr6#*`HT6rhjanvjlHU{G;@h16{&`7hU07XfYF~xDhv(R<$sU<^x{FcB zrqb*~y_$+6ADzEHDcb4O054zPl+oQD-&NAI4Ik`Ow(9hSaW5)1HeQJjvRKvfxtCeu z{^~k>Ny}bmzlh9u=4EC4>CPIXz7HSf{ElnYcAlyD?n829Vw|&m?vDg%2YvD4e!pH< z>yfljPkvhItF!B}-i^RHj)U2|L;_6^Y4lpbz<=JfN^QzkrlRs8mPLsCt1 zT{TsOx(Dp3ja1sqFjTYAqZFbTXY- zRbX*vp4Pxf{n-az_8+)0_e$9I%Dl?2>q_^gJ-3_DUq@RrE-CTuOBa_h!7X3kS~Vo7 z&Q2&hmG(SxyT!0Zw?(O5`D&+~LsV5aW>wXd8X39$+LNzT=zOfX(q zgVTdHWi?$b2xg*OLR~}MiI5-O-uCt1Odggbw@ZyTVDVLsWU74R&iQiHA#PGsqu#6R znx{G|Om;heCpU1?VX}rMfAl1g=}804H*U;MJhwW{?f9rs=9>S+2f3*B4BL{n@u!P% zf4AQ=-1UpMr4FlIeYZ%-#XaBMQ{8<0__`Fug{%8HcQ=ebpMQMcEQbhWkN|?44e%F)7qN`qtsG&R3fq zEhf0?&mR1^Wx%2mg%AJ!bJ47E#PI#r>jS-~kGXaw)Mc>C-Av-Y;k{nczWZIVW~q6UsV)-~b1{6_X3vta5jpRsGNua`QsDrMm2wLVFS zM`^OwSmM2Zt(Rp~dS>_J4Rz^#Z~kl3I;DR~x4qI*3NPfgZuGqGe39VQu&~V)z@ARn&O;GA9S8` zYoAH{`HBeqL!J!1m*?7=_rvhwnRpY=J$`A=TjXW3A8=yW@AJ@!OHaC+7qhY!bWox8eUqj$y&SHF`_-jDvS+3A^ajKKpFsa3CDe<<4A79d^!3+rL^+$JJT zeNETMMIFO)OYd%HV#&~e<8d?J#Ud0%fpYOS7<6p6Z49D1KJuGrK_}nYzOF^(_ zlIP0gyJf0b#`2xq-ST%%CK!6E?eN{Tcxn22YJ-zknx0ZkXlsGOe zwK^-o#;qpoti&O6!2!4 zN@&S0O`Fi+htsERSKb{T{Ck(e%J#B_f5k6cz1nbG@6Yw|zoy>EyzOMOLw)N}!+)N& zV@}Unt4Denx$?#jp1vQVc1(SIi(7eq{Z1u|7PacA4Qs+x>I>GKEs4JN;igX!u9uORIOuCd%u7{dD%s2$R7}&iiy~-Xkp^ zueHj+uW94f;18K=s=|I7+`-;%sajVZZHd+MTTOawuCT+N~@+IGoH#T@Y3OVRux?j>~?T8Ji6lR;jc9d+;iU#&sA0P zTj=PKA2+)6W5&=1?_}LA%Yv+TR&4uJ{G}=K{>wz)wXXhJxsQ~`FI%0>K$>$dOS)cO zR=aJ>mjBi%B!AxGr@Am^x#`{XBUO{!#*eSjk`k4)p{d89ad&8;0b6O*% zOzpxKeVZ7h>ya|-)FAaqH}vlK`sr!bE=}xmI-=aF!TzCz-Px*mKRM-^nx4C5bdGGB zI;=`lT~^h<*dgT#E?&Et+&3=U;bG?0i>hl+TiX_lndno7DdF(?DK4w`%QrPel&`p9 zw&rX8INz`PSMNO)q~?}jlYcV#+mEu^BJ;FHOz3lyH^uutOtoYWk>n-n}N*$*1 zV$PbcUw>DoSL9B(ICOAvf$yQ$t}RnSOM7RSJN{a_%Sd8ZxIR>W&b?p%o_TP{b+pgj z-|Ff;AMY=_r8B6$^5yN|J9plU!j{_J)?!ycMZwr^2Nn(Q7!cp8{as~BP0i!rkmRQh z7jK((>8~|;ig)e@_jK)3GyX63-a06&J^tfFR1g)tCL#(-NQqogP>@hTln|6|PzmXj zEL`qs3^hzV$DJd=8uamlwcLPJ67j@ z=%ylc=dqO~u9>#Mfi$${1u4ajGHZVX!Lj3F3Ma| zkh{~XRzdOY^=oahm}@q%)l;kUmsCGa|2+NVl+D+(M`g|Pnfex&CoH{dZaw;N{`T%W zd^LI;b^^AA{wY*nBH$Rd_-ET^6u^u zrhbLj&x;g~9BODX>>%qpbg0pPeD=<;snun(j?+1+Y#^NHX_s|mGyzVUj@Hp7Q^lV`d<7>}2u5l~zD_xvLB)xXG%VuAMC9%`r z^l@@jTjKtjn32vXtsW_7d*rZoa&LdimZi?$6*FPAjZeCV{jKI!n+pak!~{>!1k}~? z^uO{7ylkDzSjTrN-8=2#>gnsXJdY=R}5bo@*Ub`vFB-X~|EkN_IU)2O{1Xr(_l%VKZfssCzE1IIJSKq^jIn zZxCcfSrl)}KSxDPZND9KSs~cPk}vK&uQdu)-IkX7vg7jIGyDQ~ZTvdO#k1JRl)&iY^Eu;eNRZg>{`lxvdGn7v z#{R*>bS{Ba0|CtA4zOKt8n#w^RhoMHW0fmBj>b~PKjK5QOK@u`m1e{#$@uu4rbP)U zE+^^eR-By*<))+g{Va2BQj69N(kh&pn5$=+Tg1$ER;=E=8?x4|-Ewp&Z|xU3FYR+K<@QT;6>22s0)_^^UKI=9M@1SkE)#5d zo-<^I<)Bbdf%&%c?*hL7hB1|UXLMS_O~t9rMi2aHQG9xkeQk|#YI!Ea;=zS%Bfm%6 zv$sRpZ;YkA2_Y}o%ntJvS`~4=?R%Ch$276L?PVh&f6@5un<= z5T(V=Ak5a{?k)p09%WD7hgjjORoePAJa_7zQ_?K-DJII#Wy**t&ZW(1=)~<3MH~_m z$e6mkxLZN$7zIUoG9^uO>${djCXUPbB*$e5`)|rJ|Nf>+f?QKrku+9%FKNcD~zCAje za3pAWSS(cT-n(p{7kp$oFWoGhv~|S=v%7>-|9TU3K|eN-Ng*-p3LoFi?HB&OLF5+_ z6(!85KL<(YmQ`1c^co9t+~x4i%J9|Gt9D*ktgL}ucYdPBKk~Hw!>`YFRbJ~X@QdW& zh%7a9VUMn>kyE55VKhE}ocBe4+!9G8&FZTE6*mWa7M;SLl#k{rokI?bM*1@?ov&z* zUCKpG$6)62@YgFwZzHH;KExL(dh8uZR;Vm5)hS%Mp=mJQJ>Bx-hk;#6ZOHE32DE#& z)ZK5Sw8YSNc%*aX9!EenOT9GHH?uxlPB$A;ys^8(o`?DHA&w)0(V2MoT}(n=UV;l-&qOQ=V5{S-iWy=HJr20LyVayyh}oI zfAM8_lp1boAvBZr{(3d^M$}8^{Km#1U!0bNG;a@xP4IO`4=?lIAFaypA^!dP-+nVY z=G>ipyCc8Hc4Qmx#NFV1mV8e=sdSv~-qN6j{9|Bx(9xFPVt-_0lM4?kMSI?{FC8P> z`E4mXgoXW68XD&NdMz&ToEz(G`cM=c&OVmoFJMPsF0X<*I>%z~`GVWN+Q~-A$*RL@ z7vg?B;613Xe_gqwak^R(PtJ4~C2w8hQ)%SleloVi+Fs%sYl!!B-jtcC?e_AjxELEt zGCV9%c%@ifFgn5`n`bdA^K#|9ZiZlSLkDZ|eT@T0k4rgkFB#`fALR*emi7-=Ty?^U zi&7BzGG!t5m(%d;?s{Np4pT$l0@?27PI&2jy3dwBbbDVg)OgHw)a8VP3+ z%Z)KVhBXt1gpIXCen#`$V0-D~b)8ujn=Xs;TKgJg^6ZaYwU#7J42n&cVOzyLKJA@3 zYHDB49Q<2BCC@;;_`t?O)1>dOA+a&zU3YTj)swf2Hd{~ZFUhF;ysVI;+Bda5w+~M@ zHFx!7iqSH04SZg-NO@8s4c(ZMSH-6u{5iLbRJHo9e)f8eLM za>8}Rc2)LmEh?2pWc2iC1jo|zT)eowX_m`aub@H^A};zOb4{&~?Wv7iVC*Z6iC8u} zh6_^BQyk=-y(Gm@G-eizw@k?%WMF6%FKBExI&5_N7RO@LGajy(rJKqLr+i8t+`Yuj zpG5XZwrBpawcAXxeR@|sr|+YOLt&n3hq5lv(>Lz(+`KhncE@v$v-pk<7c)6-q1Eki zm6dU&(s*Oiz`=3YQHb*OYsQx+Phu(|6e)G>C@>3R;0TdmlMswvIP=msge3CiX!9pp4Y_q)NVF5*Ufc3Azi~R9%{p-%UR7T$t5$+%pCPk zp3GhO`;7Vom5RhZ+-%yo`^!5L+eLY&b5RWRVacExI+DloGZHyJk$@ByUUw?ob-l1~ zyuJC?gZ>)oF@JdgHi_N z<+)z4UvtpyE4I9ycQiO?w?YXm{~L-hlCjrO;q2ufBn!wYOpV&lZ1=S=t^b;@RI>HG ztwDF~2W*>~D(cz% zta!8V_lf4Z-rNs9-s<@Zb1g=ak`;Cn)4?w6WBcxyk}w&&=S-PZT-;Cjyw^k_SnbGs zL-&5%G3tyoE3=~Fp60|%$RldHgPV~t^r1NX0&*Dxqbylsa>G9{Cqc~rl#^pk^a2-m$xT! z6|w>pvoj?Hii(mXotbUBIyWC1$TVer`AezLUbpgtz}CsZS_$LYn%$L5)`cPY*Z0SPbtx)LV>}nn9t)$nqP?BmT4IhU&cdfgH$hu9 zS>|b;*^MRrhc~lr)-ofmr7C7GPiDwaXR7SPIlsr&ClxQp1uH;_vEk14RkQrz9pA^A zWO!e{^0Q2?(GcQeJ2r%}j1xuBm28tBr>qV}I;YL7f?~V*p))%?k-?GviZD< z2B&H3;iF-vcv-i2)8e2R^7zE>XTP&GHl7>x4*g@RO@s0k?}IF|135^x`@Fn^3@ab{ z)%E2v*wq>Y8Q%G2ow$1S;{9HiKi369Iz6m7IL7BJy0UA9+MO5fq{u(_@2wKf>tBdE zn4>!PSS~P-@_@%KU0z=QVoEZ*sczoYh}z>P_VY%{%t}iJa&;If&@DJFM>+~#HNARj zPQ@sxps)T+DfwS3t`on6x!duau)~++A@AytxTK7ZbEA|xE;YKmMKo8MJ<`qv6gUq& z8erW;VOIgx4*#-gIylD4Uu^WtE(%cgwQ!{HmvG^V@A46C?fB#VwaYFgxK z7oR^~Nyif&NFBmiz3eEM8;x3-eo}1Zd|mbC<*Rp(%Q!e_Z7Iz4bY}S%4-PaNroF$B zlG|`URK#l2zJF3#tjTQIKnDy4v5uq#g+})FB0E_prFR^@Gd;3&U)zm-xlc9RrZi`NS1+T;4&m9a?RChM3C!m}@cm+K&s(HL zpV@Ms3feOsd($ijP>%9}k4chj!<|*4mKkeDY2Ji4ZEdC}mx<~#(a{FGL<_J>FfT4w zd!?e@AX$@zooLw9G(;;d`L5gNTpv0?7Y)*uUJc%;5Y4E9K9$p_$Bi;E^rlr+;|}BQ zcC9awZ&|tw3+DD^zA*OX zfxBD*Jw4+Y0Sl4F=Es3fE+f?3kx2?-ZPO+RNhul}qbxiMY?bQj7W?Z26H{JwH_|7M zM@Gts?a?T!eH_?I86cLOEIFW{#$TM)%)udZgEaPVQ#;F8SR_YK=eL{O#?sNj9w#<8 z{i9L~VrVVR(;_}BS$F7)zTn?;KRYsrDY{AU#HUn6Rb6p$-M-h_f8PlU46e#vUvDWf zsiPfpD>nbIi%qH zZ}`f?hq1@f;%~G0|M+oOl4Zw_pOJH?p&(kd5ahcy%*k$Okr>s+-u8m)X>*zP(?W3N zV;}akvji=+Y3$54Rdm_;2G2U|uc-1TXTzk~>M7Qwea0HCnBq5W!rS}pChtKern#c@ zp)+UNmJCxXClVFwTv%@2blY{TswhdFeZ8mQ?C=BowT(P#UmtOm0|%;}Bt;ZA%JBBK zDu|Ug%jIemZOAxZ;g+v@ZQRxDL_-&{+}X*fbywW~%=+3B3KQq$YyG*S&2{BJ656uA zpB+x)QIC{bJZ0SVQN!idElblM6O)0++>3_ThXrFM0~$`eg|dM;M3~II=C30C05#5o zbmA(M`zx|S{N6`hN>!|-2?`otq`F|Llsp&i?rz{Y_JZr`!mbR4%=iMY|~}?%0E4J zpZUAPl5!=3xLp#Zn}^467!9q0COc<{p|nG)lyr#Y=IW?LUq#KXtp#RYJ#l+)A94H4 z%nH-K^3_*?a7iWPo~27pK@|4y1x#6?Gx;JrhA5&NsPpp zD3z(OA;9?(=;%R~boUe457OR4<-Cp3uBo=-vsskSb}@R5@!43(_Vry?!<7f+w`uTuE*+NXh~kCQXx(N_mWx0ZG#-ssK?m6qIG z^g$PNl3Q95chWga23=WO8!A9>t>bV}*Ye5`c;-@|cC{5o5HXPxSKe>y(C*u%UbI<*l} zxf&iXVq?Oi>AqT9$p-AIFg6|!TReSQ=7PvAu{ec%X@x|i>L~@f>v6H44xY>G(K>Q? zq;FH5ot4$B)-GSY@l*2MM$xV?9?e6C##b$KV>jee{C+)P-4`Af%U@9wn0|BJXlcpV zE;a5}3bur$jQHDC5&Z@4Yr4Bz;(}=s62^1$oCW?qy>Le{j=8DCH88Yv+3&V5qiJT2 zgrI!Y`vt2kuWGyuH=DT675bYT?EJJ@afkMtvc42h@F1g7*drmzE?>=n-A?@VE~QSb zf<%d>tPI&Dsy7zGV|)90ICO59q-;pZ1#T=ZROG7G#O-p|BVV>>N@ZD#qxx}a`$AkASxV3roMZ$kdsOL7!3%|=ZXC0olnVp@qW}morsi= zqyo=n#?qynK5-pMUoDz9ncW4-TOY^ysM$;(m&sJ)Kik*FLr`Gb6l^dsASikl>U7xTT4;K`Xs2rpyaFLY!t(d~YU1{Iy zy5MCye|L3z_-J6KQF`9XIW0#vjhUedO=?n65s~UH+0#aKWpNVYSJ|(#?GV4xlPi8V zNNc-IGj4cvA~kNp=*i+_PL^f-WPo+|)I-awv4)?rX-03REG9{MHMgEizPG;goNk+k zN-)|Ba-BeP*I#AD8o7tBJ_zVaww~YM@bnDOT0>m0zO`Pdevguj*Ytf^M-*<;IcghXoBD z4%7YF+d?{rnK|OFz5BtHqXR5*=res=CDP0?(NOPhqu_o9+oUwfcsQn; zdisLs)0mi8{$`DBd`z3g&=dn$;5~hFvbRl}&gnl@{ba1COX{*7o_D^r)IC6tDv~vh zJ+$=8*Pbe4p3s$Bb><1Bj)G%r>rK*!AKw=J9AGk*@Z~R}ux)8EcKFX&JPP*D4`g34 z2^@@iovrwfY3&QjfRMx;%{s=;tG8arJuVL>Z7iJc(4#UbmXXn>=bU;wrDgBq&?@0l zT;sJh^lAvt^8}MCTVMO*vL(Hu)BW-|+KmEU)7^gWX50^sP{A?JWj^RmK6hsQIaTZi zZWuZ!q=xozh-bv9{z#jvgI5nVh{p6?0br0lAe!->O#c{lPRx{ z(w_M?Pq)`y;;ua~Pm`N;bH5Cq6f((SlONeehI*=ZC7Oz9f~uJ=wfXpnPZ&J-wUL*l za-J(7o9k8cV7bBakegd&PoOY0wcqjPL`A0h-dxM+^&>u9M>zkw+GTLHlREXt;UT4w z#?EPLi7>VNbIIGgcQ-yM_-wG$73PhyJL%R++Ns<2YYRP|K{`Hx1!<@97<^GMVlkY& zP$oF$;AoW+gu{*>f?=4tgKBr_1NqLSuZhB9hEH1&hq!YEgD;`ccZlX z#k+g&(k_ZA#>+Mqe=(a4{?zEnV|v2d|H?>S1S5xORbl6#AFWYU=e+&iBE<=AbCyGg z#teUub+x5bK9h7!qF(DXTr+Wbd`HDsRV6mv@sHMTu_)cdn44vqpz?9)s~9F4;W_uQ z;;x1rcuKS}Qm*dRw@hrcS>-#POpPR5<`I{;e8#X6vbFBA9&aDg?X4Wiw>mr$AzO`& z9j1Cb=#rO31Tu~eF6dM1ZIqJBx|nKrav9?yRyaDq0;42 z_O$-kmv+(4pg@=Mi_>~d>tYnehx=?woP~^!R8lp`eQ1MPTas0EG*i@Nh59=8IxdA~ z`gcqn{ymnpAuV;_&r;W6a4M>5Y7xnX_GC%8JzKHuUoH-zad0a1%4*7P#?G=e*~^h! zf9OzpA-iI;j(T1`9_tFU+JEeNUlGS>_6K3NtjKFjTFsBrxv#V3GE0lUJ67{pjyV#Y z8KG$DpU;2LU#is1KYn%nD_Y5!_S|0{d1SIMo~wGBCmYY`>%3vEwQu{o$J!~+hoe#y z3%D`k*Pi2^yNZK?(r3)g=d_sF#3Ly+^9#Jvqs;F6MdasZJ}WO1-z{AUTPMrt{rBhP za~yK`Gto5V*UA#-LzVGq-a{+76nFo_?>_3zDNU4#R;qSVd*X!$doyllM~6}r%)Xes zE#bLqrqkSKYD#8IzO{!t{Ar=X@J8e{=8Pu8c{IfX@gVz7)oe)(%6I89jVnW9VuM@6 zgMA8>ykXVJ!1;>Re@OqQid}{l zm#{QH`-+%N&d`xK{-~J<+U4mTdOs_isi>+~tLmcrc!F|;E~t}U&?NIY9k6|J|M=Pz zvvk#rk5@%n4bMIG4AE(0{M|j(S17m*$;$8#KE9zH!#=0edlwyX)Wfh@_!q}~(CJJ3 z9Lw+X%5S3o+iq4_-I#YIEA!Dh;l{>?3k;d>7@8=|w7egwIfG^I0jjO$<}zn3`I!DX zNf**KH$N;I9L9Eq$3xh}LR`W>JBEge`HEbsqRMjy`k^uto@z>4TO~GO6+1hN3GV zHC@OIx{i599bEI75fstVq`bzx6_xk#*WrZ1z#2yyWjNJ9!DTc#B*_@jya2+@B^O2 zHnsbHYxa^}Y7>Kl=?BGUKKy>1%Ofirx3M?M)zDEbWopu>V>;XTeUlDPpu>x{#NYty z{J?zNvJo{oAv@z+m6fdC!@wa@qEdCl-1l~})oqcA2{L!Nhk;d!Ecfp} zomd#JoKi5Y@$;LzR$Bg-)SruA)#fzaZ?H+0>OcIuVoCVPEbanv%Ix9^B~=a1T0i-^ zBgy2Nt;5~`)&<+PeyjTY{2nX2?-TMnTUUoD<4Ebo+l$x4Rit+4t9RwwY?a6LnBFow zi908DsIu6{E*s5>e%)-{8zP^4QjsH+mgi zn46!xKW5PNE5-QC7ybh_`%87hQ|trFGir&DkehPN zL*W~0!ApOiWVSKg{ZabJ^A3I|Cbs6HpL!0KJxgkmAqNf}dOu%cH2*@Tp^4VvwucqF zkxB6J%Be0r9zmCL-==bc8@~Rj{!z2EQL9}nOkqRm!6mPJvCo)%pUq~i;#Vu~lZ(o} zwjYPxTOVq4?F&l5fvcd8ZKG!|nUTAt8$iC8H}X+p@cH@hc)s3_?!@nLvubn8j*^Ng zcY}gurzVV2c`-UP6y(0#%~YeL-0d@=fV(UnO%Dvlt^ZC@XE!~r6Vn&-ih?@2w~t4{~+2PM85sfl*y)+n2wSb*(Rt4!CTldhbh@{qJ*% zuy?p@w#hp#+!y}$2WDr_C8-=EC;4g-N8aZ+ue2PA`=*F571jf%SV}(LO(0>^jS_Fy zQk_^^6x?WYQIFh?`tLj(O1`&yciNVg17E}$@Be+D{C`H!{ISOWl?(8H-Ch5m-}-;} zlgD|NQ@8f^zc;yt9FzFUbUeDF{nDsu9YebbshRn-CZ^!OhnMa6R{Be=$tfEfEm{F#*Fe05@N{reMaHUn?9w!^d|L%rg=~>;hv)bls?n=FH_N3LruN z$lopo-9;;>>!P5b;FQgWm>2?9@$%)KZ9}jP@sR$$^weKqr-6R6y8w)mi#$9$gT}xN zDxD9GKbI?BEKO#EBtDG7(avG24QNIFgz6W!LInBFIBAG4uez5w;Z&Qe>U= zfO-TMh>RqH@5x?(OT*d(2{H>vwxlGdgq%+SQnFl3g`+0$AJ9hw)>3+<&(R%{DZ9;H z3xall$I=N9DVj3iu$h$z`xtj+6N(~|1+l?Uf`1A>=*)Ib_3$_17r|B4FAfh5et}T8IY&_DsagFnGb%AA7HdazqVkB0GNRgN?<6Q?Gv(@J_R4t#hE^!4ohJ$e+|O= zwjmCy90}O{5#MoFVpeC@8aL@DsD8dfyFY!YF z9M(BFWd6N0+2Ib1lfF<}Umw8_BLi1Z`B`CD`x&y0U#AfIgA*uvJ$^totG(066Eo| zT0o1Dxcd0`DDMt@PY|kc?1VuIG%5@cuc+*uC1Ac2bSDr#z{n&af&7nqY8g<-jUX=o zmk5pC#NyA@6L@K$hrn<40kMtb?fr@PIbNpvpAbWK7)8xHzbOI-@*V(Z6%}{j;wCKH zg@uK{N{c8fQ^WKCls5=@1XcF;@5ewy)8pfV+;r(@`7f~JU6AKgDyJ7fHuoAla4d=M zii_!h=*!FbD_OIc7T{zSHny9g%_Ti&_$Jqwi#24e7 z5E#377%hjO1!G-+#Rl4B7*fE6A5$>S`$iY|BrdKi_=t;4Fy1N!2q)8miJim1YCc zg)0dVvNLSyXhNNFJQR`qlnYqCJtu?^u>N$LV_TOIi2&VxY$2QC2KF}k$fcj zlwqb{dH@NBI}c84oMCf?zNv``0Dd-Han@bF2pj=-e(Mn-^abm@WW>53#=zZw1TEeoD&sAgtrcV(lfkt$>h2DDeyj0*d9tS~yrf zKMYd~$w-K~{joqJ$EagqhU1P%@m){88!9*v8qTXa1T1H09gs(`G~aE%I(w)=228Aj zAZB4xFdM0%-T?6hcC_DtedbqB5hhwQEHSq_tA8|r`VSWXv0zu!)cga|oi`vlalIU2 z@dN;K_6HT5Uu!ZmdpRz_?6xwVn5tlvGYZ})6kLp2FV7rmc&OSOYz&JM2v`Ic{2(oV zN#2u*H*eoU07B87Z*vfnl;n%{kvovL5FEiRljfr;@K*yaML_~5L&IU*?gwZ5x-Y2t z>LIUQ{vLaSISO?ZA(F+3A=Xpi_;fG(^upx;WIbFo9(AMNOG--UCuhG|UOh$d>%e{n zh!A%V+7qfPumWvndLvszTr(Y6dd5;U3gK9cx)u6YfC3rt*kIg)CrybfQvGnQqyuzc zkj8$1mjhP#NDGZ4h%FnE19BvMy}O1QLsc+TG@N{aKeUPUFq2cn#x= zHhP4&y#R6@S?4mXzt6C-u}vD~g8pxkLxYb2(*ZVCh*ADCW?=_*g%TNo#lI4h@3_v_ zzfc`Iba8lvC8w*9@lpX>d2eg$$U**ZQyxq0$Es%8Q$6^shixR!$(GnErnV(Ygvbw{ z03>b}6zuBase9@b!l&KK2cMP?hR$uU-aIY*sQ}yi6kW9b-zT(q!XKn5eeCAp*biDC zpxgzEt*TpTrC>n)@N%ofLW)uS5VS8iZvpgQC{FQGm!C72lanivY_}gm7`#UWDDxpj z4&&VSJ)5aob@f9d7*tiuTO@v9&< z;Xe>{fxiU}exq%|sA@1b%-HVK=kEGBTmG#FGM&bT#g8tZ#OJ02%@*Uvh3_{j8{1x- zJiH*Wn1F~%p|xZu>`>78Xitb{)~KXi&*RHdC;O7`Do!mPj_7Ubk=9?zwEKWv0gL+9 zvaUt#?hu;N;mFpTk(Z3^y6fxvqXW*$*_|@c{63roSlTMh$(unA=K?CD1v{-8TPcD+ z)7}>GG_BNS&`iLrC79gSj%N7353K;a6fg>2hFOlVQDPzx3kE3lgmsmWrV9w128#?Y zO8J-@9Op2LniV)uLSnn3;od@MQL<8m`%`v%CCd>TCXiS=j_^eSe+EYeyj8cbRRX^V zT(igY^t9_tnB=+1t{hISucct8;=5O7!et@7*RcJy8|2kBO$Uff#>dCc)gB=x3f_OW zgEti%M6+|ZCv0%UYdMjunAHh}No zHDqQ$^_fo&(!cWZ`Uem=SS)vg;ZCTmLO3;}W`(1$EnrMQ^mhRC{;f7oPtP>CNk|B1 z0M5>9n1Bfu>sB2U8^jM`lY)(LNs|FOC9I7k+IG1i8uk>JLb#eZb&pFJAA-h+L#I?t z5I__Kbqpy9Y$XeIpPaEM5>{1mY$+T|$qtJB&neIof;gKuAHnY5{HY zF~EYKQ)%p3-@+XVn@`zD7@3#|x+7w`vqCQGOUBz?&D77-U2+@yLON zL{N4E@J;LnBaZvu1JH@%M#1CBj`Ml$i?#ngY=Ida z`@?HTG+(eXG3|C)Yl`l-$R!+Cd3k)`bMETCjp>3*4D<3v+sgsaez(%TfY`Y#{n!o* zw`)o*j}9Gd5}ZtarmGEx)DWU56U^JBJoSC_bFpmpI0?(SKS zO%Z}=1vwEwlAyq#|ZL0#5%+dfS7ip%h%IW*xdXgFqgkT5N~e2 zoH`v2UU++fo#pjgmjR-j9CZ?;!!iV=!=Ow1U!M(?0bFBaXTPta@)}Xu_T9_4h*;Sm zSs|hAQit#oGjD(8~ZwtCU5u#vh!uc0Ee{pA{zy&_OP9go*jh!(;(a|%} z)V5}3^>qAkA3qYLL9COmZ4@r&hBFW8$gKoWx=ip=}wnBiYy-POH=o%lxPFvwY&l z!9@gsLK9VvA|kuMmrO%;Mo7W&Irh9Qxfg1y4@x_>R-AX>tO+j<*uikAkkWo*)jxLZ z*mDFX(C@&bzB2zbd$AkRu3u&)gR{V7j(3g|sU`vmA{0mdM#NL3y*Bwh$kuV!EjP+2 z{j;{%F*ofVwS1Px6`D;g>0h+R61f+;YEyNx*z>ZqCk=dWIM69)=u%?1Vd`y+5z-Ls ztS)bsq4Ypdyf(V$p1l_fsI3agD#Ott_j02fCx4Wc#Q?esR6Vli=*(xhT<1U@_CQK> zV*d-V>c_kL5$K{G6U1Tpce_0&mXtbstidsi0`j=~1M z^SZ9>CSpq3FWzu89t8igMAfwLIRbS@(PK}N5m*pW{uXlyp(lsn{OF%`LbHxTCgN^n z3uYRn*5If1+D|0Awtaizv=MN1WA!Ce#ss)g!)5acLBGa5LfcPMi`Mp*`<(tx_)19T{u-!% zFTAA_i}w6Fb@D~2HMrcBaQ%B7W(1(m=!c|u1q~IwQ~fB_{KBAmQL43v_uL;W`Gj!{ zboOT$bOQiK6Q*?r$IZny5?53!j^J7U{k^t>l*D(iIm{P>kUhfbfu)N8rb_Nb-+w2u z*KN-|C#Ln=cgdeVRk%>5XNV z?d2qQV}zYl8n!#IL?iMoffbWfMNUFo{0)XLY8kN~KMoMb0@v?mp+A971v-C4hz=Bb z;S0UB$T$Kl**3HXzm%uuB_S&q^uE%IxOz{A;YS33 zh4QL)0MqU~Q`-7o*aJBqk*guowU@~= zoMA96Ts)3=$$V?WZhJEmzeY%3(jJk+p~BpgsQ(K-oL`{1GVCW4uIN?4A#Ta@6UbZ_ z+~u*gj9Lj{9tW=YSC38K59Vk`H-sn!n@e598DIHxwg@SME4czek!4RUa3dllWINc2 ziH*HAN%_)L5jNR_h*tvKTA}^M2ZZ0;3aa>Tkp&l}b(Ik;a zH@}FXLI}I_Qg2JHL0(X1Stl?$lK{!OIgutQ3_b$e8r8C=kd9N!9}c-T{4{Xxo#|(> zzZ{S}VA?!H#77hzG#sY5Axk|#$em$&xbh)uWD0S_-g!9eHj;HwJtQ=~1Vb2hzd?jo zmO@{dhOGEzG5Q9z9*a&u zm`()vFvjiZaSIXG(9pPk>mp=zRTM>TtcBvb5O>36wnis~($)&ILRg(jL#G(FYX*c; z5O`syhco(O!ny&fXeAqastoPfSXlN{&>$Ll!2D|Ok16XxJOr6H-K0iHW^h$e%SI}p zWPNZau*J@o4iyu7>t_CKH4*bqWGA~u3BKGyI636dhBqySB{{=|Kt ze?6IM9uX$>R;nn!1hx&9u`x5edXhAtl;HGg=wwpF93t0 zg)Lyqg=YT?W%=osj}88_bN|4=B-WNmRJia4gkwZkV0|8>X>&xqOmuq1@aKS`{C#`o z`FX-sFk41rdIp4CoU@g+iPR@EOAfbxu^rW=YA&rfCBz)|M7&~RHqkYNL> z@jW0NH|~l^m~@bL!Kt&A654704ME9PuqQ(ILF{RS3Iq{Pe9uSg$lt@0(2qaJH}moG zI$vQc7}IwcMtfwxSiv2aI`oL2g>>)_wi$v19&oNFtV&Jg2NhxJ{FW{0>*F)tYVcL3 z`nXX~9v&d3Aao)SOS+<iJNH9C({`B?8qw6yVt=pWQ%2x%FxQ3lnNx%!{^m zs5qBgUDV3p^?kqMD2UO^H_3kr5N(Ox4-c|~C7k1inbmrUbsHxh-V1VsdE^Cpf1mi` zuxx@%bRg2EH}vxTAxX3;CJ|eTE`CJ>{qK7faLW$QAPOM%RFpkQT%p_|0!n-)QHVh> zB(YFZr-$<$YB1th*XjZu{n|AL8LDx}N3g6C5f7N+5WR|^$AZ}LW6wx^ef{g-QI6Qe zT`3x0K7N3b(FYVEh(rh?2Dk!H`Q4B6mz|xhOwNSy0RxDzgM&i_^*U7}h|M4O^N&{R zW}LltjfPl5kR)_=cABAB?#g6=Q}XcPLtQyq!dZbD&ZSFDegC?ob{t{73y-nh4d0GY zm?hqy)pE;>7egKmWOjXTuVAX!m9en^WZ^{NF)Ql<(CA2Uw|-fxpmafO%a|G15fRkV zS$wNVfSa-1D4RNn1}9lo>i6d$#Ewv)5h;#di)M~VWbxVfw6sHn2LeQKBqp$x!HT~MgEyA>=~(6jMA0iF;ii9nH!wg);kevmb7{PFbkUx7Z117nn7u35 z15mquNu;YpH2@|i!OevxW%4t~8_GR7i(X>18)-a(ToKwPVrN3uE(*O*(aM0Q*x&^Z z50BZYoW7sG-vi?Q987oE^N9Wd$yJM?idY?w$G?bD8{&+Wg(m5ptuL8jnfiu>V|FY) z`bEThHR-Bd`u1&uJVx!V(1{E*Nee}0hV8^N%c@JIODGH>;UMJL!~n+p-Fc?^2O%-S z=DkIs5!`iTl_)^2PvsC+94x$l20S?lNfY)+*oC3QD{@(l2qn@03@B`RSb4x=%e`gF z{o%*^q?`j4lLfHIDh&wOudz;iK=%y4UvwQmi^XsjtATU}w`%pnG(rRhohtU^eOw=H z5cnfiyhIW@Ym?WJD=QaWEXQ8Jv)iVv z$v2@Q6>_kXlWUGxk~Tz_YU}-NVCNg0(!Zf?M9}&XyWsMnpcy?k-_Xzi7sd@0m6PZ) z5N}|tr7AB!Z0eMV`aYIQt1g%8DjV=;K=FAXpfdA}KoJ<_gjbCO39|6@gL_np5Q*HE zl|`$F3sca=&4>M2^R&9mROSJz9QSkB8Acb zVfnz@d!QzY=G_J)^CB3rC@lOzv3CH=&3u&Y6(yo{WMF`L1W~0x;9!2|OoOaRrP~yW zby!g8QDMiOBfcT~Vg0q^cJ5ZIB}N`}2*=HVfnps3KJ zE`a{xx7GPkqIn6)BJv^ZqZrMY-GgA}gZS>Xy^b;}QERcc&)b$>2T|Z(^&WC~BK1_) zI~V;5D-BkS-Co}zr@Mt|gE)2L(eeEJ{DzK>S9o6-QdfM>sZfsfC=b^RzL zP}uHWjnRiCY+BeQ0-_vgyhZuZ!wKWf9Qxpi{5myp9ITk{6Jf3$Lk(~*61wqpL;$op8z9lGi z@C(F;P)ODA#^m@tzACP%Mnz@C+s%y>+XW5E zfi~0KH<0Ngm99t4n=>FYns9+*j$+BjN4UuP+X~fvWKn2_V(i^RDFeIxqwg;$@qP!c zX*lx1hln8M4|!u%Hd@{URUcD_=u64WJj+FP16dUEF;~?6kVuG&ivwwJ9G?m{Uc<8G zL&;cTTzcz+%UuI2#RWfhh~2$=OIP=!kg3S++YHcz5W9%9bUCI6PWq7PU-y2>qK%4K zxATQPLMwFRQ4+yWePnBU0P6=WsWMadqu-fUaF-H|b!s)y^Otr23mhgOn!G;pTb2Q=b+6-sfc`&K? zKfOeS>&A_zE@SCh7)d*Zp@ye`rJE9M4TKKrwC4{u%w=nrE}`j)+U)qj6MBs|9cBk^ z5fV0%VD0m@J^3~bJ)7H|`CZYUQg-pV)(=i5Zjw)~PoKN%Sh{i(52EOky=hgc5B4~j`I?mL(9_Ug4!`%uJMJ9d!%*6-iKyC?hbx(-jpG5|5I2<&<@NRoB+L~OK=uFCS$Dq31*kUn))yAXm39e5lXhDh%fQ6hoeu%)$i=^uwI zc%Q7WiC`yHnkhtvw6-x{C$Fk%C{T_cI9J@qiC4w$V*r_jKWgzja0@jf*9*=RU2{Yn z(1<)hUQNxYP*FwY^S5s|b9<(}9++C^t0QV@?XlBeI#;F_?eq~FuaeEU%i0+8DjrZ( zy8vfb^?0cA?-$p_#9m=x41Pikas(kw=p91d8i&d~f{iiAeeti2NGaDmgpT-{nh5mf zm`0;r3!l5ZhDHjM;uG8UNTm5JhF)S2Wa2IbWZ^%RsNvU^*VIhKf+(-3IDv#My~F7B zkB51-voV-qfs*HjXKjt^5kwypg|JV#xyrP@w@bC|VfMEnQZ+qI6qE#4 z`gabGjwYt0T=;MbMd{$`;YRp8h8FE{rV^p7epJp{E65edM;6}%rj|iQ2kLb3)ghdT zX=(TIVuI1q=y6-*s&(c)F>GBzW%Hg%j;dfZr?KjT2k(ihMj;}GRtKEh%ues8Pj}q< za9{na*^Q^yucMAaoRWp5E>qR@t|{x`p`qXTGexov4*71S?{MLht%=4xzo({6qw z6P!X}+(r`IHPwo8?crC?q$2s^P}#bNMHZ(@e_j*4p2zm~DriSl)7lMK@yXx6{~+iI z7O&4>kJr?A&E{d5NQGqRBge69RMCyQzurfs=O>>?VRG_aoOGo#m2*OG%~4PQ!bRh$MzgC)m}lw*w)b%)zs!`-4W%ykmpsQUAdrl{xpray z-CXm*GP?uM&K0W>O_X9D;!zbzJax{b- zSMK4Yw4(dmG~HRW)M=<3vX`2cR>9CPagmSKSJkF5pfZ-2@0Tw#icxP{8!TKmF)`^} zM-kW1*Non;9!=N`vgdw&ewmNZ3P89i+o=~482A{~0cw|JWT@12Z+}v*VY6x{~ZEW zt|jH)p_0*c@#yGl@PyyRTVN9|YtwUH`STv(=Lf__9PP4p%hT$Hjd60<8d_UFLTQ(@ z?(6HTcY1_&KiLzc99|DP{OBvn%Y?>C>8?m{4sLk;ftOE#V3h*EK%A{NyX9+H83oS}Wh^e25*? z!FzX+=Alv1$$R$bUVTj1_xJ7~u)x*I?pz4e=E~KY(f!<3jH1Hei}>%q>XDr$S1x`5 zn6p={u_ECfODqCA0sDnJFf@gf4=S4lDt->(YY`4kfW{OE(#mquNeR)bzVPWYPJ|-q6K7m72Oh!V&v)g3(b5mgBMt0s%b3_l3f32A+~+TVKm&7ZQ%!12191i+IV`=R-VmIh16IVv9MZ7 z>CIjGwoACTyYD1vXM6jKjXP=;-d= z>TuxjVHI#Nb?tMh?Rj@s0mC1-#OARvGc$wH+Z!95_4j@WI`$hlFue*mE4CVVHRxYp zka)-4WM*hkZ{5KA7}RQDZFU4U?NDbo=&jU`Y&Yq+Rpx zy8e3V@y91C;shBI?g6j=yQKDa_G@6*9XJhtdY%IxLxa6=qI7*1a2jPUaL6Ee)3OYc zT}w_@&0|{`+3;L{qgem(`M+kfGHi(Y^WiZ6tG92@?$QD(e@OM zu;~R6v&?V4k*Tkrq{(|8#P0CrL9hRPfBMub=K}<94I$ z-+Oij5I8fn)-eps+K|%%R1O3@$~nv+Izb{)8bmW3=C}=}CERYXffxr24w-=k+61>T zf*1xp8+burquFcNx=k>TjiW&UZ+E#Z>+w;CDu{uR%iFUYL#QM4C zeEY+-z_Rlafs5j_)%$x|#O{_K+)(@Pk8Np5&n#U@uuzkyn_w17qXijPcS zhitfI=qLqNK!VeV_uaSu|3B_p@>@Gh0OG{he_O(f50%{&*5iP<>hhcob7OV}fjJwx zKAa&rn22`^&X69YM(kGE`}^H)?N~l=;Dqs3;YvE2y(itxi+@=J3B;^F;t6>bA>6IM Q9)bisUHx3vIVCg!0F_#XC;$Ke literal 0 HcmV?d00001 diff --git a/docs/images/screenshots/iso_install_dd_linux.png b/docs/images/screenshots/iso_install_dd_linux.png new file mode 100644 index 0000000000000000000000000000000000000000..4bfbd6b069cd2a618bbdbdba5fa2233751d29e5a GIT binary patch literal 193045 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 aKOAOO_LDC8qw{V(i0|p@=d#Wzp$P!jj?I7o literal 0 HcmV?d00001 diff --git a/docs/images/screenshots/iso_install_etcher_macos.png b/docs/images/screenshots/iso_install_etcher_macos.png new file mode 100644 index 0000000000000000000000000000000000000000..251b48a8ecfb53d91b8a93a4f6878826b32df005 GIT binary patch literal 85762 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

x1YfW=3GjYNk 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 diff --git a/docs/images/hedgehog/images/pcap_compression.png b/docs/images/hedgehog/images/pcap_compression.png new file mode 100644 index 0000000000000000000000000000000000000000..0992069a3dbd20d698a6c71ff3969112ee599579 GIT binary patch literal 18860 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 literal 0 HcmV?d00001 diff --git a/docs/images/hedgehog/images/ssl_client_receive_code.png b/docs/images/hedgehog/images/ssl_client_receive_code.png new file mode 100644 index 0000000000000000000000000000000000000000..bf903d69ffb1fa18ad4795a7bf5107f30bff51ab GIT binary patch literal 22577 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

{ZQ1QYRcXcSjqbMNq!dq)a%ZxX3yLNDQUL z*`OcyMB~R=n>RX9R7wzoufNwy_nUgPpeqm?OQz>UrWdu}5+JWcDN@vbmX`oATtBSr ziQ$kyDk7-V&@oZa8mLvq_v)LCCk5sbYJZLM)MC{%1cA8Wi^ zH|`Q+{J66y&FEY|65#TS%x5mbhWE%XLbmt;(#?$IeXBEo*CucTkE1@fyFAjBtm|Ow zBr8^~Ag_=pvE*?`en|H67;E(_y`3Aretl<^L|@vWCT9lPCHV?@V=HD5As5)K2~zy* zOJ;-ij8|@Jn6>vcf`H@ccgvh<3_^TtBW*7YT)8mI*1zB`H0TBZ+koFH@w_$C@>i(1 zFOE9&gqNVc0c{T27qL=exI+18DjWY~($X3*>*4%t0PX z``8EliiQGA9xFIYQgF}mnBTiMX&WaPx_d-8sKG;;h7g&zlKa%~sdB}08|WiX-b|I1&-!<}t4R!krlqA75MO~A35?N09#+d=?J)!V znariDwWMwXCtG(4+{k0M_4TbO+(_w(<|Q|`#J0c7(t6cqaVyVOJo&;>;gHWAcqav* z`fJ#j;U2E^;{SrJqrjTJic`0%-5L%^bv!XMol2pfEVZ;`^To1-m8%w6)eC;O#9(Tut6sg}+O*lS zejzW?c$6k8SN*OVLAAHIq|3$4eP?aXZG9Kgi$<4m@I>Z%;75y}bF+PSY<`KW9-Yn2 z)gw|;jIK3gZhpf=PKwzj#E;ro_}toZ^?sX>fS8PlA5wpG-dU4rV|YFjzvfDk&+x0mAG zqloZ1S@y4y-=1-#)`bwcmuKd*gmJ4O5q3F&~et5W1;rL<c7~*{#)FrH3v?TYev2?PeWI1ya{Xdq++c z=V`*N*A(8Dw=@GwUF`T- zzW^TB+kI6`cDNeN_4N$42JoAMe8~l?_c1Zc!zQMtZ%w{%OG~#GuK+Lk)K8b$uVsK+b$IB@?w4*N`ed@te+2dj zp;u(2qtlH_N3`IKea=(US}rHj%WO5A@g@0MngQr7`qFe6kXstOuUX&VOyE#!7afd6%hV3L=3ryxU>n@w`y)Relly=4(8k~EU?gR8ay5Ns^C zW@ol9bF##HFqxL^o`9pHW1jP;-U350ZBrqeU+pNjz4bN2jgHPpHYrIk7;Fa~Ova;n zf2ArW2xmNT;z8x+-ZnNiHpIHBv$C;i-_MZj(MJ7h?|;tA5N=rV(!4dAg}?@)5PWg7 zO~Z3Gdz-4nVkT4PfP@zmdqh~?FZA8S=N)?IfOU};CJwLo@X5iZyLVD{WRuQ*xHy~F z$esk*my?}1fGI{3h^C_@RzqRKc}tDD0!(=EpH<@Ol`{9CJPFzuZ!aMs1cW&mb-Y=B zXmJNAHK`QyjqY=oD&Dkv$f&}qYZlELr;;SS5VX8!J}3BQd#XECiknRe)>)k0=C(Y) zp`)v7wUyYPu)5j#ab(|u6nt&iPh=8N16J1Jixc%;p65@?*Wt5Tt-ZVNik~gRq~bb0 zptOMJ_gaiqOWyOnO{$^0x<17#j@`CvF|pSZ^qvhLYhru+>*1J7cK1!R)ks~<4o1>X zWM-gk?%uJQkBvSn6*#_)XmT_LAB|Dp9o&0H@_9|A>g?d)S|`SGPdq4p$7yAC6;~A< zP5TKA&|fQ9vZ9I#DVwn|yjw9h98UhF%B?Y|kml}C|NF(o#gk6Dn7eGBa`tH^aMhTu z=b22Cc#`$CJb@ z^g>E&a-#;dOkg;`R)HSSfIz#Ux2Rr(jQ2=nNtIfUC+mVZB&}`9K$n2qaIXWcT;G|$ zTmOu+r(+tLR-#P}%wNug@;gasrr3QW1HBXp+=1!~LMuF~o>GA_EClX?PKW*(@}~s_ zGJ!x&pR$O3`(hY7uW!P z3Wsp$q;JrqvH;VBv;41dC%^Nb#{HT2r1d8@Po9?&Fm;o@NCdVBq^zj%uu$GC@V@{w CxDlrS literal 0 HcmV?d00001 diff --git a/docs/images/hedgehog/images/ssl_client_transmit.png b/docs/images/hedgehog/images/ssl_client_transmit.png new file mode 100644 index 0000000000000000000000000000000000000000..f3fd88e2d50b74dc44955f37bf297a2b441795ae GIT binary patch literal 54739 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 PkxEuZ=~~{^+ur{R5!V+- literal 0 HcmV?d00001 diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md index 58852f66e..efaa7e27b 100644 --- a/docs/malcolm-hedgehog-e2e-iso-install.md +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -18,8 +18,16 @@ In contrast to using the ISO installer, Malcolm can also be installed "natively" - [Setting up Authentication](#MalcolmAuthSetup) * [Hedgehog Linux Installation and Configuration](#HedgehogInstallAndConfig) - [Hedgehog Linux ISO Installation](#ISOInstallHedgehog) - - [Configure Interfaces](#HedgehogInterfaces) - - [Configure Capture and Forwarding](#HedgehogCapture) + - [Configure Hostname, Interfaces and Time Sync](#HedgehogInterfaces) + - [Configure Capture](#HedgehogCapture) + + [Capture](#HedgehogConfigCapture) + + [File extraction and scanning](#HedgehogZeekFileExtraction) + - [Configure Forwarding](#HedgehogConfigForwarding) + * [arkime-capture](#Hedgehogarkime-capture): Arkime session forwarding + * [ssl-client-receive](#HedgehogGetCerts): Receive client SSL files for filebeat from Malcolm + * [filebeat](#Hedgehogfilebeat): Zeek and Suricata log forwarding + * [miscbeat](#Hedgehogmiscbeat): System metrics forwarding + + [Autostart services](#HedgehogConfigAutostart) ## Obtaining the Installation ISOs @@ -269,10 +277,12 @@ You will be prompted to do the following: * Store username/password for email alert sender account: answer **Y** to specify credentials for [Email Sender Accounts](alerting.md#AlertingEmail) to be used with OpenSearch Dashboards' alerting plugin * (Re)generate internal passwords for NetBox: if you answered **Y** to "Should Malcolm run and maintain an instance of NetBox...?" during the configuration questions, you should need to asnwer **Y** to this question at least the first time you start Malcolm * Transfer self-signed client certificates to a remote log forwarder: in order for a Hedgehog Linux to securely communicate with Malcolm, it needs the client certificates generated when you answered **Y** to "(Re)generate self-signed certificates for a remote log forwarder" a few moments ago. Malcolm can facilitate the secure transfer of these to a sensor running Hedgehog. If you will be continuing on to configure a sensor running Hedgehog Linux, answer **Y** here. - - You're prompted to "Run configure-capture on the remote log forwarder, select 'Configure Forwarding,' then 'Receive client SSL files...'." Return here and press **Enter** when you've finished with [Configure Capture and Forwarding](#HedgehogCapture) below. + - You're prompted to "Run configure-capture on the remote log forwarder, select 'Configure Forwarding,' then 'Receive client SSL files...'." Continue on with the instructions for [Hedgehog Linux Installation and Configuration](#HedgehogInstallAndConfig), and return here and press **Enter** when you get to **[ssl-client-receive](#HedgehogGetCerts): Receive client SSL files for filebeat from Malcolm** below. After that process is complete, press **OK** and Malcolm will continue to start up. ## Hedgehog Linux Installation and Configuration +More detailed instructions for configuring Hedgehog Linux can be found in that section of the [documentation](hedgehog.md). + ## Hedgehog Linux ISO Installation The Hedgehog Linux installation ISO follows the same process as the [Malcolm installation](#ISOInstallMalcolm) above. @@ -290,8 +300,268 @@ At the end of the installation process, you will be prompted with a few self-exp * **Should the GUI session be locked due to inactivity?** * **Display the [Standard Mandatory DoD Notice and Consent Banner](https://www.stigviewer.com/stig/application_security_and_development/2018-12-24/finding/V-69349)?** *(only applies when installed on U.S. government information systems)* -Following these prompts, the installer will reboot and Hedgehog Linux will boot. +Following these prompts, the installer will reboot and Hedgehog Linux will boot into [kiosk mode](hedgehog-boot.md#HedgehogKioskMode). To continue setup, press **Alt+F4** to exit kiosk mode to the Hedgehog Linux desktop environment. + +![Hedgehog Linux desktop](./images/hedgehog/images/desktop.png) + +*The Hedgehog Linux desktop* + +## Configure Hostname, Interfaces and Time Sync + +https://malcolm.fyi/docs/hedgehog-config-root.html#HedgehogConfigRoot + +The first step of sensor configuration is to configure the network interfaces and sensor hostname. Clicking the **Configure Interfaces and Hostname** toolbar icon (or, if you are at a command line prompt, running `configure-interfaces`) will prompt you for the root password you created during installation, after which the configuration welcome screen is shown. Select **Continue** to proceed. + +You may next select whether to configure the network interfaces, hostname, or time synchronization. + +![Selection to configure network interfaces, hostname, or time synchronization](./images/hedgehog/images/root_config_mode.png) + +Selecting **Hostname**, you will be presented with a summary of the current sensor identification information, after which you may specify a new sensor hostname. This name will be used to tag all events forwarded from this sensor in the events' **host.name** field. + +![Specifying a new sensor hostname](./images/hedgehog/images/hostname_setting.png) + +Returning to the configuration mode selection, choose **Interface**. You will be prompted if you would like help identifying network interfaces. If you select **Yes**, you will be prompted to select a network interface, after which that interface's link LED will blink for 10 seconds to help you in its identification. This network interface identification aid will continue to prompt you to identify further network interfaces until you select **No**. + +You will be presented with a list of interfaces to configure as the sensor management interface. This is the interface the sensor itself will use to communicate with the network in order to, for example, forward captured logs to an aggregate server. In order to do so, the management interface must be assigned an IP address. This is generally **not** the interface used for capturing data. Select the interface to which you wish to assign an IP address. The interfaces are listed by name and MAC address and the associated link speed is also displayed if it can be determined. For interfaces without a connected network cable, generally a `-1` will be displayed instead of the interface speed. + +![Management interface selection](./images/hedgehog/images/select_iface.png) + +Depending on the configuration of your network, you may now specify how the management interface will be assigned an IP address. In order to communicate with an event aggregator over the management interface, either **static** or **dhcp** must be selected. + +![Interface address source](./images/hedgehog/images/iface_mode.png) + +If you select static, you will be prompted to enter the IP address, netmask, and gateway to assign to the management interface. + +![Static IP configuration](./images/hedgehog/images/iface_static.png) + +In either case, upon selecting **OK** the network interface will be brought down, configured, and brought back up, and the result of the operation will be displayed. You may choose **Quit** upon returning to the configuration tool's welcome screen. + +Returning to the configuration mode selection, choose **Time Sync**. Here you can configure the sensor to keep its time synchronized with either an NTP server (using the NTP protocol) or a local [Malcolm]({{ site.github.repository_url }}) aggregator or another HTTP/HTTPS server. On the next dialog, choose the time synchronization method you wish to configure. + +![Time synchronization method](./images/hedgehog/images/time_sync_mode.png) + +If **htpdate** is selected, you will be prompted to enter the IP address or hostname and port of an HTTP/HTTPS server (for a Malcolm instance, port `9200` may be used) and the time synchronization check frequency in minutes. A test connection will be made to determine if the time can be retrieved from the server. + +![*htpdate* configuration](./images/hedgehog/images/htpdate_setup.png) + +If *ntpdate* is selected, you will be prompted to enter the IP address or hostname of the NTP server. + +![NTP configuration](./images/hedgehog/images/ntp_host.png) + +Upon configuring time synchronization, a "Time synchronization configured successfully!" message will be displayed, after which you will be returned to the welcome screen. Select **Cancel**. + +## Configure Capture + +Clicking the **Configure Capture and Forwarding** toolbar icon (or, if you are at a command prompt, running `configure-capture`) will launch the configuration tool for capture and forwarding. The root password is not required as it was for the interface and hostname configuration, as sensor services are run under the non-privileged sensor account. Select **Continue** to proceed. You may select from a list of configuration options. + +![Select configuration mode](./images/hedgehog/images/capture_config_main.png) + +### Capture + +Choose **Configure Capture** to configure parameters related to traffic capture and local analysis. You will be prompted if you would like help identifying network interfaces. If you select **Yes**, you will be prompted to select a network interface, after which that interface's link LED will blink for 10 seconds to help you in its identification. This network interface identification aid will continue to prompt you to identify further network interfaces until you select **No**. + +You will be presented with a list of network interfaces and prompted to select one or more capture interfaces. An interface used to capture traffic is generally a different interface than the one selected previously as the management interface, and each capture interface should be connected to a network tap or span port for traffic monitoring. Capture interfaces are usually not assigned an IP address as they are only used to passively “listen” to the traffic on the wire. The interfaces are listed by name and MAC address and the associated link speed is also displayed if it can be determined. For interfaces without a connected network cable, generally a `-1` will be displayed instead of the interface speed. + +![Select capture interfaces](./images/hedgehog/images/capture_iface_select.png) + +Upon choosing the capture interfaces and selecting OK, you may optionally provide a capture filter. This filter will be used to limit what traffic the PCAP service ([`tcpdump`](https://www.tcpdump.org/)) and the traffic analysis services ([`zeek`](https://www.zeek.org/) and [`suricata`](https://suricata.io/)) will see. Capture filters are specified using [Berkeley Packet Filter (BPF)](http://biot.com/capstats/bpf.html) syntax. For example, to indicate that Hedgehog should ignore the ports it uses to communicate with Malcolm, you could specify `not port 5044 and not port 5045 and not port 8005 and not port 9200`. Clicking **OK** will attempt to validate the capture filter, if specified, and will present a warning if the filter is invalid. + +![Specify capture filters](./images/hedgehog/images/capture_filter.png) + +Next you must specify the paths where captured PCAP files and logs will be stored locally on the sensor. If the installation worked as expected, these paths should be prepopulated to reflect paths on the volumes formatted at install time for the purpose storing these artifacts. Usually these paths will exist on separate storage volumes. Enabling the PCAP and log pruning autostart services (see the section on autostart services below) will enable monitoring of these paths to ensure that their contents do not consume more than 90% of their respective volumes' space. Choose **OK** to continue. + +![Specify capture paths](./images/hedgehog/images/capture_paths.png) + +### File extraction and scanning + +Hedgehog Linux can leverage Zeek's knowledge of network protocols to automatically detect file transfers and extract those files from network traffic as Zeek sees them. + +To specify which files should be extracted, specify the Zeek file carving mode: + +![Zeek file carving mode](./images/hedgehog/images/zeek_file_carve_mode.png) + +If you're not sure what to choose, either of **mapped (except common plain text files)** (if you want to carve and scan almost all files) or **interesting** (if you only want to carve and scan files with [mime types of common attack vectors]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/sensor-iso/interface/sensor_ctl/zeek/extractor_override.interesting.zeek)) is probably a good choice. + +Next, specify which carved files to preserve (saved on the sensor under `/capture/bro/capture/extract_files/quarantine` by default). In order to not consume all of the sensor's available storage space, the oldest preserved files will be pruned along with the oldest Zeek logs as described below with **AUTOSTART_PRUNE_ZEEK** in the [autostart services](#HedgehogConfigAutostart) section. + +You'll be prompted to specify which engine(s) to use to analyze extracted files. Extracted files can be examined through any of three methods: + +![File scanners](./images/hedgehog/images/zeek_file_carve_scanners.png) + +* scanning files with [**ClamAV**](https://www.clamav.net/); to enable this method, select **ZEEK_FILE_SCAN_CLAMAV** when specifying scanners for Zeek-carved files +* submitting file hashes to [**VirusTotal**](https://www.virustotal.com/en/#search); to enable this method, select **ZEEK_FILE_SCAN_VTOT** when specifying scanners for Zeek-carved files, then manually edit `/opt/sensor/sensor_ctl/control_vars.conf` and specify your [VirusTotal API key](https://developers.virustotal.com/reference) in `VTOT_API2_KEY` +* scanning files with [**Yara**](https://github.com/VirusTotal/yara); to enable this method, select **ZEEK_FILE_SCAN_YARA** when specifying scanners for Zeek-carved files +* scanning portable executable (PE) files with [**Capa**](https://github.com/fireeye/capa); to enable this method, select **ZEEK_FILE_SCAN_CAPA** when specifying scanners for Zeek-carved files + +Files which are flagged as potentially malicious will be logged as Zeek `signatures.log` entries, and can be viewed in the **Signatures** dashboard in [OpenSearch Dashboards]({{ site.github.repository_url }}#DashboardsVisualizations) when forwarded to Malcolm. + +![File quarantine](./images/hedgehog/images/file_quarantine.png) + +Finally, you will be presented with the list of configuration variables that will be used for capture, including the values which you have configured up to this point in this section. Upon choosing **OK** these values will be written back out to the sensor configuration file located at `/opt/sensor/sensor_ctl/control_vars.conf`. It is not recommended that you edit this file manually. After confirming these values, you will be presented with a confirmation that these settings have been written to the configuration file, and you will be returned to the welcome screen. + +## Configure Forwarding + +Select **Configure Forwarding** to set up forwarding logs and statistics from the sensor to an aggregator server, such as [Malcolm]({{ site.github.repository_url }}). + +![Configure forwarders](./images/hedgehog/images/forwarder_config.png) + +There are three forwarder services used on the sensor, each for forwarding a different type of log or sensor metric. + +### arkime-capture: Arkime session forwarding + +arkime-[capture](https://github.com/arkime/arkime/tree/master/capture) is not only used to capture PCAP files, but also the parse raw traffic into sessions and forward this session metadata to an [OpenSearch](https://opensearch.org/) database so that it can be viewed in [Arkime viewer](https://arkime.com/), whether standalone or as part of a [Malcolm]({{ site.github.repository_url }}) instance. If you're using Hedgehog Linux with Malcolm, please read [Correlating Zeek logs and Arkime sessions]({{ site.github.repository_url }}#ZeekArkimeFlowCorrelation) in the Malcolm documentation for more information. + +First, select the OpenSearch connection transport protocol, either **HTTPS** or **HTTP**. If the metrics are being forwarded to Malcolm, select **HTTPS** to encrypt messages from the sensor to the aggregator using TLS v1.2 using ECDHE-RSA-AES128-GCM-SHA256. If **HTTPS** is chosen, you must choose whether to enable SSL certificate verification. If you are using a self-signed certificate (such as the one automatically created during [Malcolm's configuration]({{ site.github.repository_url }}#configure-authentication)), choose **None**. + +![OpenSearch connection protocol](./images/hedgehog/images/opensearch_connection_protocol.png) ![OpenSearch SSL verification](./images/hedgehog/images/opensearch_ssl_verification.png) + +Next, enter the **OpenSearch host** IP address (ie., the IP address of the aggregator) and port. These metrics are written to an OpenSearch database using a RESTful API, usually using port 9200. Depending on your network configuration, you may need to open this port in your firewall to allow this connection from the sensor to the aggregator. + +![OpenSearch host and port](./images/hedgehog/images/arkime-capture-ip-port.png) + +You will be asked to enter authentication credentials for the sensor's connections to the aggregator's OpenSearch API. After you've entered the username and the password, the sensor will attempt a test connection to OpenSearch using the connection information provided. If the Malcolm services have not yet been started, you may receive a **Connection refused** error. You may select **Ignore Error** for the credentials to be accepted anyway. + +![OpenSearch username](./images/hedgehog/images/opensearch_username.png) ![OpenSearch password](./images/hedgehog/images/opensearch_password.png) ![Successful OpenSearch connection](./images/hedgehog/images/opensearch_connection_success.png) + +You will be shown a dialog for a list of IP addresses used to populate an access control list (ACL) for hosts allowed to connect back to the sensor for retrieving session payloads from its PCAP files for display in Arkime viewer. The list will be prepopulated with the IP address entered a few screens prior to this one. + +![PCAP retrieval ACL](./images/hedgehog/images/malcolm_arkime_reachback_acl.png) + +Arkime supports [compression](https://arkime.com/settings#writer-simple) for the PCAP files it creates. Select `none` (at the cost of requiring more storage for PCAP files saved on the sensor) or `zstd` (at the cost of higher CPU load when writing and reading PCAP files). If you choose [`zstd`](https://en.wikipedia.org/wiki/Zstd?lang=en), you'll also be prompted for the compression level (something like `3` is probably a good choice). + +![PCAP compression](./images/hedgehog/images/pcap_compression.png) + +Finally, you'll be given the opportunity to review the all of the Arkime `capture` options you've specified. Selecting **OK** will cause the parameters to be saved and you will be returned to the configuration tool's welcome screen. + +![capture settings confirmation](./images/hedgehog/images/arkime_confirm.png) + +### ssl-client-receive: Receive client SSL files for filebeat from Malcolm + +As described above in the Malcolm configuration under [Setting up Authentication](#MalcolmAuthSetup), in order for a Hedgehog Linux to securely communicate with Malcolm, it needs the client certificates generated when you answered **Y** to "(Re)generate self-signed certificates for a remote log forwarder" during that setup. Malcolm can facilitate the secure transfer of these to a sensor running Hedgehog. + +![ssl-client-receive](./images/hedgehog/images/ssl_client_receive.png) + +*Select* ***ssl-client-receive*** *on Hedgehog* + +Select **ssl-client-receive** from the **Configuration Mode** options on the Hedgehog, then press **OK** when prompted "Run auth_setup on Malcolm 'Transfer self-signed client certificates...'." [Return](#MalcolmAuthSetup) to the Malcolm instance where `auth_setup` is running (or re-run it if needed) and press **OK**. You'll see a message with the title **ssl-client-transmit** that looks like this: + +![ssl-client-transmit](./images/hedgehog/images/ssl_client_transmit.png) + +*Run* ***auth_setup*** *and select* ***ssl-client-transmit*** *on Malcolm* + +Note Malcolm's IP address (`192.168.122.5` in the screenshot above) and the single-use code phrase (`8736-janet-kilo-tonight` in the screenshot above) and enter them on the Hedgehog: + +![ssl-client-receive-code](./images/hedgehog/images/ssl_client_receive_code.png) + +*Enter Malcolm IP address and single-use code phrase on Hedgehog* + +After a few seconds (hopefully) a progress bar will update and show the files have been 100% transfered. They are automatically saved into the `/opt/sensor/sensor_ctl/logstash-client-certificates` directory on the sensor. + +Press **OK** on the Malcolm instance. If Malcolm's `auth_setup` process was being during Malcolm's first run, Malcolm will continue to start up. + +### filebeat: Zeek and Suricata log forwarding + +[Filebeat](https://www.elastic.co/products/beats/filebeat) is used to forward [Zeek](https://www.zeek.org/) and [Suricata](https://suricata.io/) logs to a remote [Logstash](https://www.elastic.co/products/logstash) instance for further enrichment prior to insertion into an [OpenSearch](https://opensearch.org/) database. + +To configure filebeat, first provide the log path (the same path previously configured for log file generation). + +![Configure filebeat for log forwarding](./images/hedgehog/images/filebeat_log_path.png) + +You must also provide the IP address of the Logstash instance to which the logs are to be forwarded, and the port on which Logstash is listening. These logs are forwarded using the Beats protocol, generally over port 5044. Depending on your network configuration, you may need to open this port in your firewall to allow this connection from the sensor to the aggregator. + +![Configure filebeat for log forwrading](./images/hedgehog/images/filebeat_ip_port.png) + +Next you are asked whether the connection used for log forwarding should be done **unencrypted** or over **SSL**. Unencrypted communication requires less processing overhead and is simpler to configure, but the contents of the logs may be visible to anyone who is able to intercept that traffic. + +![Filebeat SSL certificate verification](./images/hedgehog/images/filebeat_ssl.png) + +If **SSL** is chosen, you must choose whether to enable [SSL certificate verification](https://www.elastic.co/guide/en/beats/filebeat/current/configuring-ssl-logstash.html). If you are using a self-signed certificate (such as the one automatically created during [Malcolm's configuration]({{ site.github.repository_url }}#configure-authentication), choose **None**. + +![Unencrypted vs. SSL encryption for log forwarding](./images/hedgehog/images/filebeat_ssl_verify.png) + +The last step for SSL-encrypted log forwarding is to specify the SSL certificate authority, certificate, and key files. These files must match those used by the Logstash instance receiving the logs on the aggregator. The steps above under **[ssl-client-receive](#HedgehogGetCerts): Receive client SSL files for filebeat from Malcolm** should have taken care of the transfer of these files between Malcolm and Hedgehog. Otherwise, manually copy ("sneakernet") the files from the `filebeat/certs/` subdirectory of the Malcolm installation to `/opt/sensor/sensor_ctl/logstash-client-certificates` on Hedgehog. + +![SSL certificate files](./images/hedgehog/images/filebeat_certs.png) + +Once you have specified all of the filebeat parameters, you will be presented with a summary of the settings related to the forwarding of these logs. Selecting **OK** will cause the parameters to be written to filebeat's configuration keystore under `/opt/sensor/sensor_ctl/logstash-client-certificates` and you will be returned to the configuration tool's welcome screen. If the Malcolm services have not yet been started, you may receive a **could not connect** error. You may select **Ignore Error** for the settings to be accepted anyway. + +![Confirm filebeat settings](./images/hedgehog/images/filebeat_confirm.png) + +### miscbeat: System metrics forwarding + +The sensor uses [Fluent Bit](https://fluentbit.io/) to gather miscellaneous system resource metrics (CPU, network I/O, disk I/O, memory utilization, temperature, etc.) and the [Beats](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-tcp.html) protocol to forward these metrics to a remote [Logstash](https://www.elastic.co/products/logstash) instance for further enrichment prior to insertion into an [OpenSearch](https://opensearch.org/) database. Metrics categories can be enabled/disabled as described in the [autostart services](#HedgehogConfigAutostart) section of this document. + +This forwarder's configuration is almost identical to that of [filebeat](#Hedgehogfilebeat) in the previous section. Select `miscbeat` from the forwarding configuration mode options and follow the same steps outlined above to set up this forwarder. + +### Autostart services + +Once the forwarders have been configured, the final step is to **Configure Autostart Services**. Choose this option from the configuration mode menu after the welcome screen of the sensor configuration tool. + +Despite configuring capture and/or forwarder services as described in previous sections, only services enabled in the autostart configuration will run when the sensor starts up. The available autostart processes are as follows (recommended services are in **bold text**): + +* **AUTOSTART_ARKIME** - [capture](#Hedgehogarkime-capture) PCAP engine for traffic capture, as well as traffic parsing and metadata insertion into OpenSearch for viewing in [Arkime](https://arkime.com/). If you are using Hedgehog Linux along with [Malcolm]({{ site.github.repository_url }}) or another Arkime installation, this is probably the packet capture engine you want to use. +* **AUTOSTART_CLAMAV_UPDATES** - Virus database update service for ClamAV (requires sensor to be connected to the internet) +* **AUTOSTART_FILEBEAT** - [filebeat](#Hedgehogfilebeat) Zeek and Suricata log forwarder +* **AUTOSTART_FLUENTBIT_AIDE** - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/exec) [AIDE](https://aide.github.io/) file system integrity checks +* **AUTOSTART_FLUENTBIT_AUDITLOG** - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/tail) [auditd](https://man7.org/linux/man-pages/man8/auditd.8.html) logs +* *AUTOSTART_FLUENTBIT_KMSG* - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/kernel-logs) the Linux kernel log buffer (these are generally reflected in syslog as well, which may make this agent redundant) +* **AUTOSTART_FLUENTBIT_METRICS** - [Fluent Bit](https://fluentbit.io/) agent for collecting [various](https://docs.fluentbit.io/manual/pipeline/inputs) system resource and performance metrics +* **AUTOSTART_FLUENTBIT_SYSLOG** - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/syslog) Linux syslog messages +* **AUTOSTART_FLUENTBIT_THERMAL** - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/thermal) system temperatures (only applicable on actual hardware, not if Hedgehog is running on a virtual machine) +* **AUTOSTART_MISCBEAT** - [filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-tcp.html) forwarder which sends system metrics collected by [Fluent Bit](https://fluentbit.io/) to a remote Logstash instance (e.g., [Malcolm]({{ site.github.repository_url }})'s) +* *AUTOSTART_NETSNIFF* - [netsniff-ng](http://netsniff-ng.org/) PCAP engine for saving packet capture (PCAP) files +* **AUTOSTART_PRUNE_PCAP** - storage space monitor to ensure that PCAP files do not consume more than 90% of the total size of the storage volume to which PCAP files are written +* **AUTOSTART_PRUNE_ZEEK** - storage space monitor to ensure that Zeek logs do not consume more than 90% of the total size of the storage volume to which Zeek logs are written +* **AUTOSTART_SURICATA** - [Suricata](https://suricata.io/) traffic analysis engine +* **AUTOSTART_SURICATA_UPDATES** - Rule update service for Suricata (requires sensor to be connected to the internet) +* *AUTOSTART_TCPDUMP* - [tcpdump](https://www.tcpdump.org/) PCAP engine for saving packet capture (PCAP) files +* **AUTOSTART_ZEEK** - [Zeek](https://www.zeek.org/) traffic analysis engine + +Note that only one packet capture engine ([capture](https://arkime.com/), [netsniff-ng](http://netsniff-ng.org/), or [tcpdump](https://www.tcpdump.org/)) can be used. + +![Autostart services](./images/hedgehog/images/autostarts.png) + +Once you have selected the autostart services, you will be prompted to confirm your selections. Doing so will cause these values to be written back out to the `/opt/sensor/sensor_ctl/control_vars.conf` configuration file. + +![Autostart services confirmation](./images/hedgehog/images/autostarts_confirm.png) + +After you have completed configuring the sensor it is recommended that you **reboot** Hedgehog to ensure all new settings take effect. If rebooting is not an option, you may click the **Restart Sensor Services** menu icon in the top menu bar, or open a terminal and run: + +``` +/opt/sensor/sensor_ctl/shutdown && sleep 10 && /opt/sensor/sensor_ctl/supervisor.sh +``` -## Configure Interfaces +This will cause the sensor services controller to stop, wait a few seconds, and restart. You can check the status of the sensor's processes by choosing **Sensor Status** from the sensor's kiosk mode, clicking the **Sensor Service Status** toolbar icon, or running `/opt/sensor/sensor_ctl/status` from the command line: -## Configure Capture and Forwarding \ No newline at end of file +``` +$ /opt/sensor/sensor_ctl/status +arkime:arkime-capture RUNNING pid 6455, uptime 0:03:17 +arkime:arkime-viewer RUNNING pid 6456, uptime 0:03:17 +beats:filebeat RUNNING pid 6457, uptime 0:03:17 +beats:miscbeat RUNNING pid 6458, uptime 0:03:17 +clamav:clamav-service RUNNING pid 6459, uptime 0:03:17 +clamav:clamav-updates RUNNING pid 6461, uptime 0:03:17 +fluentbit-auditlog RUNNING pid 6463, uptime 0:03:17 +fluentbit-kmsg STOPPED Not started +fluentbit-metrics:cpu RUNNING pid 6466, uptime 0:03:17 +fluentbit-metrics:df RUNNING pid 6471, uptime 0:03:17 +fluentbit-metrics:disk RUNNING pid 6468, uptime 0:03:17 +fluentbit-metrics:mem RUNNING pid 6472, uptime 0:03:17 +fluentbit-metrics:mem_p RUNNING pid 6473, uptime 0:03:17 +fluentbit-metrics:netif RUNNING pid 6474, uptime 0:03:17 +fluentbit-syslog RUNNING pid 6478, uptime 0:03:17 +fluentbit-thermal RUNNING pid 6480, uptime 0:03:17 +netsniff:netsniff-enp1s0 STOPPED Not started +prune:prune-pcap RUNNING pid 6484, uptime 0:03:17 +prune:prune-zeek RUNNING pid 6486, uptime 0:03:17 +supercronic RUNNING pid 6490, uptime 0:03:17 +suricata RUNNING pid 6501, uptime 0:03:17 +tcpdump:tcpdump-enp1s0 STOPPED Not started +zeek:capa RUNNING pid 6553, uptime 0:03:17 +zeek:clamav RUNNING pid 6512, uptime 0:03:17 +zeek:logger RUNNING pid 6554, uptime 0:03:17 +zeek:virustotal STOPPED Not started +zeek:watcher RUNNING pid 6510, uptime 0:03:17 +zeek:yara RUNNING pid 6548, uptime 0:03:17 +zeek:zeekctl RUNNING pid 6502, uptime 0:03:17 +``` \ No newline at end of file From a7ab52ef9dc5b021ca9516d65a0cdbe760162d21 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 11 Apr 2023 13:39:04 -0600 Subject: [PATCH 121/239] documentation wip --- docs/malcolm-hedgehog-e2e-iso-install.md | 43 ++++++++++++++++++++---- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md index efaa7e27b..1d48fa9eb 100644 --- a/docs/malcolm-hedgehog-e2e-iso-install.md +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -15,9 +15,11 @@ In contrast to using the ISO installer, Malcolm can also be installed "natively" - [ISO Installation](#ISOInstallMalcolm) - [Desktop Environment](#MalcolmDesktop) - [Configuration](#MalcolmConfig) + - [Configure Hostname and Time Sync](#MalcolmTimeSync) - [Setting up Authentication](#MalcolmAuthSetup) * [Hedgehog Linux Installation and Configuration](#HedgehogInstallAndConfig) - [Hedgehog Linux ISO Installation](#ISOInstallHedgehog) + - [Desktop Environment](#HedgehogDesktop) - [Configure Hostname, Interfaces and Time Sync](#HedgehogInterfaces) - [Configure Capture](#HedgehogCapture) + [Capture](#HedgehogConfigCapture) @@ -28,6 +30,7 @@ In contrast to using the ISO installer, Malcolm can also be installed "natively" * [filebeat](#Hedgehogfilebeat): Zeek and Suricata log forwarding * [miscbeat](#Hedgehogmiscbeat): System metrics forwarding + [Autostart services](#HedgehogConfigAutostart) +* [Verifying Traffic Capture and Forwarding](#Verify) ## Obtaining the Installation ISOs @@ -69,7 +72,7 @@ The ISO media boot on systems that support EFI-mode and legacy (BIOS) booting. C ## Malcolm Installation and Configuration -### Malcolm ISO Installation +### ISO Installation Upon Booting the Malcolm installation ISO, you're presented with the following **Boot menu**. Use the arrow keys to select **Install Malcolm**, and press Enter. @@ -114,7 +117,7 @@ Following these prompts, the installer will reboot and the Malcolm base operatin The Malcolm installer does not require an internet connection to complete successfully. If the installer prompts you to configure network connectivity, you may choose "do not configure the network at this time." -### Malcolm Desktop Environment +### Desktop Environment The Malcolm base operating system is a [hardened](hardening.md#Hardening) Linux installation based on the current [stable release](https://wiki.debian.org/DebianStable) of [Debian](https://www.debian.org/) [running](https://wiki.debian.org/Xfce) the [XFCE desktop environment](https://www.xfce.org/). It has been preloaded with all of the [components](components.md#Components) that make up Malcolm. @@ -126,8 +129,7 @@ The panel bordering the top of the Malcolm desktop is home to a number of useful ![Malcolm Desktop](./images/screenshots/malcolm_desktop.png) - -### Malcolm Configuration +### Configuration The first time the Malcolm base operating system boots the **Malcolm Configuration** wizard will start automatically. This same configuration script can be run again later by running [`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning) from the Malcolm installation directory, or clicking the **Configure Malcolm** 🔳 icon in the top panel. @@ -258,7 +260,25 @@ The [configuration and tuning](malcolm-config.md#ConfigAndTuning) wizard's quest * Enable dark mode for OpenSearch Dashboards? - Answer **Y** if you prefer dark dashboards, **N** if you prefer light ones. -### Setting up Authentication for Malcolm +### Configure Hostname and Time Sync + +If you wish to change Malcolm's hostname or configure system time synchronization, open a terminal (the icon immediately to the right of the **Applications** menu icon at the top of the Malcolm desktop) and run `sudo configure-interfaces.py` then enter your password. If you get an error about your user not belonging to the `sudo` group, run `su -c configure-interfaces.py` and use the `root` password instead. + +Here you can configure Malcolm to keep its time synchronized with either an NTP server (using the NTP protocol), another [Malcolm]({{ site.github.repository_url }}) aggregator or another HTTP/HTTPS server. On the next dialog, choose the time synchronization method you wish to configure. + +![Time synchronization method](./images/hedgehog/images/time_sync_mode.png) + +If **htpdate** is selected, you will be prompted to enter the IP address or hostname and port of an HTTP/HTTPS server (for another Malcolm instance, port `9200` may be used) and the time synchronization check frequency in minutes. A test connection will be made to determine if the time can be retrieved from the server. + +![*htpdate* configuration](./images/hedgehog/images/htpdate_setup.png) + +If *ntpdate* is selected, you will be prompted to enter the IP address or hostname of the NTP server. + +![NTP configuration](./images/hedgehog/images/ntp_host.png) + +Upon configuring time synchronization, a "Time synchronization configured successfully!" message will be displayed, after which you will be returned to the welcome screen. Select **Cancel**. + +### Setting up Authentication Once the [configuration](#MalcolmConfig) questions have been completed as described above, you can click the circular yellow Malcolm icon the panel at the top of the [desktop](#MalcolmDesktop) to start Malcolm. As you have not yet configured authentication, you will be prompted to do so. This authentication setup can be run again later by running [`./scripts/auth_setup`](authsetup.md#AuthSetup) from the Malcolm installation directory. @@ -302,6 +322,14 @@ At the end of the installation process, you will be prompted with a few self-exp Following these prompts, the installer will reboot and Hedgehog Linux will boot into [kiosk mode](hedgehog-boot.md#HedgehogKioskMode). To continue setup, press **Alt+F4** to exit kiosk mode to the Hedgehog Linux desktop environment. +### Desktop Environment + +The Hedgehog Linux base operating system is a [hardened](hedgehog-hardening.md#HedgehogHardening) Linux installation based on the current [stable release](https://wiki.debian.org/DebianStable) of [Debian](https://www.debian.org/) [running](https://wiki.debian.org/Xfce) the [XFCE desktop environment](https://www.xfce.org/). + +Display resolution should be detected and adjusted automatically. If you need to make changes to display properties, click the **Applications** menu and select **Settings** → **Display**. + +The panel bordering the top of the Malcolm desktop is home to a number of useful shortcuts: + ![Hedgehog Linux desktop](./images/hedgehog/images/desktop.png) *The Hedgehog Linux desktop* @@ -564,4 +592,7 @@ zeek:virustotal STOPPED Not started zeek:watcher RUNNING pid 6510, uptime 0:03:17 zeek:yara RUNNING pid 6548, uptime 0:03:17 zeek:zeekctl RUNNING pid 6502, uptime 0:03:17 -``` \ No newline at end of file +``` + +## Verifying Traffic Capture and Forwarding + From 618f003a596e08b84b4712346b19cae69393f584 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 11 Apr 2023 13:41:46 -0600 Subject: [PATCH 122/239] documentation wip --- docs/malcolm-hedgehog-e2e-iso-install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md index 1d48fa9eb..fcbb72c2f 100644 --- a/docs/malcolm-hedgehog-e2e-iso-install.md +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -4,7 +4,7 @@ This document outlines how to install [Malcolm]({{ site.github.repository_url }} The Malcolm and Hedgehog Linux installers as described in these instructions are intended to be used to **replace** the existing operating system, if any, of the respective systems onto which they are installed, and, as such, are designed to require as little user input as possible. For this reason, there are NO user prompts and confirmations about partitioning and reformatting hard disks for use by the operating system. The installer assumes that all non-removable storage media (eg., SSD, HDD, NVMe, etc.) are available for use and ⛔🆘😭💀 ***will partition and format them without warning*** 💀😭🆘⛔. -In contrast to using the ISO installer, Malcolm can also be installed "natively" on any x86_64 platform that can run Docker. See the [installation example using Ubuntu 22.04 LTS](ubuntu-install-example.md#InstallationExample) for that method of installation and configuration. +In contrast to using the ISO installer, Malcolm can also be installed "natively" on any x86_64 platform that can run Docker. See the [installation example using Ubuntu 22.04 LTS](ubuntu-install-example.md#InstallationExample) for that method of installation and configuration, or [Windows host system configuration](host-config-windows.md#HostSystemConfigWindows) and [macOS host system configuration](host-config-macos.md#HostSystemConfigMac) for those platforms. ### Table of Contents From 0fa91fc4f6a8c8d4d565d12231c4c5bf0ae1a6a8 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 11 Apr 2023 13:54:44 -0600 Subject: [PATCH 123/239] documentation wip --- docs/hedgehog-config-root.md | 47 ------ docs/hedgehog-config-user.md | 189 ----------------------- docs/hedgehog-config-zeek-intel.md | 7 - docs/hedgehog-config.md | 17 -- docs/hedgehog-upgrade.md | 2 +- docs/hedgehog.md | 24 ++- docs/malcolm-hedgehog-e2e-iso-install.md | 19 ++- 7 files changed, 26 insertions(+), 279 deletions(-) delete mode 100644 docs/hedgehog-config-root.md delete mode 100644 docs/hedgehog-config-user.md delete mode 100644 docs/hedgehog-config-zeek-intel.md delete mode 100644 docs/hedgehog-config.md diff --git a/docs/hedgehog-config-root.md b/docs/hedgehog-config-root.md deleted file mode 100644 index 327463d6e..000000000 --- a/docs/hedgehog-config-root.md +++ /dev/null @@ -1,47 +0,0 @@ -# Interfaces, hostname, and time synchronization - -## Hostname - -The first step of sensor configuration is to configure the network interfaces and sensor hostname. Clicking the **Configure Interfaces and Hostname** toolbar icon (or, if you are at a command line prompt, running `configure-interfaces`) will prompt you for the root password you created during installation, after which the configuration welcome screen is shown. Select **Continue** to proceed. - -You may next select whether to configure the network interfaces, hostname, or time synchronization. - -![Selection to configure network interfaces, hostname, or time synchronization](./images/hedgehog/images/root_config_mode.png) - -Selecting **Hostname**, you will be presented with a summary of the current sensor identification information, after which you may specify a new sensor hostname. This name will be used to tag all events forwarded from this sensor in the events' **host.name** field. - -![Specifying a new sensor hostname](./images/hedgehog/images/hostname_setting.png) - -## Interfaces - -Returning to the configuration mode selection, choose **Interface**. You will be prompted if you would like help identifying network interfaces. If you select **Yes**, you will be prompted to select a network interface, after which that interface's link LED will blink for 10 seconds to help you in its identification. This network interface identification aid will continue to prompt you to identify further network interfaces until you select **No**. - -You will be presented with a list of interfaces to configure as the sensor management interface. This is the interface the sensor itself will use to communicate with the network in order to, for example, forward captured logs to an aggregate server. In order to do so, the management interface must be assigned an IP address. This is generally **not** the interface used for capturing data. Select the interface to which you wish to assign an IP address. The interfaces are listed by name and MAC address and the associated link speed is also displayed if it can be determined. For interfaces without a connected network cable, generally a `-1` will be displayed instead of the interface speed. - -![Management interface selection](./images/hedgehog/images/select_iface.png) - -Depending on the configuration of your network, you may now specify how the management interface will be assigned an IP address. In order to communicate with an event aggregator over the management interface, either **static** or **dhcp** must be selected. - -![Interface address source](./images/hedgehog/images/iface_mode.png) - -If you select static, you will be prompted to enter the IP address, netmask, and gateway to assign to the management interface. - -![Static IP configuration](./images/hedgehog/images/iface_static.png) - -In either case, upon selecting **OK** the network interface will be brought down, configured, and brought back up, and the result of the operation will be displayed. You may choose **Quit** upon returning to the configuration tool's welcome screen. - -## Time synchronization - -Returning to the configuration mode selection, choose **Time Sync**. Here you can configure the sensor to keep its time synchronized with either an NTP server (using the NTP protocol) or a local [Malcolm]({{ site.github.repository_url }}) aggregator or another HTTP/HTTPS server. On the next dialog, choose the time synchronization method you wish to configure. - -![Time synchronization method](./images/hedgehog/images/time_sync_mode.png) - -If **htpdate** is selected, you will be prompted to enter the IP address or hostname and port of an HTTP/HTTPS server (for a Malcolm instance, port `9200` may be used) and the time synchronization check frequency in minutes. A test connection will be made to determine if the time can be retrieved from the server. - -![*htpdate* configuration](./images/hedgehog/images/htpdate_setup.png) - -If *ntpdate* is selected, you will be prompted to enter the IP address or hostname of the NTP server. - -![NTP configuration](./images/hedgehog/images/ntp_host.png) - -Upon configuring time synchronization, a "Time synchronization configured successfully!" message will be displayed, after which you will be returned to the welcome screen. \ No newline at end of file diff --git a/docs/hedgehog-config-user.md b/docs/hedgehog-config-user.md deleted file mode 100644 index 5bbaea362..000000000 --- a/docs/hedgehog-config-user.md +++ /dev/null @@ -1,189 +0,0 @@ -# Capture, forwarding, and autostart services - -Clicking the **Configure Capture and Forwarding** toolbar icon (or, if you are at a command prompt, running `configure-capture`) will launch the configuration tool for capture and forwarding. The root password is not required as it was for the interface and hostname configuration, as sensor services are run under the non-privileged sensor account. Select **Continue** to proceed. You may select from a list of configuration options. - -![Select configuration mode](./images/hedgehog/images/capture_config_main.png) - -## Capture - -Choose **Configure Capture** to configure parameters related to traffic capture and local analysis. You will be prompted if you would like help identifying network interfaces. If you select **Yes**, you will be prompted to select a network interface, after which that interface's link LED will blink for 10 seconds to help you in its identification. This network interface identification aid will continue to prompt you to identify further network interfaces until you select **No**. - -You will be presented with a list of network interfaces and prompted to select one or more capture interfaces. An interface used to capture traffic is generally a different interface than the one selected previously as the management interface, and each capture interface should be connected to a network tap or span port for traffic monitoring. Capture interfaces are usually not assigned an IP address as they are only used to passively “listen” to the traffic on the wire. The interfaces are listed by name and MAC address and the associated link speed is also displayed if it can be determined. For interfaces without a connected network cable, generally a `-1` will be displayed instead of the interface speed. - -![Select capture interfaces](./images/hedgehog/images/capture_iface_select.png) - -Upon choosing the capture interfaces and selecting OK, you may optionally provide a capture filter. This filter will be used to limit what traffic the PCAP service ([`tcpdump`](https://www.tcpdump.org/)) and the traffic analysis services ([`zeek`](https://www.zeek.org/) and [`suricata`](https://suricata.io/)) will see. Capture filters are specified using [Berkeley Packet Filter (BPF)](http://biot.com/capstats/bpf.html) syntax. Clicking **OK** will attempt to validate the capture filter, if specified, and will present a warning if the filter is invalid. - -![Specify capture filters](./images/hedgehog/images/capture_filter.png) - -Next you must specify the paths where captured PCAP files and logs will be stored locally on the sensor. If the installation worked as expected, these paths should be prepopulated to reflect paths on the volumes formatted at install time for the purpose storing these artifacts. Usually these paths will exist on separate storage volumes. Enabling the PCAP and log pruning autostart services (see the section on autostart services below) will enable monitoring of these paths to ensure that their contents do not consume more than 90% of their respective volumes' space. Choose **OK** to continue. - -![Specify capture paths](./images/hedgehog/images/capture_paths.png) - -### Automatic file extraction and scanning - -Hedgehog Linux can leverage Zeek's knowledge of network protocols to automatically detect file transfers and extract those files from network traffic as Zeek sees them. - -To specify which files should be extracted, specify the Zeek file carving mode: - -![Zeek file carving mode](./images/hedgehog/images/zeek_file_carve_mode.png) - -If you're not sure what to choose, either of **mapped (except common plain text files)** (if you want to carve and scan almost all files) or **interesting** (if you only want to carve and scan files with [mime types of common attack vectors]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/sensor-iso/interface/sensor_ctl/zeek/extractor_override.interesting.zeek)) is probably a good choice. - -Next, specify which carved files to preserve (saved on the sensor under `/capture/bro/capture/extract_files/quarantine` by default). In order to not consume all of the sensor's available storage space, the oldest preserved files will be pruned along with the oldest Zeek logs as described below with **AUTOSTART_PRUNE_ZEEK** in the [autostart services](#HedgehogConfigAutostart) section. - -You'll be prompted to specify which engine(s) to use to analyze extracted files. Extracted files can be examined through any of three methods: - -![File scanners](./images/hedgehog/images/zeek_file_carve_scanners.png) - -* scanning files with [**ClamAV**](https://www.clamav.net/); to enable this method, select **ZEEK_FILE_SCAN_CLAMAV** when specifying scanners for Zeek-carved files -* submitting file hashes to [**VirusTotal**](https://www.virustotal.com/en/#search); to enable this method, select **ZEEK_FILE_SCAN_VTOT** when specifying scanners for Zeek-carved files, then manually edit `/opt/sensor/sensor_ctl/control_vars.conf` and specify your [VirusTotal API key](https://developers.virustotal.com/reference) in `VTOT_API2_KEY` -* scanning files with [**Yara**](https://github.com/VirusTotal/yara); to enable this method, select **ZEEK_FILE_SCAN_YARA** when specifying scanners for Zeek-carved files -* scanning portable executable (PE) files with [**Capa**](https://github.com/fireeye/capa); to enable this method, select **ZEEK_FILE_SCAN_CAPA** when specifying scanners for Zeek-carved files - -Files which are flagged as potentially malicious will be logged as Zeek `signatures.log` entries, and can be viewed in the **Signatures** dashboard in [OpenSearch Dashboards]({{ site.github.repository_url }}#DashboardsVisualizations) when forwarded to Malcolm. - -![File quarantine](./images/hedgehog/images/file_quarantine.png) - -Finally, you will be presented with the list of configuration variables that will be used for capture, including the values which you have configured up to this point in this section. Upon choosing **OK** these values will be written back out to the sensor configuration file located at `/opt/sensor/sensor_ctl/control_vars.conf`. It is not recommended that you edit this file manually. After confirming these values, you will be presented with a confirmation that these settings have been written to the configuration file, and you will be returned to the welcome screen. - -## Forwarding - -Select **Configure Forwarding** to set up forwarding logs and statistics from the sensor to an aggregator server, such as [Malcolm]({{ site.github.repository_url }}). - -![Configure forwarders](./images/hedgehog/images/forwarder_config.png) - -There are three forwarder services used on the sensor, each for forwarding a different type of log or sensor metric. - -## arkime-capture: Arkime session forwarding - -arkime-[capture](https://github.com/arkime/arkime/tree/master/capture) is not only used to capture PCAP files, but also the parse raw traffic into sessions and forward this session metadata to an [OpenSearch](https://opensearch.org/) database so that it can be viewed in [Arkime viewer](https://arkime.com/), whether standalone or as part of a [Malcolm]({{ site.github.repository_url }}) instance. If you're using Hedgehog Linux with Malcolm, please read [Correlating Zeek logs and Arkime sessions]({{ site.github.repository_url }}#ZeekArkimeFlowCorrelation) in the Malcolm documentation for more information. - -First, select the OpenSearch connection transport protocol, either **HTTPS** or **HTTP**. If the metrics are being forwarded to Malcolm, select **HTTPS** to encrypt messages from the sensor to the aggregator using TLS v1.2 using ECDHE-RSA-AES128-GCM-SHA256. If **HTTPS** is chosen, you must choose whether to enable SSL certificate verification. If you are using a self-signed certificate (such as the one automatically created during [Malcolm's configuration]({{ site.github.repository_url }}#configure-authentication)), choose **None**. - -![OpenSearch connection protocol](./images/hedgehog/images/opensearch_connection_protocol.png) ![OpenSearch SSL verification](./images/hedgehog/images/opensearch_ssl_verification.png) - -Next, enter the **OpenSearch host** IP address (ie., the IP address of the aggregator) and port. These metrics are written to an OpenSearch database using a RESTful API, usually using port 9200. Depending on your network configuration, you may need to open this port in your firewall to allow this connection from the sensor to the aggregator. - -![OpenSearch host and port](./images/hedgehog/images/arkime-capture-ip-port.png) - -You will be asked to enter authentication credentials for the sensor's connections to the aggregator's OpenSearch API. After you've entered the username and the password, the sensor will attempt a test connection to OpenSearch using the connection information provided. If the Malcolm services have not yet been started, you may receive a **Connection refused** error. You may select **Ignore Error** for the credentials to be accepted anyway. - -![OpenSearch username](./images/hedgehog/images/opensearch_username.png) ![OpenSearch password](./images/hedgehog/images/opensearch_password.png) ![Successful OpenSearch connection](./images/hedgehog/images/opensearch_connection_success.png) - -Finally, you will be shown a dialog for a list of IP addresses used to populate an access control list (ACL) for hosts allowed to connect back to the sensor for retrieving session payloads from its PCAP files for display in Arkime viewer. The list will be prepopulated with the IP address entered a few screens prior to this one. - -![PCAP retrieval ACL](./images/hedgehog/images/malcolm_arkime_reachback_acl.png) - -Finally, you'll be given the opportunity to review the all of the Arkime `capture` options you've specified. Selecting **OK** will cause the parameters to be saved and you will be returned to the configuration tool's welcome screen. - -![capture settings confirmation](./images/hedgehog/images/arkime_confirm.png) - -## filebeat: Zeek and Suricata log forwarding - -[Filebeat](https://www.elastic.co/products/beats/filebeat) is used to forward [Zeek](https://www.zeek.org/) and [Suricata](https://suricata.io/) logs to a remote [Logstash](https://www.elastic.co/products/logstash) instance for further enrichment prior to insertion into an [OpenSearch](https://opensearch.org/) database. - -To configure filebeat, first provide the log path (the same path previously configured for log file generation). - -![Configure filebeat for log forwarding](./images/hedgehog/images/filebeat_log_path.png) - -You must also provide the IP address of the Logstash instance to which the logs are to be forwarded, and the port on which Logstash is listening. These logs are forwarded using the Beats protocol, generally over port 5044. Depending on your network configuration, you may need to open this port in your firewall to allow this connection from the sensor to the aggregator. - -![Configure filebeat for log forwrading](./images/hedgehog/images/filebeat_ip_port.png) - -Next you are asked whether the connection used for log forwarding should be done **unencrypted** or over **SSL**. Unencrypted communication requires less processing overhead and is simpler to configure, but the contents of the logs may be visible to anyone who is able to intercept that traffic. - -![Filebeat SSL certificate verification](./images/hedgehog/images/filebeat_ssl.png) - -If **SSL** is chosen, you must choose whether to enable [SSL certificate verification](https://www.elastic.co/guide/en/beats/filebeat/current/configuring-ssl-logstash.html). If you are using a self-signed certificate (such as the one automatically created during [Malcolm's configuration]({{ site.github.repository_url }}#configure-authentication), choose **None**. - -![Unencrypted vs. SSL encryption for log forwarding](./images/hedgehog/images/filebeat_ssl_verify.png) - -The last step for SSL-encrypted log forwarding is to specify the SSL certificate authority, certificate, and key files. These files must match those used by the Logstash instance receiving the logs on the aggregator. If Malcolm's `auth_setup` script was used to generate these files they would be found in the `filebeat/certs/` subdirectory of the Malcolm installation and must be manually copied to the sensor (stored under `/opt/sensor/sensor_ctl/logstash-client-certificates` or in any other path accessible to the sensor account). Specify the location of the certificate authorities file (eg., `ca.crt`), the certificate file (eg., `client.crt`), and the key file (eg., `client.key`). - -![SSL certificate files](./images/hedgehog/images/filebeat_certs.png) - -The Logstash instance receiving the events must be similarly configured with matching SSL certificate and key files. Under Malcolm, the `BEATS_SSL` variable must be set to `true` in Malcolm's `docker-compose.yml` file and the SSL files must exist in the `logstash/certs/` subdirectory of the Malcolm installation. - -Once you have specified all of the filebeat parameters, you will be presented with a summary of the settings related to the forwarding of these logs. Selecting **OK** will cause the parameters to be written to filebeat's configuration keystore under `/opt/sensor/sensor_ctl/logstash-client-certificates` and you will be returned to the configuration tool's welcome screen. - -![Confirm filebeat settings](./images/hedgehog/images/filebeat_confirm.png) - -## miscbeat: System metrics forwarding - -The sensor uses [Fluent Bit](https://fluentbit.io/) to gather miscellaneous system resource metrics (CPU, network I/O, disk I/O, memory utilization, temperature, etc.) and the [Beats](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-tcp.html) protocol to forward these metrics to a remote [Logstash](https://www.elastic.co/products/logstash) instance for further enrichment prior to insertion into an [OpenSearch](https://opensearch.org/) database. Metrics categories can be enabled/disabled as described in the [autostart services](#HedgehogConfigAutostart) section of this document. - -This forwarder's configuration is almost identical to that of [filebeat](#Hedgehogfilebeat) in the previous section. Select `miscbeat` from the forwarding configuration mode options and follow the same steps outlined above to set up this forwarder. - -## Autostart services - -Once the forwarders have been configured, the final step is to **Configure Autostart Services**. Choose this option from the configuration mode menu after the welcome screen of the sensor configuration tool. - -Despite configuring capture and/or forwarder services as described in previous sections, only services enabled in the autostart configuration will run when the sensor starts up. The available autostart processes are as follows (recommended services are in **bold text**): - -* **AUTOSTART_ARKIME** - [capture](#Hedgehogarkime-capture) PCAP engine for traffic capture, as well as traffic parsing and metadata insertion into OpenSearch for viewing in [Arkime](https://arkime.com/). If you are using Hedgehog Linux along with [Malcolm]({{ site.github.repository_url }}) or another Arkime installation, this is probably the packet capture engine you want to use. -* **AUTOSTART_CLAMAV_UPDATES** - Virus database update service for ClamAV (requires sensor to be connected to the internet) -* **AUTOSTART_FILEBEAT** - [filebeat](#Hedgehogfilebeat) Zeek and Suricata log forwarder -* **AUTOSTART_FLUENTBIT_AIDE** - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/exec) [AIDE](https://aide.github.io/) file system integrity checks -* **AUTOSTART_FLUENTBIT_AUDITLOG** - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/tail) [auditd](https://man7.org/linux/man-pages/man8/auditd.8.html) logs -* *AUTOSTART_FLUENTBIT_KMSG* - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/kernel-logs) the Linux kernel log buffer (these are generally reflected in syslog as well, which may make this agent redundant) -* **AUTOSTART_FLUENTBIT_METRICS** - [Fluent Bit](https://fluentbit.io/) agent for collecting [various](https://docs.fluentbit.io/manual/pipeline/inputs) system resource and performance metrics -* **AUTOSTART_FLUENTBIT_SYSLOG** - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/syslog) Linux syslog messages -* **AUTOSTART_FLUENTBIT_THERMAL** - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/thermal) system temperatures -* **AUTOSTART_MISCBEAT** - [filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-tcp.html) forwarder which sends system metrics collected by [Fluent Bit](https://fluentbit.io/) to a remote Logstash instance (e.g., [Malcolm]({{ site.github.repository_url }})'s) -* *AUTOSTART_NETSNIFF* - [netsniff-ng](http://netsniff-ng.org/) PCAP engine for saving packet capture (PCAP) files -* **AUTOSTART_PRUNE_PCAP** - storage space monitor to ensure that PCAP files do not consume more than 90% of the total size of the storage volume to which PCAP files are written -* **AUTOSTART_PRUNE_ZEEK** - storage space monitor to ensure that Zeek logs do not consume more than 90% of the total size of the storage volume to which Zeek logs are written -* **AUTOSTART_SURICATA** - [Suricata](https://suricata.io/) traffic analysis engine -* **AUTOSTART_SURICATA_UPDATES** - Rule update service for Suricata (requires sensor to be connected to the internet) -* *AUTOSTART_TCPDUMP* - [tcpdump](https://www.tcpdump.org/) PCAP engine for saving packet capture (PCAP) files -* **AUTOSTART_ZEEK** - [Zeek](https://www.zeek.org/) traffic analysis engine - -Note that only one packet capture engine ([capture](https://arkime.com/), [netsniff-ng](http://netsniff-ng.org/), or [tcpdump](https://www.tcpdump.org/)) can be used. - -![Autostart services](./images/hedgehog/images/autostarts.png) - -Once you have selected the autostart services, you will be prompted to confirm your selections. Doing so will cause these values to be written back out to the `/opt/sensor/sensor_ctl/control_vars.conf` configuration file. - -![Autostart services confirmation](./images/hedgehog/images/autostarts_confirm.png) - -After you have completed configuring the sensor it is recommended that you reboot the sensor to ensure all new settings take effect. If rebooting is not an option, you may click the **Restart Sensor Services** menu icon in the top menu bar, or open a terminal and run: - -``` -/opt/sensor/sensor_ctl/shutdown && sleep 10 && /opt/sensor/sensor_ctl/supervisor.sh -``` - -This will cause the sensor services controller to stop, wait a few seconds, and restart. You can check the status of the sensor's processes by choosing **Sensor Status** from the sensor's kiosk mode, clicking the **Sensor Service Status** toolbar icon, or running `/opt/sensor/sensor_ctl/status` from the command line: - -``` -$ /opt/sensor/sensor_ctl/status -arkime:arkime-capture RUNNING pid 6455, uptime 0:03:17 -arkime:arkime-viewer RUNNING pid 6456, uptime 0:03:17 -beats:filebeat RUNNING pid 6457, uptime 0:03:17 -beats:miscbeat RUNNING pid 6458, uptime 0:03:17 -clamav:clamav-service RUNNING pid 6459, uptime 0:03:17 -clamav:clamav-updates RUNNING pid 6461, uptime 0:03:17 -fluentbit-auditlog RUNNING pid 6463, uptime 0:03:17 -fluentbit-kmsg STOPPED Not started -fluentbit-metrics:cpu RUNNING pid 6466, uptime 0:03:17 -fluentbit-metrics:df RUNNING pid 6471, uptime 0:03:17 -fluentbit-metrics:disk RUNNING pid 6468, uptime 0:03:17 -fluentbit-metrics:mem RUNNING pid 6472, uptime 0:03:17 -fluentbit-metrics:mem_p RUNNING pid 6473, uptime 0:03:17 -fluentbit-metrics:netif RUNNING pid 6474, uptime 0:03:17 -fluentbit-syslog RUNNING pid 6478, uptime 0:03:17 -fluentbit-thermal RUNNING pid 6480, uptime 0:03:17 -netsniff:netsniff-enp1s0 STOPPED Not started -prune:prune-pcap RUNNING pid 6484, uptime 0:03:17 -prune:prune-zeek RUNNING pid 6486, uptime 0:03:17 -supercronic RUNNING pid 6490, uptime 0:03:17 -suricata RUNNING pid 6501, uptime 0:03:17 -tcpdump:tcpdump-enp1s0 STOPPED Not started -zeek:capa RUNNING pid 6553, uptime 0:03:17 -zeek:clamav RUNNING pid 6512, uptime 0:03:17 -zeek:logger RUNNING pid 6554, uptime 0:03:17 -zeek:virustotal STOPPED Not started -zeek:watcher RUNNING pid 6510, uptime 0:03:17 -zeek:yara RUNNING pid 6548, uptime 0:03:17 -zeek:zeekctl RUNNING pid 6502, uptime 0:03:17 -``` \ No newline at end of file diff --git a/docs/hedgehog-config-zeek-intel.md b/docs/hedgehog-config-zeek-intel.md deleted file mode 100644 index cef702d53..000000000 --- a/docs/hedgehog-config-zeek-intel.md +++ /dev/null @@ -1,7 +0,0 @@ -# Zeek Intelligence Framework - -To quote Zeek's [Intelligence Framework](https://docs.zeek.org/en/master/frameworks/intel.html) documentation, "The goals of Zeek’s Intelligence Framework are to consume intelligence data, make it available for matching, and provide infrastructure to improve performance and memory utilization. Data in the Intelligence Framework is an atomic piece of intelligence such as an IP address or an e-mail address. This atomic data will be packed with metadata such as a freeform source field, a freeform descriptive field, and a URL which might lead to more information about the specific item." Zeek [intelligence](https://docs.zeek.org/en/master/scripts/base/frameworks/intel/main.zeek.html) [indicator types](https://docs.zeek.org/en/master/scripts/base/frameworks/intel/main.zeek.html#type-Intel::Type) include IP addresses, URLs, file names, hashes, email addresses, and more. - -Hedgehog Linux doesn't come bundled with intelligence files from any particular feed, but they can be easily included into your local instance. Before Zeek starts, Hedgehog Linux configures it such that intelligence files will be automatically included in its local policy. Subdirectories under `/opt/sensor/sensor_ctl/zeek/intel` which contain their own `__load__.zeek` file will be `@load`-ed as-is, while subdirectories containing "loose" intelligence files will be [loaded](https://docs.zeek.org/en/master/frameworks/intel.html#loading-intelligence) automatically with a `redef Intel::read_files` directive. - -Note that Hedgehog Linux does not manage updates for these intelligence files. You should use the update mechanism suggested by your feeds' maintainers to keep them up to date. Adding and deleting intelligence files under this directory will take effect upon restarting Zeek. \ No newline at end of file diff --git a/docs/hedgehog-config.md b/docs/hedgehog-config.md deleted file mode 100644 index 9fcb2fb4e..000000000 --- a/docs/hedgehog-config.md +++ /dev/null @@ -1,17 +0,0 @@ -# Configuration - -Kiosk mode can be exited by connecting an external USB keyboard and pressing **Alt+F4**, upon which the *sensor* user's desktop is shown. - -![Sensor login session desktop](./images/hedgehog/images/desktop.png) - -Several icons are available in the top menu bar: - -* **Terminal** - opens a command prompt in a terminal emulator -* **Browser** - opens a web browser -* **Kiosk** – returns the sensor to kiosk mode -* **README** – displays this document -* **Sensor status** – displays a list with the status of each sensor service -* **Configure capture and forwarding** – opens a dialog for configuring the sensor's capture and forwarding services, as well as specifying which services should autostart upon boot -* **Configure interfaces and hostname** – opens a dialog for configuring the sensor's network interfaces and setting the sensor's hostname -* **Restart sensor services** - stops and restarts all of the [autostart services](hedgehog-config-user.md#HedgehogConfigAutostart) - diff --git a/docs/hedgehog-upgrade.md b/docs/hedgehog-upgrade.md index 512b9af33..6c7e69e29 100644 --- a/docs/hedgehog-upgrade.md +++ b/docs/hedgehog-upgrade.md @@ -315,7 +315,7 @@ sensor@hedgehog:opt$ for BEAT in filebeat miscbeat; do cp /opt/sensor_upgrade_ba sensor@hedgehog:opt$ cp /opt/sensor_upgrade_backup_2020-05-07/sensor_ctl/filebeat/{ca.crt,client.crt,client.key} /opt/sensor/sensor_ctl/logstash-client-certificates/ ``` -24. Despite what we just did, you may consider running `capture-config` to re-configure [capture, forwarding, and autostart services](hedgehog-config-user.md#HedgehogConfigUser) from scratch anyway. You can use the backed-up version of `control_vars.conf` to refer back to as a basis for things you might want to restore (e.g., `CAPTURE_INTERFACE`, `CAPTURE_FILTER`, `PCAP_PATH`, `ZEEK_LOG_PATH`, your autostart settings, etc.). +24. Despite what we just did, you may consider running `capture-config` to re-configure [capture, forwarding, and autostart services](malcolm-hedgehog-e2e-iso-install.md#HedgehogInstallAndConfig) from scratch anyway. You can use the backed-up version of `control_vars.conf` to refer back to as a basis for things you might want to restore (e.g., `CAPTURE_INTERFACE`, `CAPTURE_FILTER`, `PCAP_PATH`, `ZEEK_LOG_PATH`, your autostart settings, etc.). 25. Once you feel confident you've completed all of these steps, issue a reboot on the Hedgehog diff --git a/docs/hedgehog.md b/docs/hedgehog.md index 8464427f6..a9d95ba9e 100644 --- a/docs/hedgehog.md +++ b/docs/hedgehog.md @@ -19,19 +19,17 @@ Hedgehog Linux is a Debian-based operating system built to - [Installer](hedgehog-installation.md#HedgehogInstaller) * [Boot](hedgehog-boot.md#HedgehogBoot) - [Kiosk mode](hedgehog-boot.md#HedgehogKioskMode) -* [Configuration](hedgehog-config.md#HedgehogConfiguration) - - [Interfaces, hostname, and time synchronization](hedgehog-config-root.md#HedgehogConfigRoot) - + [Hostname](hedgehog-config-root.md#HedgehogConfigHostname) - + [Interfaces](hedgehog-config-root.md#HedgehogConfigIface) - + [Time synchronization](hedgehog-config-root.md#HedgehogConfigTime) - - [Capture, forwarding, and autostart services](hedgehog-config-user.md#HedgehogConfigUser) - + [Capture](hedgehog-config-user.md#HedgehogConfigCapture) - * [Automatic file extraction and scanning](hedgehog-config-user.md#HedgehogZeekFileExtraction) - + [Forwarding](hedgehog-config-user.md#HedgehogConfigForwarding) - * [arkime-capture](hedgehog-config-user.md#Hedgehogarkime-capture): Arkime session forwarding - * [filebeat](hedgehog-config-user.md#Hedgehogfilebeat): Zeek and Suricata log forwarding - * [miscbeat](hedgehog-config-user.md#Hedgehogmiscbeat): System metrics forwarding - + [Autostart services](hedgehog-config-user.md#HedgehogConfigAutostart) +* [Configuration](malcolm-hedgehog-e2e-iso-install.md#HedgehogInstallAndConfig) + - [Configure Hostname, Interfaces and Time Sync](malcolm-hedgehog-e2e-iso-install.md#HedgehogInterfaces) + - [Configure Capture](malcolm-hedgehog-e2e-iso-install.md#HedgehogCapture) + + [Capture](malcolm-hedgehog-e2e-iso-install.md#HedgehogConfigCapture) + + [File extraction and scanning](malcolm-hedgehog-e2e-iso-install.md#HedgehogZeekFileExtraction) + - [Configure Forwarding](malcolm-hedgehog-e2e-iso-install.md#HedgehogConfigForwarding) + * [arkime-capture](malcolm-hedgehog-e2e-iso-install.md#Hedgehogarkime-capture): Arkime session forwarding + * [ssl-client-receive](malcolm-hedgehog-e2e-iso-install.md#HedgehogGetCerts): Receive client SSL files for filebeat from Malcolm + * [filebeat](malcolm-hedgehog-e2e-iso-install.md#Hedgehogfilebeat): Zeek and Suricata log forwarding + * [miscbeat](malcolm-hedgehog-e2e-iso-install.md#Hedgehogmiscbeat): System metrics forwarding + - [Autostart services](malcolm-hedgehog-e2e-iso-install.md#HedgehogConfigAutostart) + [Zeek Intelligence Framework](hedgehog-config-zeek-intel.md#HedgehogZeekIntel) * [Appendix A - Generating the ISO](hedgehog-iso-build.md#HedgehogISOBuild) * [Appendix B - Configuring SSH access](hedgehog-ssh.md#HedgehogConfigSSH) diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md index fcbb72c2f..cad982000 100644 --- a/docs/malcolm-hedgehog-e2e-iso-install.md +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -242,7 +242,7 @@ The [configuration and tuning](malcolm-config.md#ConfigAndTuning) wizard's quest * Capture packets using netsniff-ng? - Answer **Y** for Malcolm to [capture network traffic](live-analysis.md#LocalPCAP) on the local network interface(s) indicated using [netsniff-ng](http://netsniff-ng.org/) (instead of tcpdump). These PCAP files are then periodically rotated into Arkime for analysis. netsniff-ng is Malcolm's preferred tool for capturing network traffic. * Capture packets using tcpdump? - - Answer **Y** for Malcolm to [capture network traffic](live-analysis.md#LocalPCAP) on the local network interface(s) indicated using [tcpdump](https://www.tcpdump.org/) (instead of netsniff-ng). + - Answer **Y** for Malcolm to [capture network traffic](live-analysis.md#LocalPCAP) on the local network interface(s) indicated using [tcpdump](https://www.tcpdump.org/) (instead of netsniff-ng). Do not answer **Y** for both `tcpdump` and `netsniff-ng`. * Should Arkime delete PCAP files based on available storage? - Answering **Y** allows Arkime to prune (delete) old PCAP files based on available disk space (see https://arkime.com/faq#pcap-deletion). * Should Malcolm analyze live network traffic with Suricata? @@ -320,7 +320,9 @@ At the end of the installation process, you will be prompted with a few self-exp * **Should the GUI session be locked due to inactivity?** * **Display the [Standard Mandatory DoD Notice and Consent Banner](https://www.stigviewer.com/stig/application_security_and_development/2018-12-24/finding/V-69349)?** *(only applies when installed on U.S. government information systems)* -Following these prompts, the installer will reboot and Hedgehog Linux will boot into [kiosk mode](hedgehog-boot.md#HedgehogKioskMode). To continue setup, press **Alt+F4** to exit kiosk mode to the Hedgehog Linux desktop environment. +Following these prompts, the installer will reboot and Hedgehog Linux will boot into [kiosk mode](hedgehog-boot.md#HedgehogKioskMode). + +Kiosk mode can be exited by connecting an external USB keyboard and pressing **Alt+F4**, upon which the *sensor* user's desktop is shown. ### Desktop Environment @@ -334,9 +336,16 @@ The panel bordering the top of the Malcolm desktop is home to a number of useful *The Hedgehog Linux desktop* -## Configure Hostname, Interfaces and Time Sync +* **Terminal** - opens a command prompt in a terminal emulator +* **Browser** - opens a web browser +* **Kiosk** – returns the sensor to kiosk mode +* **README** – displays this document +* **Sensor status** – displays a list with the status of each sensor service +* **Configure capture and forwarding** – opens a dialog for configuring the sensor's capture and forwarding services, as well as specifying which services should autostart upon boot +* **Configure interfaces and hostname** – opens a dialog for configuring the sensor's network interfaces and setting the sensor's hostname +* **Restart sensor services** - stops and restarts all of the [autostart services](#HedgehogConfigAutostart) -https://malcolm.fyi/docs/hedgehog-config-root.html#HedgehogConfigRoot +## Configure Hostname, Interfaces and Time Sync The first step of sensor configuration is to configure the network interfaces and sensor hostname. Clicking the **Configure Interfaces and Hostname** toolbar icon (or, if you are at a command line prompt, running `configure-interfaces`) will prompt you for the root password you created during installation, after which the configuration welcome screen is shown. Select **Continue** to proceed. @@ -392,7 +401,7 @@ You will be presented with a list of network interfaces and prompted to select o ![Select capture interfaces](./images/hedgehog/images/capture_iface_select.png) -Upon choosing the capture interfaces and selecting OK, you may optionally provide a capture filter. This filter will be used to limit what traffic the PCAP service ([`tcpdump`](https://www.tcpdump.org/)) and the traffic analysis services ([`zeek`](https://www.zeek.org/) and [`suricata`](https://suricata.io/)) will see. Capture filters are specified using [Berkeley Packet Filter (BPF)](http://biot.com/capstats/bpf.html) syntax. For example, to indicate that Hedgehog should ignore the ports it uses to communicate with Malcolm, you could specify `not port 5044 and not port 5045 and not port 8005 and not port 9200`. Clicking **OK** will attempt to validate the capture filter, if specified, and will present a warning if the filter is invalid. +Upon choosing the capture interfaces and selecting OK, you may optionally provide a capture filter. This filter will be used to limit what traffic the PCAP service ([netsniff-ng](http://netsniff-ng.org/) or [tcpdump](https://www.tcpdump.org/)) and the traffic analysis services ([`zeek`](https://www.zeek.org/) and [`suricata`](https://suricata.io/)) will see. Capture filters are specified using [Berkeley Packet Filter (BPF)](http://biot.com/capstats/bpf.html) syntax. For example, to indicate that Hedgehog should ignore the ports it uses to communicate with Malcolm, you could specify `not port 5044 and not port 5045 and not port 8005 and not port 9200`. Clicking **OK** will attempt to validate the capture filter, if specified, and will present a warning if the filter is invalid. ![Specify capture filters](./images/hedgehog/images/capture_filter.png) From e6fb1bab7fce162a256a7d972028c81b15ef2df5 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 11 Apr 2023 14:00:51 -0600 Subject: [PATCH 124/239] documentation wip --- docs/quickstart.md | 6 ++++-- docs/ubuntu-install-example.md | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/quickstart.md b/docs/quickstart.md index ec05eafd7..a3761eccc 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -6,9 +6,9 @@ ## Getting Malcolm -For a `TL;DR` example of downloading, configuring, and running Malcolm on a Linux platform, see [Installation example using Ubuntu 22.04 LTS](ubuntu-install-example.md#InstallationExample). +For a `TL;DR` example of downloading, configuring, and running Malcolm in Docker on a Linux platform, see **[Installation example using Ubuntu 22.04 LTS](ubuntu-install-example.md#InstallationExample)**. -The scripts to control Malcolm require Python 3. The [`install.py`](malcolm-config.md#ConfigAndTuning) script requires the [requests](https://docs.python-requests.org/en/latest/) module for Python 3, and will make use of the [pythondialog](https://pythondialog.sourceforge.io/) module for user interaction (on Linux) if it is available. +For a more in-depth guide convering installing both Malcolm and a [Hedgehog Linux](hedgehog.md) sensor using the [Malcolm installer ISO](malcolm-iso.md#ISO) and [Hedgehog Linux installer ISO](hedgehog-installation.md#HedgehogInstallation), see **[End-to-end Malcolm and Hedgehog Linux ISO Installation](malcolm-hedgehog-e2e-iso-install.md#InstallationExample)**. ### Source code @@ -20,6 +20,8 @@ The `build.sh` script can build Malcolm's Docker images from scratch. See [Build ### Initial configuration +The scripts to control Malcolm require Python 3. The [`install.py`](malcolm-config.md#ConfigAndTuning) script requires the [requests](https://docs.python-requests.org/en/latest/) module for Python 3, and will make use of the [pythondialog](https://pythondialog.sourceforge.io/) module for user interaction (on Linux) if it is available. + You must run [`auth_setup`](authsetup.md#AuthSetup) prior to pulling Malcolm's Docker images. You should also ensure your system configuration and `docker-compose.yml` settings are tuned by running `./scripts/install.py` or `./scripts/install.py --configure` (see [System configuration and tuning](malcolm-config.md#ConfigAndTuning)). ### Pull Malcolm's Docker images diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index 90b083060..2bd6ea61e 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -2,6 +2,8 @@ Here's a step-by-step example of getting [Malcolm from GitHub]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}), configuring your system and your Malcolm instance, and running it on a system running Ubuntu Linux. Your mileage may vary depending on your individual system configuration, but this should be a good starting point. +For a more in-depth guide convering installing both Malcolm and a [Hedgehog Linux](hedgehog.md) sensor using the [Malcolm installer ISO](malcolm-iso.md#ISO) and [Hedgehog Linux installer ISO](hedgehog-installation.md#HedgehogInstallation), see **[End-to-end Malcolm and Hedgehog Linux ISO Installation](malcolm-hedgehog-e2e-iso-install.md#InstallationExample)**. + The commands in this example should be executed as a non-root user. You can use `git` to clone Malcolm into a local working copy, or you can download and extract the artifacts from the [latest release]({{ site.github.repository_url }}/releases). From 4b9bba0423328ea645a0b60645936eb699e80bf8 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 11 Apr 2023 14:16:27 -0600 Subject: [PATCH 125/239] documentation wip --- .../arkime_sessions_node_filter.png | Bin 0 -> 307008 bytes docs/malcolm-hedgehog-e2e-iso-install.md | 19 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 docs/images/screenshots/arkime_sessions_node_filter.png diff --git a/docs/images/screenshots/arkime_sessions_node_filter.png b/docs/images/screenshots/arkime_sessions_node_filter.png new file mode 100644 index 0000000000000000000000000000000000000000..97a327d9708c2520d7134a2122a53b3faf280996 GIT binary patch literal 307008 zcmZsBb97}*^ktlMY}@I$V_O~DPRF*j4GIEqXOePR1qg^h&|OK*Q9<8@(AM6@ z*v#@5p`)AaFT!80X2w83uB#R4>T$R&_99<3(6qquhHCJlOc(rPOD)5ewGxe45>vv? zbz$nMNKgcq+dvyHRbTCwe9y`b^mLk51{y4h7dM^8s@PE*mUK5iQm zCv$Ifw{e|s?-R*)FW3-OYf}jG?_OS?udQWgchZ(yFkU|0fm>DiTxV}rZ>vv&RNguW zTU~9RAM3+D9vN3(c3$*v9nV)jQ(zP}d+t=Nt_tE=eCd~Q_dY-Ut5W%dpxdcDyQ1IC zsNNmU*Q0HGV05tf@NbfrUv9j_URh8lldo__Z5UrW@z0xS~GIE^=O$E zB3)(MukM3l%Xo4M96)y9uDN9yM4R)dD8Tm6I*AAI4#u%KHEWw69wfF1P2P)c2yof@ zylfKOo8-El90nCTxQgDo%+`5#@&0O_?g+Djf;N81PjM1iU>=h@yr;M#Cfe~fG^g*p zYI=)Z%|jtfNg zjI0Ub$HjTUD(5c5rNDwRwu5vTk^ULDUtjETmx{gVkvA|k#t#!(t)xK4!UGqVSSQcN z!e(x&R(n2PuGV0@Au2(JL0vFU9D&WUpjDBiVNuo*m84<4__M5{VN*1k2UvemvZ}Ia z^9^&|(e|XL2=WTka4^+4i*>BLZi06x*HcBOakR9oqH{2c%WBmpS$o5}?aHnx9x~sdK+Kr9!{u#@*U8A z%BDK}-1rhZ%uslmSkHEQ3ogI7!6fSdD}g~%%%6(2R})~f_P7(tZ#r+0@9LXarOz@jV!!bhr>tfvcx)j=@zMJ%-g=Hm1U>M z?^URnb|Js?$FsHSV!W<}5E$WM66L5E6SGUzmF2f2P-sV02tcLRx8Bn&(JG<3Bp5Y`0c+XMpsi&;1 zot@t}(=T?K zvwd$3L_mGc(|!{Be%6ixHV1jUHsK%8=7GqjL+uh-DDr%D?W75&5y74j<>zLqtbOsHAIUb(A=UH%|I2;5eks>qN#7qO>hZsjrRPo zVr&iq(7wa-5I>0%fSn#RCs*6B^5uKSmyY5fup=cal@VMuX^MbmZJ=xNv9t*mqaV_m zr&EZs{4Rl#K_gq4NCo?pTd^}X^%XYAA9u0!j~FKD@SAD5h?d=s)l`Fg;i(^)Y!)S> zY4hs2;O5M7P+2d;AdCDPSIm)3KXuC39%4WQMeYvIqUw`4?GF%4h?e(#u$!{ zRxeZKtEX|4VV(8ON7UvhV_6fw#PK+wdcv97%Q$Bd4ljzM?>GQnG%~UNaKR5|nxU6^ zjs^AIqMudoT7*RG;FlGt|K=6s1{}S+S~8e56hy(1*f=<9(3jjQgSmae1Sy}#Dz8;$ zjksr;**$Wy6)l}K5KukFSYM=)#5zoaSj~Qe0@0dny`(a!EpQba*PZw^YBWDEMPq=cKv zfFbywIuq82t#46GNQZRcMjU;43Fw_THAJO=v=ZZ(!i25Fk)k3O4Y0&#V}9bj+R&jGh5G$);C)%114`U-Ukt7Qw zqBQQua$j$N$Vk#SayDR{UC!MmR>#T6 zKhvy^EXqBIStnAAuEE-AIB+UAs@M&jrU}U2@$Th65o(s-vl#}A7D9|1Wt$`mreUpTKr+!} z5i;P;d5bxL7D7+*AG#+{3eql1GNC(B9>SX$suvX+o1H6&Sfg+9(mjOuc+^C32tB9U zh!D?zz#oiyy~bbc$=Mf52O4)R>HMdjijb)X%^7GD;B5rj01kPr|nQ}8pb z+_?~|3^`sTJ0T|ICZtq#v5+{;r=b5?2*zs9_I;W~EbQrzO-Z3Bip+_CAgrwuU+`)q z7%NqcHtxU=c&o)GVK`0~5La8yu=j+nU*Px}-*RMPK;m2?CPqUbe;ED&saAjMlT03J zn=Mx`3jLmi_A%`6*qSY(aZ3pvXh#m)(5-&YWYtnZ@)@nUcsc$!l?+NGKCeh>q$GIQ z03pY5nZq_ckxg&LDQr-e(|}@|wLf#sBQ1Y+pZNXFrxl6iT^$R?@%5(zCGxzO)|QB8 za`#39R1(m8d;{dDOP7X3T7%^?B?NezS7+nP(d=g<1ODaFtj@2a>sX^#lwP){riG8z zW+O<@RnCTRn<1+C&+{>4%|=oV54kXDd?dwv)^ll}xG*R^dUwd_+)2owk`#8Wb*xw|_+x(r@Ntq(h@c`~l+&Ya{dmQ6 zmll)Av>0-J$K_^bB~F5@2?7lmYatW4cuXzs*<~eOBElx2mbQgLPzN_X54dPnmVNd*HwKg(g2%L)!UdOtRSulC542Xq+fed~ zq4Z+=ep$euqSZ=DOU7BRg`_#$k&+MzbBHcueqM=j2zX|u`WfwBjPA~;6V#sT>4C<; zv2EMuBxKLjh(tVjSqQGaD5-%Pm|>mEDui?VIGaPA(EHhqiAoe0E2?$NjB^fRd;%78 zsxVoE-JLN>A!<>k#Vwz3bp)pZ@tCIkEM)TA-WE#K%9CIzgd|kdw6KJHlfYDDF1cQg}n%&#h)x-!|LoZuPD0;pJ=gQ zFom#fzixz~h`(g|MlMNEFvJB@^*4M|?DTZOWVKMYuUCF;9YaupyKPvBTn_J=^78(S zwYa{7xJm6Yl3>OKRAcrA07Yv?M16KnmTcmp_&MI0hif!pBE& z{D{`Y{-Kb3IeIz&z8w3A%)*fhHbFQ5BaK1h?O5=oe! z@f}>7G`ePESS?1f)WAULH^Q`h8)#YGP(P~0D~+;0G@5igvEm#M(7R%_q()?a_@fw87JW)Z#vMahu- zIyP!noU`kaL*p>PU>=H9)o09f?U(f-t0#aJy?T`XCHLHs<$EmDSOCI4*SHC7b0vRW zhifg>hTotss#FQ?8nKS1ENF;bk>j>B%Ni6dh_HVviUmB8@>8uqk(W{*V`mC{)akkX zXa9U;D^E~L5%BS5R1;>s69^_Yoo#l7!6tU>uo<&`QE#)!e6Ln*#841)%EeZ7F&%&5r>BVT8MWJA}OG4AiUf!pJrv8W=9)5ycg z;zD{o{5V;&*z%IB)id~`g}KlZ5_ zD`_mG=-H*nuL{70tOdT?H<;)$?)+JyWTl!)g@eIPkx)cS%UEPQPuPr;f3_Z2{W{8)_f&ZldaiejsZmaX_#7&`eW{70 zJ`@*jwF=k|!n?sb3pz$60N!G!qGCWYDa^+Kjk}|0{pc>f)~nbDV=I2H4yU{&{BVfD zNDm#NeH%&!c@BPwjcH=AU~6$^IV~-iI9kf{X~XO+l6p(O(aL<8-XVw^@`1sY6z-~> zB~@4wcO7Q(B>4}hK`F>ni&wY20ILqC^~nQ!Y)Hx*^&S^vIwT%R8oh0O(6A$2ZnhpV zHjFtq3-YxBR5&zf_mjEDG0dm$NzE+Wvl@sGyG)D75&17KTLfqyYI5!`$7>r-K`EtW z(#q;nWfn0h+K{3NP{gunRY%;9vJ#$IhToXtkeF&*@$BZoT5BjD#W=}8F5F$ibtKu& zWJkZ315{NU2=*fqIkGZ@O8Wst=K3&RI9O&;GmxS79IOZExm(O|(ey`Ww}$!W$? z>!dXhWz@?n%0D4H95m0sxciuKHVF*X?cmA!cQaRG{mh9A$|JM!=L*dj$AIB54Lt=e zZj~#2ZYc30azy%mZU^s7i!w=}i!|g27rnsLNN6Uh>&>A%rvmCL$wU$^q&8IGv}u~m zY^@v`VRFy4i0!XsR1l1OAK8}9J**~Xw`wQrD$XS6X66{~kHCzSFIP~?auoB0mST4T zol_d4|4rIU??GIEXoaG8DUUp-7e)UP7wMb{sr|MyPQlSnmUGXzC_!@(S6xH zo6R81c9$rati^@8zryg41TPV1?+hYRVq%unj5ArLYip}8`ENb(B-9I;8X8=Uc;4JH zIQ(11_2`u8ddBhO*+9@tHhXXdT(bw_$QT^i>9^T{)+mY6tHo;Mnj$=Anxr}FRppmb zQOXD58zFcP$1uOvX{Ga(z#2O}UXRGr_WGL`W%FZ5>x|$!JCfZ{Zh_$fg3#mFb)b~} z2AIIzZd724BzXt9#P;g)KzNm5UNEBX=mw=$Dyw+Q{cX8+IhF1oqD0EDoHme}-vXT1 zql{`E_3ieaS$~!a?UzX}EnSD=&y^5Vc2z%9oNfa*2elCHWx<<}sXK*^2g_cWVW$JX z>G0LCi#jV2)v_O^&Z!8Ai(DTwXa1UXsE-BL#^{-{s?T52YVPsCh9~Drqjdm%LU3`V zQ8)z_v!(x_9iS$#)tp6N1jClY*;BdqM5zqkH8n!!H$R<*z_kX=xp6MK5|Em^5!maz zbdke$H_{HT%vBYn6UN#3Jq~|qxBQ5Lt|TA!mFU)<4NMS0_X^Qx|x2}wMy>y!n1W>yB$A~2x?ADw`IC&W-c}miuyIMz-jQr?}0CuGrL5B z*rAELtZ|Qs-x|4nBPe-`N|>k8VGR+i=Y!w_*!dg~i*dwqyu!$IYni!3J#t8zVZfj*XwV~L-Nr`7KL zB1eBctgXqF%bi4)8&f0&+r~4i^7YTcR=*jn3@tKHxVkrycR(rU65|a=S4F&X4v7nO zTNd}I!kSGEM46#AC zz5u6N6Md#(EFc_Yh#xKR&$S;{lp{2TUZ}sy9PVtr#I<?Ra0Fa;CI~jDWd@)T?5@VDDIG`rKldsml2w zFr!kI9kTM}zQfge^lSWPv4TEE&PMYO!dQDT#r!eb*)I~%5ZMroKhM$HIO!j2tXIe} zeVk1<|EbXwG`Xt(2KGl@`Rhziu@Fw;tfA=7N9)sZ;=)_FpLj(YWC_!07VkReL=3~= zW$7rRx^@96+NTF?2`|P6FG3+(Y7lgMQBr@`u;_Y!tg8d3Vgc>6&=g@yJtHfRGg+7@ z#Kunyf_hLr#8Ru*0kGE;6}LKa8y>(TUKxU4<2fBQ!6?yv#t_t@aHyaO1OB!=D#%&FIP_&5>5wLIo^u9vqG zCufUj*m+gk|=u zx22Zu$W0ReK;YeqhNC`1I`4x(>C=tscAI5gS>ZZR6XuAkWR~mWITGTgaw3W`7d4;% z8FWI-H-|(vS^ee`if!PE%FK$g5ZqA0nJ>X!3Oa)jL$ADZ9WXF z=!izvQ;+RTX{=#c5MEN@tGf=pQqlq-t9`i&JY92c*|wX&clsqy-{EZc%GmANs; zli6K_pj#=vx^0`~c-(Z2jCbK?Cat;aP?Yw7QzZ5ppcRv0U6)Yuk>Z0#;uB#qxkaRD zDuU-l3P!Ku1IM*V?n&pPz9RO_ZS72q2kRXbj>L+N&mOSLK*i(k&*HkkLpkj8`Gx9; z$;Uw;ts4B9c4WP_%=Em_cv(w_P{8I{Tt1ZA&yI&x;m_Ll*H1%Xm{X^9(B z!HIkOPI0E&%ooS}l|j4N6@r`NUUCW!S{L?$=hxCwiT7fpsK|CD zZ|_6yY4B&Cty#LsZzhNvRV%L;Q%_#4y1g=Y`b5Z2nU%+g4UYp1)Ck(Xzb1Ylou=Wg zOi_GTdI(Zb=ScqHVWd-lVPxH^JXV_NGB|RWN9A`<9HuMNr0M$DX0)dTEj7bVLKDgcuZ&<1}!LCx*g^%-Xtdyl+T~Zwli0=ym}E?hGexryW1s zy=hKd+99dipCD~Sel&v&ejow)MZL|QB^Tx)x>5J;LKi(Aj7L5u{ag2$EuPXkN_v!& zP7kVH@=1te=RA*Ul9n9^700<_VIP63a+#N2|_Coz0R@%fb&6 z<4ai5)c~jN3a8UVz2UN6iF1?T7LU34L?uVRKVWltuAZz(yPs1W5I(dE1zRFISc~$* zC4!<7nmOrATZD5*EEuJ^&E+>Nu8HmoT`|BWab+n)Ud20$tUgdqy@8@B-2sHMiZC<^-mlf#|S!IJ;TWe6=|9peE5f!#=|h=GaG;Mo*hX% zWV~X)KBF^Y`)bgt7-dFRN({t!amx_z{fIc;1aAFqEQDip{HBx(pJ~1b{A;hP8!WRf zd|%Vnwq9`$e4O*S;df!kuy>~CXmbny#Vs9wA$?DElzY1{jUTS4Ht5-7`H(H4zQ>XM z7ZH8@By5GBf-v!t-L8qzwM#N2)FFm~2 zIB(Opz&^EDsJyVSHQKFKCu-?PI9S|-xfAb4Q`6$Uqleidn=j`kNcho}BX^Iowz?sv zMSAZ+&X2B3(7x$18sh(4L%d_0)#c-xi(JbWZ4Wmz5#po`RN2)>Ozu(Gls^g@QzZUy{@1Lc-nZbNwnn(Wko5S)00WzQh|Ia$ihMV;h zApPfx&5J2P|8EZ=xL)TQFM6!$k@fXT$jH0ErdC!1XAj7*5DR8VCrE{6B&@7S%W7;u z-agbUEG+en5c9bI_RCSY0$gtGO4zg}qN0MHgr@)Yh{w#vh5{eFppV31xx2S_GFOD~ z@%EHgSeP_8NY3(~iASkB1MMLa^j|p7G}#cCTUeBomn&?G2}gw_C84uguZwGFV6UvG zrxZ$fdHG|L{5#j7e4(tOiab)<@u$h8hB{2B$bVg=v`Z4GBWTwRjPB|q5n*J zm~n)A9ui<~GE7)`ZS6ekF!8FL8`qMG3Nj`pls<-e#Q)6i5O+3&53oSb&#pgzvKpu- zwNepxj8<+4Fd>$r@x5R{Kai0$)rpoOI~ z*Cabz<+#!AYXM4n=1-|OoIZwcuOMKj={HGB+bcT$T_Zx2(?9na7LTgeLR|(Kx-as( zFNi4?CDQt$;NlN$Q?&hn(zz=vuU|@@nr5+K5qbM(eBkZ+3D{_9Cpd3K@|Vo59`+nL z8KvXJNppk#T|38Kz*rNuw<9FbMOtFAV4d6i%+&PM^2zz_+XrGk8H`N>zGxVzR?LU? zU5k15Eh(C4fhHK(eveBt_x`!chHN3Z=b3qu>6Fe)ni zvM#bKtn#yYa^k^AQCsR>89anAj_ zRXh{*G5cGjA)fql*Xe`3IZdz;&wdku>MipRi?dX(NYRX)*1Y0YVrr$1c))$s>k?SX zaM`Vt^gl@SWnpgZJq&PK!rF2@VZXkZf335|_gGWBShJZiG6pMUucX71&#}h@ok&ys zyLX6Z@ucQRIX3fcpYCC&g6xw-M<*5X==D1q`&l|6xBGLu{K(+yf*OUU!+QM&k6%7L1vin$A;9$Q#WU$4c=%S?YLvNUKz6v{Y1Nyv#B$L54rwBQYPwRv>1=ZxbnZ5=s!kS&-b>emoTc)1+O z$`+V*4KCK*g^;+Y+t=UH515<4@}3f}Zf`dvgPZV-N@_B^UV-c(DXxc6%FIk*_0(0G z;D#J8@9VI5J=LPYd~WaTUtTVn>>pV8^4%1#U_aL%>(XD|wzAJ+x2F45QQfuTzod0{fSA6=c~zH_|nJi~Bp*TGM_qikA#b;x@EASf=YFx>6` zCYE1#dW!w|y40npto+fy&RbhoH#|AXdHm*)_uQ=3aq~~o%wMXbfdZ#ZYe$b2Uut}S zq~-Pw7guFFH}F!Sl$s{!4s)a>K>il=?Zux*N0$LPhXa>rcHsSIci1*u@c&rs$VeS> zl7WV1Y+IAbzPRGyX7)_eb{UQahB!&2AB2Vh4krmMr*u`;sUf-t6s6f_68Zfr8qbi^g?G7#P7rdn-0qt+Ux596pDzqhr*~ z7mZx23m;cLU4a|ceEAwrO9;VzesfqtlHo-gx2LD(pSMe07az@$(L+~HD6kMuZ|{%u zEmelBtc#ILXJ<3<8=c$_Z(AU?d(wP{bA=NZmt#{?hLb`FAPjr$HYbXz|75m&9Ca12 z=q)3$L<##0@J7(B#vH8Dvo?IpQ*$psF7#&h)}%Ffn{4;Jq~oq6@#VQ2#Z zMz*S?vsy!iSHytDQ}B8uaoNTsCF8{8kkXafV!5T-XY75%k zC5G+$Q&}11!~Fz8-}P?Z_AooE(DHgpo$|x&2VF8C|MjjmoIM|OAjoNIE}a+_6&Bhb z;WeQ^2R2`Q>ds704`1)}HNTcpQ_|r2>0{0>F5WbCefclxc#yPikR@yNn-yjC_C<3~ zM%z3Sad7y*+|NxlpIv%9eG0B!c)qPA+disqk&=;N`ChCw-97KgRy(fT$~3+7AXMh+ ziSzQkoX}n*eH{n$>23e%)wBWJ9Ygf8?sB!U?)21AuG8zmK z62`~%Rn~*Y*khFIs(t9?D0?U)7~c}(a|Pu!G&=g|Ao;66R_mYi5^HU&jt)10el|vP z`{uU+e+V2whypM4i#FjRK%5kL!tGwzOQ4Ft#trUr+z7TU5_CnH)Dp?`e!I;2_~te3 zw?3M+18qGD`VdadFeA zDM^G=^!sF{_a$y&Neon?OZDkpzH9pmSP=G}Iz32b#q^YYt!x&2$Yi+UHf_3-IPiT2 zV0^v&ux?7X09a>!UMWp8E$^(Lpy2j8LwE1SwfU^lqok_pD1u4$sMVsX$GGlp%BK^o zKJxT-+0Y@xsm<+nq)4qRCmZt`Eiy6^7AtP5 z&%t}*bitze!KCX?OG&8NT%mp6!xDfjy-fLRLad6~o~$(?@O6El>v-Ov0cb(}){*BB zz4hMn9>wkY-Ir0%d!Qa>1SX^Blc_+aBmm};u@v;(J_mp)D0ZrOzZ`7s1ehtNe57+E zBv}*D(Ix#RdQod5@$z|}(``N(BmhK_mo2{cZ@!0zjl4sHgSW>dT|Er2#$Si}jW%07 z)j8l=P77%LLjyNukUpUN5q$#g?$~^752C!!wsc>fJ7gJcLM*}NeL;~@nVhLGbnMwr zTVFA+udgyoT+@I677bXu%g#3p&)X9dZssHTb{%?k_0$LgHr7`9q06H)ThANxk4=}a zbV<6F5DZ<dJQk$N4`1R}F%r)*FuT#CN-IVrFHv{~X6! zxBYOkT$bw%f+GW3_gd>{u63}aT1G&7p>Ms6(H?uwE>wU9~2Z6B3 z;TK;7M~GTmT@E!t3#?LA0X}EoDu8XlHb2eA<(Is*++TD@%_tortoKnUI*3u^DM%xs&+Ji3hgz;WQSDyfu2w#@xA&Xl#}^4<9#-vE-_KRfNwVKu`gyeExI+ z@YMas>@Bp+-V&odasUHuzMl=x>me|O3F7{^&vyIrM|gVk3>QWF;dE*QSV^Y$Jpy{+ zDgI9LPVpN+ee;M#y#BH+8-~MPnEgt z?I6Ar>5?_RI6m&6KJJI~N-8QMOJA0sf?1xoK5Y-|?;naje|)~Y-?!7KUwT+n6?AlZ zW`B*nZ2L#6)foIrw%E|+v$ux;u%(wFzATfmqzWL*wZ=@5@TBvG!RKdox@J9c0&n(Y z3ZJiFQ(cR*Q`hsEVD>ivJ$Q%FjnFu79ut*y&bPPr zZtpi+HRcOxjgiiF{6w``KRh0BfhO5MnDO0QS*Ot(-QO~wa-SZ^TUt1zGT3waZX?Dc zj(tofPT7eU06V_(xaD7Pb_ZC>6u|Uh`?e8e`Q~;$sjHsp=8Cc-hxRxOSRnk7%&`UE zP5eei4E2YBLbk@(iFa%@kB{OH(5cz{mIwxG%keAdQDOINZspo)6B2enL&szW4(%OV ztpNe_{@HA`I`i;C8SLFJtBM}CaC2*PJ;*ppHq4}YO=4ZAmruWp_P(yTCh%bb-?L|$ zXu(C;pKoM@F~ez>a}hDh+b&()IdS z0cKE*hc%NwAOL^6VboqNnmaLCo(9oz-5D@FhWyjon$}>k)PS>NJYOvMQ*sQj5qWsu zZRz#~qlhR>7|6(Qu-Kew01}l2#_LKticHFA6Cj+hZ9XH&et!Dov9RO9i$>EKY_`9^ z;;2kpr>CuIyc(Irr=*B0Dk?g@nA{yTX--`@qYw~$4fvr!%j@b+E!!Caxt>+HxbHiy zH=8k;WIK^>y>0r`+3patT2h`ku9i>YvgvatCMxF_6vQPZVQXLBy#P^CV$RY%Gudnc zaO%7>7Zufo*!R+sl9e-@UrUugD_?q|F?m`;zM%{<8T8vu@}5aea=fL#Z+-Dh5q#Yr z(Ji!gd0VcvM)DJON3wmUsD3?Qk0n(tU(IU>1;dk(lLti~daql*09>HBva+z)RHwmys zC%JXo2c7prH;+`dH*3MKU+rI?Vl!_tJP-M%$48B8SDXecqBJl7T_di-`J6TW)EzSg zE|m%qV~y1*kxy@CdY!dn=`gd}9_73=5)od^y`6Wg77ASBMtNQ@g}jHHpfW-jKn?*c z6*EYJ6qES)p-K9xp)3pFf%S_`h~I({B)>HLfk!pE!OTJRUt7)roUwluK;$$?kkWef z%|QejpFzuQ9_0=Y01C)!lG;;xs5y>>#l;4gPe25$wop|bdiX1O{`?6>$iWdiI(iDa z`sKznIdF7$^A7W6iNXC-T)f-5$zk1YkBSa|LBB012qI&WzOeDuz2n*Q)A{T#m#l0( zeVbV;DEOu2w$PAIN&!frB~>=Zu}e26McF-J!t75Obpg$5>KlRQFu*4engL)hueC5pcij$QtK)rb-Fc0ce@&dr=X0!%M(x_I=mFqj zKyphG{KWv>v43M|7vG~$!QuYFjE)EwzRtmfdjWC`r1K; z=OCZXrs)+3nhW$Au^%^qV*ZXr20m${|8-d+v3B}DGjyERUM?#_%N3K&)A3DBn z3L2T5j#bPY0>m$zd{VU(2-kNxWs{nMb}M)KDn2Q8+>@Ib$H$NWT98hl*orpO3Y(rx zK!ubgG{qGoNFUJ}t})QN6vwo}jC32}uS;oq?SuqUAR2kq1)KZC0N2R8a^R7!UMvII zkP1o@_QUH^uW4%DfC~k*<#du@zf)CRBSQt9EKy`7kU~mlF^s7!{_v4U^M~knm_rbY zP1_~LjEG6S%q?w11BFSSAjPc(=qNMi-<-^{d9yM82+4sEQgM`QSVa%5>0|NR|FpmN z2EZtE1w}=8)32wEFAHzmiA3(#VVmZg!fX+-x)sHxGn@DHqAYj!uIRZHn6FC@g6elk zMa7F$!Sa?C@ZdfHLC<$@fA$ObQ8~;v)!%GDH8q!2_wz$GK2IKhS@ZDJ)L-CdecIjiA{dVozo{*Gecj3f_D<=(U-)*zjSkbrV<`O;X@Tau2)b)~p-u5W7DLy-U z=zlv9=*dtVnWTEH5!J|u1S$rGT@Uz?!xjD2ZfRN7*}YNck=okYO|yfT+tQZL0UGyp zx1|;x2EYlV(p+(wOeVX}ZhrXm^vX1?)c&Gwb5^c3sm}(*TRGw1KRRMO%7Od~{4ZQK zL01bu((b1H*InnP+_j&NX2g8P@}fT*`)+l4Y!LzjNz^;sCy=D~!~X9qD1Ifc(ir$g zkTL=K)D-9g4I^g1T0#M8`vqDT?RsnyEl&Eoi$&0_VWd(62SybqrLe}sxezK=e2sIb z53~5i+{BcQ{6ZXpyRlaBBUHxoI-*1!X6XI zc6HhX2qFC@0;oRAjeSC1LNVj_?6Zm+_g5IfXf@Toa>W;2@f5~2#@gD+k0*C8RJ7EF zz0YrSf$NoK4E|N=KJ;Q7f`uYOIVLTB%|qc_s3-y zfW&5Ry29{gXNwWMzV zdxd1Qs-mJKpi0vg8w4;xUohN-DveNT{`lar&LJyf%GRg{zC7ZfC# zmX>zy-m!6;=EZW)1b{!rQ(1j$SM6e+p4<-j?lJH0++H~Uloew07E6^aT<>cdd%zz9 zyz#xxo60pB3YQkJp##4`_yZ99?%m6cyVvta1sDkM@1AX>Zwtkplc_*rrzc5CEHx$d zS;`g`>K&E9bH3vFRw0zt#gz}c&MWGTwP6L+tA$HHWt|kbex0`p{^C+n`?)am+W@}g_RzqV zt7z46e_IA9={QXBxG)$?F#_mdLuE?@z!vO6PU zx!z3GDzlPQfeJfvv>z{P1dtbIY)2&NI|LmZV4gNzF!qL`$pB#jK%T(kW`hlx-8mCmQt>FKFjt&w`NIRH0Dr88vx}M?8PEy+T>O;DBdWKwfwTD4DafyE~C=UmFES8A(Z_ zPD?sM0zHW9)@+Q7jDLZaL};kxR?S{@Hq2$qTa+^Z0CwVjwj5n@-AnWT15cvNue!eE zncwe@4Z99Lp7>4^ov?%KQ37u4ap5S(C?GL&E7_ z(SZO$Dtxy0E0$0&n()$Ery-=v`irFX`^m%m&B+$1RbF%Z>nhIsiL6g(rK+mxkTl!I z-~`)OK(P1Sz`V-B!h+}LORkKCskQatp{Dhb-`myB4o6+f!Qz^FyX!ZX%@@haj>qp= zuOAqSP!092u6J8s9LGAENdtH;>l3ox_B?D`?{mij`w#AJ*FC|Hts4Lu=JvSqwO2c4 z%=och7b;BLem~oF?e4XIbRfDkHa@!F`F1(@SYXzJPOH&t80~X|o8@tt4U0L=haIEd zYRSR2<%!eKU`0asu}`sdcLsSCT}c2P2w+7AynwAl7mh)Hkf12D3-EW-CeMRWW$wr4 ztLuv_@^k*bq=`6bz>TT!?&C@uzoRQBCzb<&Z}6}K%m?W6s7cmfRjTr9rr|I920C9? zpS3Ei!edn^-+f{tItTSG#W$S#!38iyjhndZf?PZpRfXDS6J1{tk%4EJG35ZEdlrT5{4k&mlDJkLJ zUrPaEHW3w7D4>oDcn2y_aO3tj1v&XFpmOWUOMr&f1_cK4wn5Iz8`bIcrl74Y?d2s@ zbOsnbAlTTltg^^aC2*FZvI&C#CQV$ zA|NJ2EbaYL{YBQJS1mbIF|_w4OFOWi z@6SVg`||(~YFOIb2hi2L!|?w*kB5ji9n(A;^4)2st2BTb= z7kAQL6M0o@@;~L|qh3_H8(dp+^|5Zzkw!X6#P_;yYJXf!2Vj~Ktp7|Q@4TZ3HT;S9 z`_Z0T8|8N?eQ|4Kkv_$0SWp|ukSX3OgOe~PFc73iDI{{@SsbeS|5VL`h3KI_F;mFF zej}mNm;Jh_U6on-0Z>s;U;mLP0gc!ywmLR6lu*Yh&VPphXbrUr!G8)9C$&-%cVy-G z_5KsMg!eHWrh5pA=DzuEU8!P-zv4M-6H&tnn# zcVJOikZA8C!wWce8B31@?MQNqQ0TK}W z@6VPX+cqlyEx&~f%UgA+c3QFG$e6&MoR|SJKH#9i-y;|tNF1kNAOe3+K{z|Jfg8ejkp8Jy;Z z;4ToyLLD3&Ftf1y(CY>|e{3x266_(|5eF-pDhn+4)gxO!G<=gQnG)o3uuWEfcyMw06;5B@V_PmtRrq9>ifUl0$Lo< zRL>;A!TRSfJK+TXsFeSUtFHixI`Fn$L_j13q(ekPL{jNc5KuwDLQ1+}>F(}S=>`E6 z6zMLB1&IaeE(z)G_%83g|2OmAjOdKO#xKr2_ndR@K6z4=zUH#C9@<^C&yhJDxjT(} z&>_HKh9=TFW&7VR;|~^cD9KUF?tHt{2$MjPQA_Un`O|XVu(IN>IwcaQ|HZt6f_5qM z*3uf+R|aoukvfz0%=r~cS*{0D0@fDK0Dr{6#f*=B+LEH zitHcNKR`kZrMGXNy|5?9Q!dc$N-X_kKAdj2CR7(lc(3GCoPo1E>F?h+h#JjE;s0}= z-KTSK zYzatNXal<}P#ZhF!#zPJiM{ey9^D~fq$~k?TrqCJqLAENVLhkv(+WfJVuomSypZoW zUQ6KmdE4XSo?XrVb5N>PW3x9EEsI3b`CN7>Eo%PB9Ph2j>gh2da!hoKM;r@uOKGB- zAH2ZtyF(HT+9`g+GuG56nhNwp=f{r+f2i)1n&*CHdQQ0P9;6Urvk8RN{POFeue#No%36qr1uO%jOPAH`;7W+uT8fOlT|?Z@gW z2gsUP<$2$}4VUH>_1VwO%^90)+09K7a6cIR-}ZD{q3-a{4EaPs3@?z#enGPuX)MN? zVs{%F5U^!(Pw02KHI_Y`x(n`da*i$v-&4;2n=3HsqH^qXYj0K2aVRM!p$n*)U*CN5 z;+}T7h(qLWeQipaF*H>6W*y7W(yLO8YU-Fst}LCB+JIOvSy6La#1s^$`{m=E_XVb& z8_fS`a!4kppnxC~$Zo>A8!=4er|lgb2HnY8ABh>zUvu4yhf>ECgy!`j1DNKveHSQ# zYz^p5x9fJ+vu6n4z( z5C6P^5I;8N^zbw*7yXg_(lbIZsjGWWqAvCP3}==8AL_j2lV+^6{=p|r{yzP!KX3E7 zMy@t&`Y?uZQY#3OFp>moQ)90Z;;!Ld>~2VStyos1ifmMTv=Z!}L(h+z6)elJTVss zp4f~+@fmDyfrPLH7TXT+b6zk$WrsXZ)*P$92bBHQIJnq4m+foaZfsq1t1GQhXE^xQ zo<$Y);gQJ;Of<)8{LgB`-;}eutDQ$yFXI?QS}pYS9fU<_lD?Ym-xjkp>?IC4HrSr> zbhWKtA$VOj|dMpU#ZE2TBQT*r$XT`J|HUaG;Gc&LGj(7jnA!lZ~SCv*ck+G&a zUCELOzjKc-bYa1eA354t?0F&q|GGWeb1dce`uduc&vpTS+s2j~-#101!+EFcVsoK> z%Cub5!h&$BZj~f}kWNxk@(PI4ZXhAnY@*``jqj9Ue($m<=R+>*=g6nXH%2i+6Z!i6 z`=;%i_m0M<&LgX;jx9FE*2*U&U0hrO=jkH@Mb>L_`;c`m8oSD%JdwP3(N(kkJ5RQT zynL)>sW+>=o$86ME=h(`6mqN#G;cRi`bdTRlylyB-Q!)`c!3Q*=rA$^R+Uu*eG2tZ zn}>7tB42Jvz3gRcji8eP!4{}^XUYkW_oJUpl?LM#)}kk?Q@Ge;Q*j?YboOLOQ9Ure z1u~OLtTf;TSV{VGtuInMjP9|sfBy0%sJZ#7C_UH8;CH>=Oy(!IqbF6 z4P;)uB6<5Z#%;4vmhOByDmAscg~BtGLenYw+qdO{WC_v)ZXHooRnz z^;QOvYlGH;v>Fa0%pB@v$?za{f?7Ap$fA;xq}0^d2?KqhVR-(0)D+)eGR1@Q(jvvc zJceh5jv-#aQhZ=wE$t$~C{@AP8OyR}Ya?s+?9a`>lgj__5!yUq8YkV|C0+63r8%Zo z=uI6d>uQbOw7B~lb>5sDzVvk4M(8%djrxN$g$;>Um|3f%Z`;Q;WNt4~XLYd?3-Dt~I+jxp47xZNG+^|M_hBqSZK?I&rs=p5P6Ao_1 zs@e&Q+`_`>U{Y3I=imC`M-_{a6Q?!f3ZCNQgsz*5c^PToJnSx$j)RPd5#e-bq!^S7 z1S!Swtf=XlH~;|I>m2!oQ3j0`DkC@F%*>a&|2|Hf`R_I`tjq2{Tda8OPW36Vf2}cX zd{_TE?<>lLlqxY30>x6*^ViV$yJ(Ab9iE{RU;_UM!7uwce3iudeu!KE27;ugNyQ zpUJ0+2D^GVOMo^o;*lox&J?p^_GHRn5CT^B@6Um~<7*&IDQI;Ilom6JQz{-eO526b z*2SIe(G|;ATJIEfW}b3Zo0FsH1X3B^U-T((5i-alf$E;lQk1_0w-OHE*SrE&qfPB@ zu|*CAVQcE4vrSg$Iql4*0*Dv_#?}nis_fTdUBz699=PvSEPXFSj!piP-eStAsECJ% z2;1F}wkms?wzj86hnqV3`udyWwrEm9S-W4SA>bk{bY+7;^@VCee{=eAvfFlW&EB90 z{EyY3su3@8^v$H_MbnaSVPTBR%|Mlrs^5qz8!e*poC6OA@`#X6dF1a>@+kAhrz9% zvy+Tw#SqS;YNgFX$hi^1;*oQh zQN@yt$=? z*LFevSM)X4ZSme-4~L7@u;7`G&Y|%KBa6`P$rrEHJ#E&VG)ns@@bVr^%FG7_XyFH(uAzg=}bbW7a3qy}vk66ze)Y!W(KsGr zQmjhQ#Y4P*6jCVV6COBeo^=%y+m>!~Z#*1UUTC=dYZ2Y;VVW2fFiy(c>K5@F$)20< zKuKHf&4-@KjshQ5DKi&>r0H7S=1nM3OiWUJ(e|08r7=TVp0C4~WSRm941noCCZ*`4 zdY(iq}*R%^^-0YKq!x6k?Xz|%9Svs2z- z&6K9y0x$yz#LlRhrJdRwK+C0BDa>)vDxhHSx9tW#y=YLE)=_poc*vff26w7HebNVq zm<-w(uL+B}kBSRx=S@#}Ax*M#x^n?Ji3qerff2a-*RKcsblUo`RHlcDn1K-3fKETN z7X&?1-S;oP!0{_-_3^nf_+6dhy|~GrC!i0b#SSFx$ST5V1h!K)G+IkcUc0t@G5b$n z^ryN~LxJUmaKuN)K=au9TJ7<@$RUeGu0v55_XR5&xch-&^!`N0_ex*BRJ>Q~(SbVgYceMo+#GluzS$k`WMwf!o{Va8G;#kNF?XxeXZ{#LGgP*o zy5=AeVUl=w>ao4O-PYBi2hl9CW4BbN*}Dr}W=UU7c7_-aZA$_?_60x8q;0q@@*>u2 zR^lK8zG}TzIz-ICFr-(iVz*IYl#5UGR6*_4?!LcX;Lc1A(n+m)BeP6`ayErPJ^rDC zV9OaVk+Dl|g-7VqjvZ%^k5v-*hgRKG>7B|cr}l1D zjWO$BC!U&Tz9>TWfr#AkMEm*HIra&J!-+a zDmi03%s#RCePigz`IF@l{UgoSoj5q zzy=2Dqwf)R)f1OK%faNytt#`iIL_U*jxWEWL1x>8exnpG(0rsQ=55{(H*%CrBeEis zLR;dzLXYyY3&~flj6j7I*k7P z6B!w7kh>1gJlOBugX(2pebv^@jkcvlTHnY>uQ;KPo?K-Cso;7MH5m)r*gqv!ECxg26ZR!AdKa zizI?Z3|vA8zyxEn=g14CwzkT^t>1mPH3!o{O^Z9lhF!wX<;FaZ#){VmzmHK0Sa85g z`4sn#8`=XvsGp!Rf6mDv34X%H#bpGJCEYJFDDh-u_>91jmDuEMZTV;fEKCnNdd^+Q zNlBYN-9^0yMAJ#k93R(!9N#TQM*8~tm_L7b2lG|O*3ZTVhBmdsX@z_MUoJ5b@DSQx z;wJ8qI(HGdSdJC$`>)K+IrLh|KxYUyNrWVCk#5U#NGJ%4IzWMktj4AKu(wA5Flt_4 zPuq5KPh4DFxa!52P%c*WLUCsx!GIK1w)-XemBYNjbL`+vWnn|wv4_6i^my|rv;CvUTSIRe5hFpw! zr#WBL!8zkcnghNyR!31#WMtFIo{R)5IHOs$fn_&*a}-j3<)(R*4V~F3>#1v%^V1O8p`@Yd044{D z@38x+x%Na=gl7#X!_Zsl_oRDQXmBPbC8-u0(m~)#?DIiK`eRJ0hu@-xhliJ{pa;I} z)+5D_D<+&v?>)4eucT`;>P_9S0`Am~MuRZ)QdPA*Q(6v^)sOFx==}RL4-Xh2;c**| z%EoTo@yje3urYqIrgb(IwMqV&gXy-8LWo8U4^~P?r*$NtTdRLLxFg zo)Rv=#@QA*;gp3J|D#g5MrkAbo0-p;Tbomq9H_YpF3sj?!kh1#Vhy46KJv~lJCu{2~lQ!BRp&>!x z;aP=+7)f1&79|YY2iL^Oy&sL0E^=LTS)tKLRL)JhwZ)DDnt;c5$Zqr84eo7e5u-oF z5bhkbyF(j0jqasC9+`XU=b0m)An)KnzO&$7Hsv>*`me#|gZBV3w_Y&Uw}Z8mS!U?X z8&VolDFrnWig2p#G;+I*q`AF;BtbJL&bSDM%Bg8IpnK)kCtLtLj)|AgG{Ss$Lh%Hh z+a7tGFQEEM4u2-GY;~92{gn)JSJj>lD8c=sVF?i=2~%<3k()Z!%007ZcCFl1p5&9- zY}91WtXi$ms;{ZyKeT;Vs~*|_{DqmcGlkUni}-?JapA{bOS#?75sK;UIUc~V0(-S6}GO{r6*-RZUGX4H4{ zke|O;Vwbz?k)Sy=T2{1{FT86~6B4L86YX!94d%~JI8Ip}XaIwJrl@E%^GkXycJVJh zb~*@TLsRdnxw!fH9Zo;=DNQ{BfdZf@U$xj_N~`F@hfOEZUZR0@Vj3DbXw4QpPH|_f z;-7tF`H&E;0KL8(G}b>09-0mDdYr1rgi*FaNUwg^Uz=>ks-iE{dE!1NCt>x3L&Iw` z`%+6l{A7pac5nb*slIIqxtX``(tDhTk~YtW`--y}?h!RL86@)l*47q;Yzl3SutXeI z5w}3X*y&EfC<*s~;20Hf7bY3}xtMq7X|*R2mdnB?i1WUIlKUgE2a~RPdJUAmjON$8 zxk-xp@>$-S82>40PZ}vnpfgDvcpzfJcgruz?qi>MRf-ZVL?I4ymtkD8l19HzCu<#O--22&c(xC#IlhI zQN%?4k|s9~N$KeP4LV{UW%a`N1-)bj@yN*Q=ue-5xa!B-IAdL1U75thkUwdau2Iv_ z=sci~PE7Rq`IFlXGViOi^-M_m?v4(;uV3#jt*$;XGoz`Vi1y&63|m@XZ~W!IlQBeN zby^{$B7Zwt``;htsE&E%`PdI1Kc*|OMJ0)l@~Ywp3k%<%ruMmcFIMN`98;P39KL%i z?%*~nE6(%mU*9^F!C_kH>X5Z8xB?ma>7Apt&SW15+PiJFyfep+UtRs$*46b`PmgqT zbhL49Yi8{}4Cu1Z9L_yW+d6Usb^CGlD^;wdzw`6A@7*JP|Ngx%2AyWbdQ~OBLgPZ0 z`f$T_qwblOYeYm+Dk_w2Zf>`tpY{w6Vt;*OTAmz}o69=*<2^x!lGmu(&JnzT$ACj+ z=0}t4QMWib$)Q^LNM<|%txMzmd+WXzW+R=vQbI27U*E7lz4L)_r=HTKmj3MQOq&vW zXJLM>|7#{s%vSN${N242F4Coq4I+fH9xa0GI#&O(`2JR~ISq(VPoHvsdLHxkYaZUe z_4UUuUfkE!&77H=3y+9U`0~cIqN>WL!$6yxa%P>A%DmcT(_@5I@SU0(e}FX^#>e(% zY)K)>+`gIJe|&A|wD2!q*vQDq3z5-J?GNK_dZ;(>?dEX(#Vv&tcG|wZ4{-VT<1I>YZb(Z>VZyN~w3whSDKBSw z@PG^kr?oGv9CHY|YXoE^LJ_t(*`Gf@;jdu7bqgUdQ9_PL?=`ksesiNl?LG*i#MIQf zJL6Z2b^enDD5r*Fq&dmq@Pkg+^62E`@f{m)c^2RLdI@rl3PLw=B2CT7%aaML#7R`# zurnUJaJY+oDUMk38l&IkU-}#zkHx)&;#qEs(nd$$086wXa?C*m3$5|Ziy``HQ1wO? zCI-g21B0cRnUUxC*x+D9TiZ2<@A`)x3~q;oeQWe+drMaqUR6{K2lGR!vokgBFk~4+ z18qplj1@7}jgYEi8E*Wh9}?X7n7)l(!%J2W2}s&8auy?O($>}no=0`KT8#b??2)qB zkmiN(%1nkgxwcy$=A1?ohq2v*xF{!PL7Y$`~Ed7?nK84;8isg5j@k{pky${ifHU zw$|vd2Q?)tY|B-yTbhj~@&p78=gTr~>Tc^k$j!J4My6q+jho4>MA?ISqxMf5dM5b( z5YOIiBvn7ik{s+(ctzBe=rdOQT1jchhwv?flLj`;f?<<3?<_3%m-={mD(PSS^jfX; zYUsCwjB}^$6U;B4QqVrctBj4cLMDQ0Qz7+hLfv&9LAh58Cb=of6<+*G!og#8`$K|v zv^20k=TC>lO~{#P9-kCG(wlL(`s8j^KlSN>{$00mjVMy8u%UewoPn_Ycgx^vJq8I&tsQWf*?{w@fEwMo$E!@Q7-|~Yv99#n2 zLT3&Rj($<{vojfj)Asg+2>1!};|29Rw$ad=)YMl3bhvvYybjLK$R#CxGKN+FeR@Vl zf^$iauxob!G2|OC4+CL}NiY#s{36TMEh?$-o3u|N zQYgk9InH7~ee(MJ`RacC&FjqP`v*g7hGf*@oVS^n7|BB~^Tt`lJ8r6!0XoC-{P0`t zAEcKyhSacvu3dG@S1%{uS=0)QlsG42`G|Y>?z0}_2XLKTs2b#wT0BERoce1GY#If%Hv@SJUpm`N~D?#ogUVw`MltOdUm$IXxpWZA7 z59GajoJ-RHy2y76GP<=~;PZX~0k>FS#w9y7v>LwY6`-gb1uK@MZ8gj7Fk56NKXVk?b%Vh%D+5v>_A|6}1{euFzF! z3X4>4g1$0xs`I7SIKO-GPj+Z@6meuzAl~q*r0rt2cU~S|-Kh3Dd0H~0B*&Y@AfJPm&rx}T`33IS+Etgl-DupS;A!Kfnc z#mUq=!me6I#>SwaYdC3WSvb0)qM|>|hAiwH9YscDY_&tn%PA0#IODPaM=n#l=VyV$ zbac2B6clfiQ!znlT^(^|V-uX+H4&JZo=YVlEU?k&=ebcf^g>;K+_mce>gx6th7N=-IPi5J(#pCDwEzKc-30K#!@c zx(5f6+f14B^z1CZh)cC-AD`$cX|SIX+neE%cDMS5c^m7M?*e**`5JIAz!zIwEv+^m zA%Q8)*`5p^+yZHZ{{eNss5}^V6;%GA(Rh9^ekv5z;kwlrm=vmbqa2ZmhXLQh_4?d^kT;N3W3g+p^M%wkzZf%eY^sX!OzOeemAorHRmEL6uqvmKq zWazKc?$(yU5T$w18DbjIL$ez%;>z-y{To>Egwap0A(Zk?h{^s&3ONhG_ojzM3Vf$X zY4EX~TwD|mPgF~+#OYnlF%i@Aaav9Lbk&@4^$JeYXrM-nH$pKGB!!fY5xfnuywTng zj~_pN`ZS24&-Tbo{8v+x(!!I3q(W1(xc5IJIiGb^SSOyI-uY2s@_aTZ@`9I(RPoHp zN+YSRwuXEtw|P1?{|@bUGaX-_p;XS>NvLCN3JHxv(ZACE+FJHZG`o5U>2;jn8tkoS;>qcQHqgpU0P%IY@7$b9 zhS`^J5M&fo!Rkswx;J+*5j(TL8uTdt>pNe*Jp0i+Q)MV0d9CV>w*+GI-`1 z+{%}X*S0>T%eilb-oCzus1tGZutqj$H^lkRI~lff{C#qDuKELeU^lC_<`<8Pv&i(m z5ks%wkdP-3eyBdgiub(&hheziznh&?C^VH`2%sknIp&yN*qiKaur=!+6vB%`67nST zMj8VNZ5WDv!Yg)A+3j!!HRm$fY+pY=r)mIFR@AqTw;EsFPE_JL->RQvHNa zu~7#M$l^yU1$B1fk9%I!I&BA|w#-Jb^QtQMTm6}PS_cMhHXPle&FXFAEDH4Zhu4n@ zZGf&X2|`=@5#;r5bEsI+)zsGZj*Q@P#^D9%+#(6if8K=OrTMpwNA@^N67>5`ofYW~ z$Qy%kb4ytG`W5enWffFpTFGyZH!oiXJMV0dNIS^6a`rD|xEr50wfYs*jp~k`zHOaA>>dSXIM(DBE0fF|>4qPUJnY-Bzi^J-Xo_!5B;`y)!YxC`5{frK|z zO6P_P=bp;RJ=WE&c`+TywD_~WK8+xxiWs1;p*ce;@O!J4v^4YVtnSSGJXZEAEWjea zz`%^6?c}!&>94*%(U~#nOngYHqmuz5F%4~=hYeLlkmB`#)Fo+A>!+HogYMdS-SOs<)D+rgbB4>HfvEKqSqKwqaeJI6>Xf zg8F5vATCZ%N5|`SvZfp_&(fJ9g45E%ytK6B1$7WKp^AzirFZWv%g-wSb-@N=B)i_Y zxYIj69+sM#%2QJ4`7>P7VQlcb#BB*re!wJ*nCHPDQ%hzj-Qwb+pbp12>i+cT7V%{i z@6}hbl#~=f`)_R)ca=hgY`2Nw{MEW1K-WSbgsN$j(A;zAE{veVk!qi};pc8G(t7cN zfS32rh_e(tfVeo}{QOPjq0n~yUtrgRC+Bxb`ERB;EV~q5RZkJL{eUsP=w0=2b~v>19-tt$ zx8J+$x%5y9Dp;#;-@b)iN!ai{$quFdKlt*523;#3QC6`#F2X)jF)lqDu5rdg*eYkX zgi`44EZqM>qvQECH}Gvw;7?H?QdD#tcRaX1T)TLS{hKKUVno#>K28FUeqDFT&V?Ia@8HPGdF^-8 zYvGvE~Oiu`2goW)s_?(-RwaafYYQC4fXWY+TDy2%!DFcmvb^Piz@yNiX zcw@vPBO_L*W&iqg@jdv(?E6u#uee9kF8LOJPnmO&uWbzCQiz#(S6s}^#YtPuZZ5$q zDcV&Z_G7u^J>uK)YU3wj&#TTsFDLFg9v^nqvL{%5Q=1#WF!r5e!J#hYvrouOr^ zxrrN7<=bIJ%wmr!Ff<~#E7yj>cW9|qn0F*K+JT8s;#{FW*y-y*Ry)zn%;Wm@`X%v^ z;;<^%RwHix9>XNb(C&Qvvb$$#cWJ#?^KfY(o+3$9!*9+eOs4Y6v5u;UX?5paLMa?n z5Yr+lH`l`OJ?q;f!Njz*eqyfssz@R{Jgi6*jqR9j8#7VQ#lDlv2gco8-q;9i z)Yg`pL|A?4T0NyF!6zbu zfjXx-8)sZ7P_oO;e#-MAYoJt5oC~|6g4^MwQ-ia^MLyykD7j;e5q&{HRvc<2h{ndp zhkrk!*9;k`w>(@o>of%ZrehbU(1seVrz^a+qo?@ z#qW^tL@<;Sa)`KB#osqu1la;)0KL8Cw}(>~bw#d|vKCC1Kf{IdD&3q~?{jf+L4bE| zrJ&v$@Cy_Lgq0FhxW9j&z$S(1B+I%|SCGnc7{$a0SQS$xU-l;GEFQYG^19A7xe;YD z>?M|HaM2RtaB^}geSNdJzkj8P8`$(gSC2LFs64wrk#8t;KJKAZaf_;*gM%v8xnLi* z(PR=|nlzx-(n{a3zVRQ^Qhv{lZv&?NLqkjerY8psevR5*DjYJ##%$N|Ld~z};b3Fu zU*W=d#CPcHn_)nIABEYO-T5EjBbIFat?8vG<3bI?G_2B_LZ3ZX+*|TJzzbop2ueRjJ~0C}toV zWGEp5W^UgRMC2)RJeGY&5hlZQ>Q*x|!{|M!jc77RAEFsv#P__9;HKOoJ@*Lk&{{I4 z?R+x*;*{p?+}GT+CM_*jD!2ikD$2D4Or3fB!PibvShU)VsH}O%iFnkT*AUHwN>*#8 z#M{%Bm|s{Byt!rc)!+E5tDr?Izcw|ho$HG7vkL-kKz{vHWy)#sg29K+*gWg+YPb%h81k^7k0 zlhr$0;2JS!ZpP{-x?@JPwAV9ow097(t(Ls9L}iY{r>o8t+uB`0zFpBn6Yenfc5>2x zd;bUSci+l?sc#)F#QK|Tb7p6RUdy^o8W#wC$_=$@MlGOZwLRSY2FkRFcznc~RT9Yz zO3zv^-1ue-nV7mtSUh#YxuT&EK2<=vWNm}hu;a3 zDn3iq)O9??tbzif8__cqv=r{{)R#TYQw0SXef_J@%Ft7H4Qi#%5TT@!^mhTwqW{&2^xUdE8fdFtB4YY@rdH>;^0tEs8= z{`vD;lR-9;URGUQ1dJtOa`F%e?NQ=m0}qbxy7N~)jX)xBzdJ2^V*h!qkPV@~BqL0z z(N&29^qCiNj9L~j7ftXL^tc(x~OP1Tw zLL7SRRl_N&U}o*l<>ije?oV#tFziYUc0VCZudio-C5o5I2TUPDhYrG7^hZ8AR)u70 z9`3^8Vmt_QHm{XWh3xKvlQHO2dV@PIp;-S%aL~2Dv@{0DM=`OHU59bW=|T0Ou}?v~ z2S;x@<9X<$rE^C-7(5~{Y)fS_x(;YXlrz+}2t(@c6v$$0m;ppAMUgRQ9C50d|A zlYd9wLV4>-D!(P+NreCJw78bdrR0oDEV$VqOVCe6&AySOF9V-f8-&+;06HV}>df zqwQ-!dkQE%`#BOzy@WZ$810Uj=CW?u{%dS5CgPzQ6UIU+E}sbSh2HxWh7;(&$mK)3@=RK6Gd2SLhJBQDQ%(p{~*EhgNs@ug*5@@$G12NPRE8>qTs~lJUvb zW=>kZgxf^R;m|17;Xk7p!F%KsX6qX z4UCQk0GDbu9m71B!Yi#pvkdp5#%Ky^k4d1n9sW6U088(ZqilyqN3Vy=V2Vc?IYwZ? z@&Z^W;NE+JP4!XwMMXIelNf=g%Q+P^QFBC|Zl?W3)TF(Z_)Qp*sABf) z2cs1|#wCK*vv(Ad1#!jGBB28RT|_tT{_ET9Q*<5PdaJ4FJvv&2M_*rEO#_jmU*4F3 z)62^tV+4Xl0_7q$wQMm99{Zb{B}XJCPAN#E(dluG=|iLFS6?NSl%l$lQS&y8(NBl* z#mEN+N_$L8VdPmG(^J1Ff+q#@KZxTO6|AIU*UGM9$i*F~g}&wLx0wL;97;rg`WVE!O1&541{*F$n3zePG#%7N1^WRxv4f zDm^Rq(^i!HO0u`6voZ>AjybGLOB)dEy3mEmkWvOD zi7b+dZ^(e<*NZlWp#!V@x|=aY7Wv&lfuRk;h>XtzHG=%CES;v+UMOh=eeTg1cjI@7 z6j!maF-z!IWUQ$rZaktk@;swX<+ zLSVxk{NTFN>Vaaq;incem;wO9s+!rIR)?F?`f&5crA*Jw-LqWyudfILF+abM?hDNv z6uQfcm#u_8x4nJA#+I&Vhhb2+kP^yeEWD%`BWV#d(3sndY4h< zc?lgF8duUFKpCZ3jmyEK&x5VUK>ELa?M`d@@q-iQM!lP6!UWH0h0w8OHiEQeYvl0e zHn+DWrKI}eyAw1fLp@LD2m;SHcF^^deLPjwuIb8ou#eH`HThj0o*Q76ym+y&FYqt{ zW$IS5cVcI6FRh}oBpsfVkt5%ZNJBSm&9UbHU=`QWw!ave^7N=Pq{If_SLV%|JC|B{ zQBgGCl1dSRpnidbbgps6O6ESRP;6QF9roXD=$^j5Ii-5ovj9n@tGA1}C}XF82u0+>S?g0q;v|Kiy* zLp~DKsX0m|RcAuDE`mZr{Nm#5G_F+Q{$ss4)$3FChE#MsPC%H!Nz^l;~7a~i!N<>5hZisS2j1+#TAaE79K zT}XE1Kdko3(j>z}fu1_HkuA3JiTwO*6tkU~Ry9Z6EuA6tg#Wx*n8$|*`Sh`BpC+_0lG>GxNdO1Fd zo7L<0VUX|(j$M{GHsbd=hN#1YU5)J*V!*;8Nz9+ZO!SMwE>=~l3;jpC`}@C+_XK8E zM_#Rtn{?kM1K$+TBawQ=e+ZPy-MzhxE`_^4K*E6)m6#1jSb0Z7T~e@g5HX!sz+V{Z z3v+p1j~Zm{-xAolh=K0;6SBtv|H%SCr~pLr>8wccQkRK1O&1JEBn}rlGUUiVLqYqc z9O((Jf;kI51A}GF!AAL|o)7a%czUK|tZ5z**aZs(Zd_*{uh@R5ZzJ0Fut9t> zGy9a-$lx=^{nOI8K!M)c8%jdNzc%`<&kH-(AANn(I52?Km#c`0*x#cS$`PBGnD~+} z8VFnP*kYc;TYP+r_%^%)kWL`9F&+QIR(8}JFfkO|g)LaXF{&?Oos$NgT@)CozBoi# zrKL9!n@vPxKjDyfrb!U}mx^R&R;tFKRfi9u1aAl!UG}iWxtU;V!VnjQTO^p^QHhBm z5YUI=STKLtjUTANCmVpa^N537?_gaF!aNl-0V^+G*^q;03Pl9exaio}>)+uS>P^MpB~s6j;lkl!IDAqm^veXx6QkkzJOstwgtUq4=g3sNwk*4?0@ z!YEV;g8?=3F@Bq8qoAuvWxvX*I6P&D1t|jpO1QXNZ1?WTK7Y<@Crl?&k7|CRx4)_i zv=O3Qj|~^d@`hHImy4D=m}Db#*G30a>fG)U(h6Ryo%DjoddxvS)+yaRTy80$oUan` z&`dcaeVPp%dC0BYig|wN!_T1<(`(-^^zUW9`DV4P&j_kZGl76cBv9(hULrsL?$Yvd z8E%wa2|>5jLh%w@FrW_VKuH9Zv823XdSxZFx%sdW@oKC-2o?%PK~Mk-?}u-%F)fcT z_5ZI&ZTgu5`+6IbXKd`k;d=rFQZlVadl8jKwqW-NnHsKdk#nnbulx|P41BZPZg?vZ zDi+nhg))C4Y@GM`p{fUEi-ocb-cL9jj-A1rMT_rv4sTYY3W9mw*_2(qo_z&B66a&D zG3N|3eCTjA>C(UGriu4gd?y=H=^%Y|9{8PBRu$=p|JK!mB0_$_CYcc6e`S%Z>W>DXF+P-AmAyBaoFKToQ`=nI>(IX&hjnw}YWlFl&E3$uTtKQ#09g35_*&Jf`lP7m1@A>uLBCq-3) zl5*|{jvGOPjz&s^Rp(^uNx~y?-|FncDK?8}*I3d(sL|xXGDiyfL+# z7AC%|Ez|AsAlOXSo=8PrQRGa-oz!*++TQ1^DLSDH^TptZNJU4BBmQn{0*NCUqYnlk zt*&2>3<+t59@?yQt(E3o(ZytiHD3}{<3J0R3r4=~8HCwIf=Iu10QPafx z0VeC($f1n=4GE`4nH*gah!*gq(==3- z=53AUaTSf3nVN0=O4Fh(tqCbc?(A>-goaioPjv~5#XdE+v@E}VS~={o@|G%k;XjHOLUSA@;y zG;p4y(Jvjm;D`=hoPn;Lxsd5je3FHMbw&Lf)koE$?C%XQ{|-!v~vh>cZtg9^%I zxJ~&Re2tw2%gYQ}Lc;5)6tTV2#Spdd*SR6XXVB%s)UdXF-@T9Ro1m^0HWKNr@j+|w zB8eYDgIFp*r-dD=H8@cMQ|8u!>c8i07UX#j-RN@^(*lw#HBtK`34F`|9rXism-^fX z=1`cZ^@Z{b4E58eWa-zh@nEY!GQB`kS87Ti?A%M5MnkH{s)=A|uI|JG8$O%|;wPwx z6m%6N88PCEIgU=oChLtKQ1fr%ARv$=Le7~S3EnCq-ZP!;I|F2s5$@5>fav=(O5Xw*&cC_+}wwHl5nB}R#awS(i#&CE$_yS6+sqKPy-=g`qFIy z|CEQkIGrzZ7o=-shS9{y1HN@${V2*n?pmI zGIn-kKa%S8{}9%I#aNO5SVBr#R#ugO)>R=*+}E@AsM}BaLjncq3+`|o5{*<^v4x`d z%->T4$3~r>ov3q=-D;D<2a_1P@`{VEwT9-qWp-MEYw?nu*nMa&vMpC&S_9;Z6t2 zL%CHPqC*MMuxtKTcK1xu->@TpFIiP;T*|#{WRh9Ek1~{oIfg}SNPtguowf|%z9dAvd67$6=%=z`_r4IAfz*J z?mkkC9RGH@FpPSLX=!N*>EFxuYzEqCwG@Yg6rjbY=ginLZRGzt_jt!)*$W)`%iQJr zH#EsSgXr53Da4_#pn$~5aIq1CJJZwuxQ$VHA-TCY^ruH>=9}TlW0B>P9z%v}x|#Cr zPn&+n+=J9Qc&}?ek1(A6A6su77UjFO4TA_sE0R(wN_TfENT+mn58X3JiL|tIcXxMp zcXxL)Fub?V_wD`L@4KIWI5=SD&ULLg&vUH{KrLhGbbbNi;=g>P|5tnkW)tGSYOvDM zW=92Y!5?nPUKu`O6q?W-ZB8s8w0&~-@EBfQlcT8l2VQ``qCdCW zCArUmE2*%Mdp$^N1*2HZVV@PIrdBa=2ZhQV zpx6L8KEShT-K3Gh0mF~m$%!E0K!W<0V1^fk9BY5q_7x34wFSs@6l-6ASl}{DM5#?F zZpjc>Qhq`pk3DW6;0J3odUN0OMT0Soc_s+a^Y-6?Kg{c~>DZEqUms%R?MH7~`78Y^}mk_O`6{j+3YZ4Nv~CHkEMPWI&M}qfMnp3!bC8(ybNx4*xh439=E6lRsm71 zfMeG1h;3FX!z^@W-!U@`zb&cWph_?He2+C-5hYvE?W>LhlJB|?*eqS1iUc#k(Q!$Q zWhmt4&Na+*kiV8({XJuiwRfMRCF9l8qi z3t;@El<+GV1X>~lfk@o6*OqX+=%T}FXuY}uR%7z|P?0MGa8L+9iZQoG^+%ex?w6kx zVl0|I7qMB@)34AlKOnvV{JHXhKe;%n?cUnD)P)Z0dWZL}TNFzuTX8)zT(Hp!>^{!| z`%N2{?enW2*!`hrN3#E~lnCF@tcOhGRs9SwPzV_N?FlG80U;4k5`Ku0oX6IPS`WYz zr3Vzdz~m{Q)x>Yy%&)YR5$G^fPe9((E0;*yf?bkVo?c@<{so_EMvS|YJGH8*zjUw2N)e#Ggzp+I2pdrE}>QSUvv z$(@Xn+PN;2J1d&c)+z?XKgjc3rWpv-T2LA%5p^D*RZq1Rpf!wokTA|7qRuGt%C4v0 z;kzNj;n!CmREebeB>~{!mH$U791K8u>jTKJf@#LN6v9Y&!+>TEKp^^KbT_lA3LThP z2F#L7kCOQJJ+Qzhp==R4jA1e92QLR;lBA5SB?mL7!1HcqH#cLHHeb-Bc&#JW)zZ%+0a?pK3uj6^XI=YU4ZEl_8e#;|F|JPo|ZK-~ZG0@PGa2`;vxx(*K$JGcFMa zA5biR`-{^Uu>ioLEz;oYc`3+TqvM_X8IaG@2d;XsPIFn<*ZwU3i0 z9ePYcrL$Pvpk09D00INRLjx|=d)>Co^pv#eaey|ju&{Fe*`j+F4GvDD7|Fdk1u&c7 z)b}#V(BOZc;9s}Z_o*8mnPT9YHD9Jb$p;u<`(A(nQ)gubaN|q_Ax!Tu-~dSl&^ZIt zqvOlV{y{;KfHa03RSs9j0nE;~i5316tDXSo>fe&Kf1kjJB{MCxzw}J`1Mp#BV+m5A z`TU!VgnxJ`*5l(NJX}SXvtyM@#?}!b066>{2tqxm5{^C(2$}r$t|;K!<>!AFrvG}m zzXwA)*WoBoA3mt2t`m9@9AL9BNbjVPVa9y@^sl@5*Joebb7PcfDve>)j3DKXw!^)TGFJKgU&j=ocv&nLWr-j2@bPUI z8^H^6rq@R`{)meZvU(L?nFPL@w_pvuV zMQwZO%_hlI-AI>M|A$2I-_H}coIc!NFTDPjlL3qWg~Hl@-`d~rs=h~k{I7fd`;RTu zuFBeSue4P6pZ|G|e;xj}MA!86@ImV_63kV)^*rH}D$t7{G*&`venlu*=wF}u=bOK2 zM@nWnLk17&v>cnKy>w?3bv@JJv`9gZI&y8f5&t}y|M;y0C64`!lAQaULWJ<aL`oKzoqs#yNKIOp{jpk6}+{>l3EyV9tBUBvu{yk40GObA#{2~7Op|8Iy=u&5B zM6xT>690!jm;6=1UeP}Z{(n55!WCnTM{Z4(;4mFrPyVjEMPEyOrg!be#H9i?CNnri zLlOw#gNSg^998W`Y!mALZ6bePCjX8->5>uKMyrQx+4HgVSh)Y^yOr;GvkQIjsNt(?%IGGV4N4bT%BGJQbQL zbbP4quBNYpozIpVK2!4~{`cAd3@YgFWEo(lK==OcIfdw^j2L+W(c;+|k?u#HIV`E% zTs;lXyI+Jq9NiHhToAn8!9&}+GGZG41Tnb(#Xw6?T3{=G;*vv(>g2yXK!r9ae4K>> zYHSdL4mHYdmANGcEdZSggLz{eA3{#njDf5eT4YDn**V;u7N+|&MUv(Ex@$fD4QaS{ zg{m~A-LG_bL7?fq9hk#I3}L{6laAU7{p5$dah{03A(v|1DVIB7H#(v9QgXTYCf_P@ zz4-KoTwsc9bfuoSurLu=y#Kt(fB&Y+-wFqMv^ai2HzW-g&{^)r*Efg|&zxT80CSwG za^5J_efBe?l%j0XCJsXhz6?Ge!#F;>Ec@iPUJ>rmh@jw2K{s&FvB~LTuqh0`G9Qgt z-Eo{Cn0r(4$o2NIGf?guvpM^_Us_sg`}>G+Og+20v7F@gxa+yIWC*FfUuz4nT%NZP zJfR(obG^7=4J%iu<>PUB_3L22Jm3ayo;@5yK2Cjt>9ZM1vkcrI9lZRHmG}Q!{A|{J zCF}W=wbTdfvt;)b7xCcp4X+k|8zIgQ$JmX9=NpWeVBJJ8j(-ClhbCn-NO z7Pc$6(gLA@ynh!=0O?yw;)*zQ6-!a>WUX`CCMow7~!7F}E}vCoksb z%61ywkewTtVk4%6vh`zm9OCfQvX-|Euk2ODwTuI_Gn_eauvWmb58kH7}!@ys7wcXkWUy98mp zHpc*kakmRPMt+a4esMJE1^|c->RJZ#+T-tzh5;s=DwWI zFMoB;o(r-(R9SPCs9x@8rr~+1LuycHP4O0B@Y0kmfq4-oQSM=kIPdwnQBh-%Vq&hz zYdQtT@XhV?U!B<_1G2JWP4B2|_P4RjL+Sz$onNocMU7=xaqPE^t%K*ZLyo2hUf#Qk zwC$!V%!Fp<(xxAM*S_Rb-p?-|BSfdFzT|eQ{w5RXE#KA|sI;9qk}nLIkYLv7mHMUz z_rjBpv2L|LL-J3E*q1Lzo#HgY>+v)MSMGIn_3ya_h-Iv`&4{)k#uXb(=o_0Izp-&& z$d#kHPisWa)@D>SVInod3R-J=c}@bt5e(`_ao&@EMq+Mnd5v1*;W1bdTz3DSPfc0u|0bV^Z<^gFoE)`poe?Vc`fhmX0VTaQ{(;EcV760 zy85+vD5z`ciYu{~4T0zJ`Y`qEIP68?v6qJ#y_Ubv8{JB!^qxW8qqY^%uSYdSTU3wt zrhwpH^JYC$rV$7#XQR3+exxx-gQSpY3VE}O)nz8SoDi2?|+)NffKP`1G z2Oq3|0G-StLi?TBq>c09 zAlA|^Qe!eeuO(QzaQoE3&-q^HAk5r_pMOJub12295r0Rk;@sj?E2K7YN3K(*vXY^0 zwFp_KP3Rf!L?<@*vr?4ZGDrsqpZ;xdpr-r7Ygdm$_@?LU654~LfS-sP*{P*s(!WcG z4e6JgT{oJCIfUo&J!A?AAg6-@fDkCWT$y^b}fP#

- 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%0AB%)4lgujOz6mqY10l9LULgv1` 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 literal 0 HcmV?d00001 diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md index cad982000..d97e1f8b2 100644 --- a/docs/malcolm-hedgehog-e2e-iso-install.md +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -291,8 +291,8 @@ As this is the first time setting up authentication, ensure the **all** option i You will be prompted to do the following: * Store administrator username/password for local Malcolm access: specifies the administrator credentials when using [local account management](#AuthBasicAccountManagement) (instead of LDAP) for authentication. -* (Re)generate self-signed certificates for HTTPS access: creates the self-signed [TLS certificates](#TLSCerts) used for encrypting the connections between users' web browsers and Malcolm -* (Re)generate self-signed certificates for a remote log forwarder: creates the self-signed [TLS certificates](#TLSCerts) for communications from a remote log forwarder (such as Hedgehog Linux or forwarders for other [third-Party logs](third-party-logs.md#ThirdPartyLogs)) +* (Re)generate self-signed certificates for HTTPS access: creates the self-signed [TLS certificates](authsetup.md#TLSCerts) used for encrypting the connections between users' web browsers and Malcolm +* (Re)generate self-signed certificates for a remote log forwarder: creates the self-signed [TLS certificates](authsetup.md#TLSCerts) for communications from a remote log forwarder (such as Hedgehog Linux or forwarders for other [third-Party logs](third-party-logs.md#ThirdPartyLogs)) * Configure remote primary or secondary OpenSearch instance: **N** if you are using Malcolm's local OpenSearch instance, or **Y** to specify credentials for a remote OpenSearch cluster (see [OpenSearch instances](opensearch-instances.md#OpenSearchInstance)) * Store username/password for email alert sender account: answer **Y** to specify credentials for [Email Sender Accounts](alerting.md#AlertingEmail) to be used with OpenSearch Dashboards' alerting plugin * (Re)generate internal passwords for NetBox: if you answered **Y** to "Should Malcolm run and maintain an instance of NetBox...?" during the configuration questions, you should need to asnwer **Y** to this question at least the first time you start Malcolm @@ -605,3 +605,18 @@ zeek:zeekctl RUNNING pid 6502, uptime 0:03:17 ##
Verifying Traffic Capture and Forwarding +The easiest way to verify that network traffic is being captured by the sensor and forwarded to Malcolm is through Malcolm's Arkime [Sessions](arkime.md#ArkimeSessions) interface. + +If you are logged into the Malcolm [desktop environment](#MalcolmDesktop), click the Arkime icon (**🦉**) in the top panel. If you're connecting from another browser, connect to `https://`. + +As Malcolm is using [self-signed TLS certificates](authsetup.md#TLSCerts), you will likely have to confirm an exception in your browser to allow the self-signed certificates to proceed. Enter the credentials you specified when you [configured authentication](#MalcolmAuthSetup). + +Arkime's sessions view will be displayed. To view records from a specific Hedgehog Linux sensor, you can filter on the `node` field. In the search bar, enter `node == hedgehoghostname` (replacing `hedgehoghostname` with the [hostname](#HedgehogInterfaces) you configured for Hedgehog). See the [Search Queries in Arkime and OpenSearch](queries-cheat-sheet.md#SearchCheatSheet) cheat sheet for more search syntax hints. + +![Arkime's Sessions view](./images/screenshots/arkime_sessions_node_filter.png) + +*Arkime's sessions view with a filter on `node`* + +Arkime's views button (indicated by the eyeball **👁** icon) allows overlaying additional previously-specified filters onto the current sessions filters. For convenience, Malcolm provides several Arkime preconfigured views including filtering on the `event.provider` and `event.dataset` fields. This can be combined with the `node` filter described above to verify that different network log types (e.g., Arkime sessions, Zeek logs, Suricata alerts, etc.) are all being captured and forwarded correctly. + +![Malcolm views](./images/screenshots/arkime_apply_view.png) From 7dab50a9d776be0eaa201f315af831543775541f Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 12 Apr 2023 07:01:08 -0600 Subject: [PATCH 126/239] Bump Zeek to v5.2.1 and CAPA to v5.1.0 --- Dockerfiles/file-monitor.Dockerfile | 2 +- Dockerfiles/zeek.Dockerfile | 2 +- sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index 193508398..53a104eb1 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -79,7 +79,7 @@ ENV YARA_VERSION "4.3.0" ENV YARA_URL "https://github.com/VirusTotal/yara/archive/v${YARA_VERSION}.tar.gz" ENV YARA_RULES_SRC_DIR "/yara-rules-src" ENV YARA_RULES_DIR "/yara-rules" -ENV CAPA_VERSION "5.0.0" +ENV CAPA_VERSION "5.1.0" ENV CAPA_URL "https://github.com/fireeye/capa/releases/download/v${CAPA_VERSION}/capa-v${CAPA_VERSION}-linux.zip" ENV CAPA_DIR "/opt/capa" ENV CAPA_BIN "${CAPA_DIR}/capa" diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index b4dc385bf..a2a753b00 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -31,7 +31,7 @@ ENV PUSER_PRIV_DROP false # for download and install ARG ZEEK_LTS= -ARG ZEEK_VERSION=5.2.0-0 +ARG ZEEK_VERSION=5.2.1-0 ENV ZEEK_LTS $ZEEK_LTS ENV ZEEK_VERSION $ZEEK_VERSION diff --git a/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot b/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot index a43cab53c..01d1a807a 100755 --- a/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot +++ b/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot @@ -13,7 +13,7 @@ GITHUB_API_CURL_ARGS+=( -H ) GITHUB_API_CURL_ARGS+=( "Accept: application/vnd.github.v3+json" ) [[ -n "$GITHUB_TOKEN" ]] && GITHUB_API_CURL_ARGS+=( -H ) && GITHUB_API_CURL_ARGS+=( "Authorization: token $GITHUB_TOKEN" ) -ZEEK_VER=5.2.0-0 +ZEEK_VER=5.2.1-0 ZEEK_LTS= ZEEK_DIR="/opt/zeek" export PATH="${ZEEK_DIR}"/bin:$PATH From fa1cb704f1366979f55c16087ad9e48a6a7331b7 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 12 Apr 2023 13:48:13 -0600 Subject: [PATCH 127/239] gzip off for file-upload --- file-upload/nginx/sites-available/default | 1 + 1 file changed, 1 insertion(+) diff --git a/file-upload/nginx/sites-available/default b/file-upload/nginx/sites-available/default index 36936f866..68fb276d8 100644 --- a/file-upload/nginx/sites-available/default +++ b/file-upload/nginx/sites-available/default @@ -2,6 +2,7 @@ server { listen 80 default_server; sendfile on; + gzip off; client_max_body_size 50G; client_body_buffer_size 4M; From 1947780512c9e9162bca3512c0edbc9463851e38 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 12 Apr 2023 14:17:58 -0600 Subject: [PATCH 128/239] idaholab/malcolm#169, make htadmin work with an /auth URI instead of a separate :488 port --- Dockerfiles/htadmin.Dockerfile | 2 + Dockerfiles/nginx.Dockerfile | 10 ++-- docker-compose-standalone.yml | 1 - docker-compose.yml | 1 - docs/authsetup.md | 4 +- docs/development.md | 4 +- docs/opensearch-instances.md | 2 +- docs/quickstart.md | 2 +- docs/ubuntu-install-example.md | 4 +- htadmin/nginx/sites-available/default | 3 +- kubernetes/99-nginx-proxy.yml | 15 ------ .../panel/launcher-18/16343117498.desktop | 2 +- .../share/applications/malcolm-users.desktop | 2 +- nginx/nginx.conf | 52 ++++++++++--------- scripts/build.sh | 2 +- scripts/control.py | 12 ++--- scripts/malcolm_appliance_packager.sh | 2 +- 17 files changed, 57 insertions(+), 63 deletions(-) diff --git a/Dockerfiles/htadmin.Dockerfile b/Dockerfiles/htadmin.Dockerfile index ad7497573..6d13de570 100644 --- a/Dockerfiles/htadmin.Dockerfile +++ b/Dockerfiles/htadmin.Dockerfile @@ -61,6 +61,8 @@ RUN apt-get -q update && \ cd /tmp && \ mkdir -p ./htadmin && \ curl -sSL "$HTADMIN_URL" | tar xzvf - -C ./htadmin --strip-components 1 && \ + find /tmp/htadmin -type f -name index.php -execdir mv index.php htadmin.php \; && \ + find /tmp/htadmin -type f -exec sed -i 's/index.php/htadmin.php/g' "{}" \; && \ mv /tmp/htadmin/sites/html/htadmin /var/www/htadmin && \ cd /var/www/htadmin && \ ( grep -rhoPi "(src|href)=['\"]https?://.+?['\"]" ./includes/* | sed "s/^[a-zA-Z]*=['\"]*//" | sed "s/['\"]$//" | xargs -r -l curl -s -S -L -J -O ) && \ diff --git a/Dockerfiles/nginx.Dockerfile b/Dockerfiles/nginx.Dockerfile index 024101833..5cd0060e1 100644 --- a/Dockerfiles/nginx.Dockerfile +++ b/Dockerfiles/nginx.Dockerfile @@ -87,10 +87,12 @@ ENV NGINX_LDAP_TLS_STUNNEL_CHECK_IP $NGINX_LDAP_TLS_STUNNEL_CHECK_IP ENV NGINX_LDAP_TLS_STUNNEL_VERIFY_LEVEL $NGINX_LDAP_TLS_STUNNEL_VERIFY_LEVEL # build latest nginx with nginx-auth-ldap -ENV NGINX_VERSION=1.20.2 +ENV NGINX_VERSION=1.22.1 ENV NGINX_AUTH_LDAP_BRANCH=master +ENV NGINX_HTTP_SUB_FILTER_BRANCH=master ADD https://codeload.github.com/mmguero-dev/nginx-auth-ldap/tar.gz/$NGINX_AUTH_LDAP_BRANCH /nginx-auth-ldap.tar.gz +ADD https://codeload.github.com/yaoweibin/ngx_http_substitutions_filter_module/tar.gz/$NGINX_HTTP_SUB_FILTER_BRANCH /ngx_http_substitutions_filter_module-master.tar.gz ADD http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz /nginx.tar.gz RUN set -x ; \ @@ -140,6 +142,7 @@ RUN set -x ; \ --with-file-aio \ --with-http_v2_module \ --add-module=/usr/src/nginx-auth-ldap \ + --add-module=/usr/src/ngx_http_substitutions_filter_module \ " ; \ apk update --no-cache; \ apk upgrade --no-cache; \ @@ -166,9 +169,10 @@ RUN set -x ; \ zlib-dev \ ; \ \ - mkdir -p /usr/src/nginx-auth-ldap /www /www/logs/nginx ; \ + mkdir -p /usr/src/nginx-auth-ldap /usr/src/ngx_http_substitutions_filter_module /www /www/logs/nginx ; \ tar -zxC /usr/src -f /nginx.tar.gz ; \ tar -zxC /usr/src/nginx-auth-ldap --strip=1 -f /nginx-auth-ldap.tar.gz ; \ + tar -zxC /usr/src/ngx_http_substitutions_filter_module --strip=1 -f /ngx_http_substitutions_filter_module-master.tar.gz ; \ cd /usr/src/nginx-$NGINX_VERSION ; \ ./configure $CONFIG --with-debug ; \ make -j$(getconf _NPROCESSORS_ONLN) ; \ @@ -216,7 +220,7 @@ RUN set -x ; \ apk del .nginx-build-deps ; \ apk del .gettext ; \ mv /tmp/envsubst /usr/local/bin/ ; \ - rm -rf /usr/src/* /var/tmp/* /var/cache/apk/* /nginx.tar.gz /nginx-auth-ldap.tar.gz; \ + rm -rf /usr/src/* /var/tmp/* /var/cache/apk/* /nginx.tar.gz /nginx-auth-ldap.tar.gz /ngx_http_substitutions_filter_module-master.tar.gz; \ touch /etc/nginx/nginx_ldap.conf /etc/nginx/nginx_blank.conf; COPY --from=jwilder/nginx-proxy:alpine /app/nginx.tmpl /etc/nginx/ diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index f93027835..6be8327b2 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -632,7 +632,6 @@ services: - upload ports: - "0.0.0.0:443:443" - - "0.0.0.0:488:488" - "127.0.0.1:5601:5601" - "127.0.0.1:9200:9200" volumes: diff --git a/docker-compose.yml b/docker-compose.yml index f262700fb..ae2afb004 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -708,7 +708,6 @@ services: - upload ports: - "0.0.0.0:443:443" - - "0.0.0.0:488:488" - "127.0.0.1:5601:5601" - "127.0.0.1:9200:9200" volumes: diff --git a/docs/authsetup.md b/docs/authsetup.md index 4a0fe6a10..e1d051edd 100644 --- a/docs/authsetup.md +++ b/docs/authsetup.md @@ -27,11 +27,11 @@ In either case, you **must** run `./scripts/auth_setup` before starting Malcolm # Local account management -[`auth_setup`](#AuthSetup) is used to define the username and password for the administrator account. Once Malcolm is running, the administrator account can be used to manage other user accounts via a **Malcolm User Management** page served over HTTPS on port 488 (e.g., [https://localhost:488](https://localhost:488) if you are connecting locally). +[`auth_setup`](#AuthSetup) is used to define the username and password for the administrator account. Once Malcolm is running, the administrator account can be used to manage other user accounts via a **Malcolm User Management** page at [https://localhost/auth](https://localhost/auth/) if you are connecting locally) Malcolm user accounts can be used to access the [interfaces](quickstart.md#UserInterfaceURLs) of all of its [components](components.md#Components), including Arkime. Arkime uses its own internal database of user accounts, so when a Malcolm user account logs in to Arkime for the first time Malcolm creates a corresponding Arkime user account automatically. This being the case, it is *not* recommended to use the Arkime **Users** settings page or change the password via the **Password** form under the Arkime **Settings** page, as those settings would not be consistently used across Malcolm. -Users may change their passwords via the **Malcolm User Management** page by clicking **User Self Service**. A forgotten password can also be reset via an emailed link, though this requires SMTP server settings to be specified in `htadmin/config.ini` in the Malcolm installation directory. +Users may change their passwords via the **Malcolm User Management** page by clicking **User Self Service**. ## Lightweight Directory Access Protocol (LDAP) authentication diff --git a/docs/development.md b/docs/development.md index b303fd414..a8829ae46 100644 --- a/docs/development.md +++ b/docs/development.md @@ -90,7 +90,7 @@ Administrator username: analyst analyst password: analyst password (again): -Additional local accounts can be created at https://localhost:488/ when Malcolm is running +Additional local accounts can be created at https://localhost/auth/ when Malcolm is running (Re)generate self-signed certificates for HTTPS access (Y/n): y @@ -129,7 +129,7 @@ A minute or so after starting Malcolm, the following services will be accessible - PCAP upload (web): https://localhost/upload/ - PCAP upload (sftp): sftp://USERNAME@127.0.0.1:8022/files/ - NetBox: https://localhost/netbox/ - - Account management: https://localhost:488/ + - Account management: https://localhost/auth/ - Documentation: https://localhost/readme/ ``` diff --git a/docs/opensearch-instances.md b/docs/opensearch-instances.md index 57427db2a..3c981d5c2 100644 --- a/docs/opensearch-instances.md +++ b/docs/opensearch-instances.md @@ -55,7 +55,7 @@ OpenSearch username: servicedb servicedb password: servicedb password (again): -Additional local accounts can be created at https://localhost:488/ when Malcolm is running +Additional local accounts can be created at https://localhost/auth/ when Malcolm is running Require SSL certificate validation for OpenSearch communication? (Y/n): n diff --git a/docs/quickstart.md b/docs/quickstart.md index c4afa47df..1f1b23933 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -93,4 +93,4 @@ A few minutes after starting Malcolm (probably 5 to 10 minutes for Logstash to b * [Capture File and Log Archive Upload (Web)](upload.md#Upload): [https://localhost/upload/](https://localhost/upload/) * [Capture File and Log Archive Upload (SFTP)](upload.md#Upload): `sftp://@127.0.0.1:8022/files` * [NetBox](asset-interaction-analysis.md#AssetInteractionAnalysis): [https://localhost/netbox/](https://localhost/netbox/) -* [Account Management](authsetup.md#AuthBasicAccountManagement): [https://localhost:488](https://localhost:488) \ No newline at end of file +* [Account Management](authsetup.md#AuthBasicAccountManagement): [https://localhost/auth/](https://localhost/auth/) \ No newline at end of file diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index ecd285fbe..569c0739d 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -224,7 +224,7 @@ Administrator username: analyst analyst password: analyst password (again): -Additional local accounts can be created at https://localhost:488/ when Malcolm is running +Additional local accounts can be created at https://localhost/auth/ when Malcolm is running (Re)generate self-signed certificates for HTTPS access (Y/n): y @@ -293,7 +293,7 @@ In a few minutes, Malcolm services will be accessible via the following URLs: - PCAP upload (web): https://localhost/upload/ - PCAP upload (sftp): sftp://username@127.0.0.1:8022/files/ - NetBox: https://localhost/netbox/ - - Account management: https://localhost:488/ + - Account management: https://localhost/auth/ - Documentation: https://localhost/readme/ NAME COMMAND SERVICE STATUS PORTS diff --git a/htadmin/nginx/sites-available/default b/htadmin/nginx/sites-available/default index 82cb823ee..e68745b15 100644 --- a/htadmin/nginx/sites-available/default +++ b/htadmin/nginx/sites-available/default @@ -2,9 +2,10 @@ server { listen 80 default_server; sendfile on; + gzip off; root /var/www/htadmin; - index index.php index.html index.htm; + index htadmin.php index.html index.htm; server_name htaccess.malcolm.local; diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index aa47eb436..ae7e10d51 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -11,20 +11,6 @@ spec: selector: name: nginx-proxy-deployment ---- -apiVersion: v1 -kind: Service -metadata: - name: nginx-htadmin-proxy - namespace: malcolm -spec: - ports: - - port: 488 - protocol: TCP - selector: - name: nginx-proxy-deployment - - --- apiVersion: apps/v1 kind: Deployment @@ -50,7 +36,6 @@ spec: ports: - containerPort: 443 - containerPort: 8443 - - containerPort: 488 envFrom: - configMapRef: name: process-env diff --git a/malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/panel/launcher-18/16343117498.desktop b/malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/panel/launcher-18/16343117498.desktop index 74b317196..216c3b8a3 100644 --- a/malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/panel/launcher-18/16343117498.desktop +++ b/malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/panel/launcher-18/16343117498.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Version=1.0 Name=Malcolm - User Management -Exec=/opt/firefox/firefox https://localhost:488/ +Exec=/opt/firefox/firefox https://localhost/auth/ Terminal=false X-MultipleArgs=false Type=Application diff --git a/malcolm-iso/config/includes.chroot/usr/share/applications/malcolm-users.desktop b/malcolm-iso/config/includes.chroot/usr/share/applications/malcolm-users.desktop index ea603f854..411365af7 100644 --- a/malcolm-iso/config/includes.chroot/usr/share/applications/malcolm-users.desktop +++ b/malcolm-iso/config/includes.chroot/usr/share/applications/malcolm-users.desktop @@ -1,6 +1,6 @@ Version=1.0 Name=Malcolm - User Management -Exec=/opt/firefox/firefox https://localhost:488/ +Exec=/opt/firefox/firefox https://localhost/auth/ Terminal=false X-MultipleArgs=false Type=Application diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 8c424ae09..05a138c45 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -80,23 +80,39 @@ http { } } - # # htadmin (htpasswd/user management) - # server { - # listen 488; - # include /etc/nginx/nginx_ssl_config.conf; - - # location / { - # proxy_pass http://htadmin; - # proxy_redirect off; - # proxy_set_header Host htadmin.malcolm.local; - # } - # } - # Main web interface server { listen 443; include /etc/nginx/nginx_ssl_config.conf; + # favicon, logos, banners, etc. + include /etc/nginx/nginx_image_aliases.conf; + + # HTTP basic user management (doesn't use nginx_auth_rt as it does its own auth directly) + location /auth { + proxy_pass http://htadmin; + proxy_redirect off; + proxy_set_header Accept-Encoding ""; + proxy_set_header Host htadmin.malcolm.local; + rewrite ^/auth/?(.*) /$1 break; + subs_filter_types '*'; + subs_filter '(src|action|href)="([\w\.-]+\.(php|css|js))' '$1="/auth/$2' gir; + subs_filter 'href="styles/' 'href="/auth/styles/' gi; + subs_filter 'src="script/' 'src="/auth/script/' gi; + subs_filter '/fonts/glyphicons' '/auth/fonts/glyphicons' gi; + } + location ~* ^/(htadmin|admin_login)(\.php)\b(.*) { + proxy_pass http://htadmin/$1$2$3; + proxy_redirect off; + proxy_set_header Accept-Encoding ""; + proxy_set_header Host htadmin.malcolm.local; + subs_filter_types '*'; + subs_filter '(src|action|href)="([\w\.-]+\.(php|css|js))' '$1="/auth/$2' gir; + subs_filter 'href="styles/' 'href="/auth/styles/' gi; + subs_filter 'src="script/' 'src="/auth/script/' gi; + subs_filter '/fonts/glyphicons' '/auth/fonts/glyphicons' gi; + } + # Malcolm readme location /readme { include /etc/nginx/nginx_auth_rt.conf; @@ -104,13 +120,6 @@ http { try_files $uri $uri/index.html; } - location /htadmin { - rewrite ^/htadmin(.*)/?$ /$1 break; - proxy_pass http://htadmin; - proxy_redirect off; - proxy_set_header Host htadmin.malcolm.local; - } - # Malcolm file upload location /upload { include /etc/nginx/nginx_auth_rt.conf; @@ -144,8 +153,6 @@ http { proxy_set_header Host arkime.malcolm.local; } - - # Arkime -> Dashboards shortcut location ~* ^/idark2dash(.*) { include /etc/nginx/nginx_auth_rt.conf; @@ -229,9 +236,6 @@ http { proxy_set_header X-Remote-Auth $authenticated_user; } - # favicon, logos, banners, etc. - include /etc/nginx/nginx_image_aliases.conf; - # Fix cyberchef JS module(s) # https://localhost/arkime/session/190924-KgO9H30qhdREw7ltsDXn1Rgp/modules/Regex.js location ~* ^/arkime/session/.*/(modules/.*\.js) { diff --git a/scripts/build.sh b/scripts/build.sh index c31a36dd1..d6c950864 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -96,7 +96,7 @@ FILES_IN_IMAGES=( "/usr/share/filebeat/filebeat.yml;filebeat-oss" "/var/www/upload/js/jquery.fileupload.js;file-upload" "/opt/freq_server/freq_server.py;freq" - "/var/www/htadmin/index.php;htadmin" + "/var/www/htadmin/htadmin.php;htadmin" "/etc/ip_protocol_name_to_number.yaml;logstash" "/etc/ja3.yaml;logstash" "/etc/vendor_macs.yaml;logstash" diff --git a/scripts/control.py b/scripts/control.py index 72c6ef248..1ef28888f 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -702,7 +702,7 @@ def logs(): print(" - PCAP upload (web): https://localhost/upload/") print(" - PCAP upload (sftp): sftp://username@127.0.0.1:8022/files/") print(" - NetBox: https://localhost/netbox/\n") - print(" - Account management: https://localhost:488/\n") + print(" - Account management: https://localhost/auth/\n") print(" - Documentation: https://localhost/readme/\n") process.poll() @@ -1123,7 +1123,7 @@ def authSetup(wipe=False): f.write('; Change this to customize your title:\n') f.write('app_title = Malcolm User Management\n\n') f.write('; htpasswd file\n') - f.write('secure_path = ./config/htpasswd\n') + f.write('secure_path = ./config/auth/htpasswd\n') f.write('; metadata file\n') f.write('metadata_path = ./config/metadata\n\n') f.write('; administrator user/password (htpasswd -b -c -B ...)\n') @@ -1131,17 +1131,17 @@ def authSetup(wipe=False): f.write('; username field quality checks\n') f.write(';\n') f.write('min_username_len = 4\n') - f.write('max_username_len = 12\n\n') + f.write('max_username_len = 32\n\n') f.write('; Password field quality checks\n') f.write(';\n') - f.write('min_password_len = 6\n') - f.write('max_password_len = 20\n\n') + f.write('min_password_len = 8\n') + f.write('max_password_len = 128\n\n') # touch the metadata file open(os.path.join(MalcolmPath, os.path.join('htadmin', 'metadata')), 'a').close() DisplayMessage( - 'Additional local accounts can be created at https://localhost:488/ when Malcolm is running', + 'Additional local accounts can be created at https://localhost/auth/ when Malcolm is running', ) # generate HTTPS self-signed certificates diff --git a/scripts/malcolm_appliance_packager.sh b/scripts/malcolm_appliance_packager.sh index 260e54d8a..932f303b5 100755 --- a/scripts/malcolm_appliance_packager.sh +++ b/scripts/malcolm_appliance_packager.sh @@ -157,7 +157,7 @@ if mkdir "$DESTDIR"; then echo " - PCAP upload (web): https://localhost/upload/" | tee -a "$README" echo " - PCAP upload (sftp): sftp://USERNAME@127.0.0.1:8022/files/" | tee -a "$README" echo " - NetBox: https://localhost/netbox/" | tee -a "$README" - echo " - Account management: https://localhost:488/" | tee -a "$README" + echo " - Account management: https://localhost/auth/" | tee -a "$README" echo " - Documentation: https://localhost/readme/" | tee -a "$README" popd >/dev/null 2>&1 popd >/dev/null 2>&1 From bd78af7653b219da54d2379a2ee7f41a3a5d13be Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Wed, 12 Apr 2023 16:01:23 -0600 Subject: [PATCH 129/239] rm -rf /tmp/auth/* will fail (as it's read only) so don't bail at that point --- nginx/scripts/docker_entrypoint.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nginx/scripts/docker_entrypoint.sh b/nginx/scripts/docker_entrypoint.sh index 2f63f3aa8..5932d6fa1 100755 --- a/nginx/scripts/docker_entrypoint.sh +++ b/nginx/scripts/docker_entrypoint.sh @@ -238,8 +238,9 @@ EOF fi # basic vs. ldap if [[ ! -f /etc/nginx/auth/htpasswd ]] && [[ -f /tmp/auth/default/htpasswd ]]; then + mkdir -p /etc/nginx/auth/ cp /tmp/auth/default/htpasswd /etc/nginx/auth/ - rm -rf /tmp/auth/* + rm -rf /tmp/auth/* || true fi # start supervisor (which will spawn nginx, stunnel, etc.) or whatever the default command is From 9c006aa246d24c6f442de32f1ccc4a19eaef7c89 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Wed, 12 Apr 2023 16:20:52 -0600 Subject: [PATCH 130/239] we can't nest mohunt points inside a container, make them siblings instead (idaholab/Malcolm#169) --- Dockerfiles/htadmin.Dockerfile | 3 ++- docker-compose-standalone.yml | 2 +- docker-compose.yml | 2 +- docs/contributing-local-modifications.md | 2 +- htadmin/htadmin.sh | 10 ++++++---- htadmin/nginx/sites-available/default | 8 ++++++++ kubernetes/19-htadmin.yml | 8 ++++---- scripts/control.py | 2 +- 8 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Dockerfiles/htadmin.Dockerfile b/Dockerfiles/htadmin.Dockerfile index 6d13de570..5283808a0 100644 --- a/Dockerfiles/htadmin.Dockerfile +++ b/Dockerfiles/htadmin.Dockerfile @@ -67,7 +67,8 @@ RUN apt-get -q update && \ cd /var/www/htadmin && \ ( grep -rhoPi "(src|href)=['\"]https?://.+?['\"]" ./includes/* | sed "s/^[a-zA-Z]*=['\"]*//" | sed "s/['\"]$//" | xargs -r -l curl -s -S -L -J -O ) && \ sed -i "s@http[^'\"]*/@@gI" ./includes/* && \ - mkdir fonts && cd fonts && \ + mkdir fonts config auth default && \ + cd fonts && \ curl -s -S -L -J -O "https://maxcdn.bootstrapcdn.com/bootstrap/$BOOTSTRAP_VERSION/fonts/glyphicons-halflings-regular.ttf" && \ curl -s -S -L -J -O "https://maxcdn.bootstrapcdn.com/bootstrap/$BOOTSTRAP_VERSION/fonts/glyphicons-halflings-regular.woff" && \ curl -s -S -L -J -O "https://maxcdn.bootstrapcdn.com/bootstrap/$BOOTSTRAP_VERSION/fonts/glyphicons-halflings-regular.woff2" && \ diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 6be8327b2..762c4af0c 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -445,7 +445,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./htadmin/config.ini:/var/www/htadmin/config/config.ini:rw - ./htadmin/metadata:/var/www/htadmin/config/metadata:rw - - ./nginx/htpasswd:/var/www/htadmin/config/auth/htpasswd:rw + - ./nginx/htpasswd:/var/www/htadmin/auth/htpasswd:rw healthcheck: test: ["CMD", "curl", "--silent", "--fail", "http://localhost"] interval: 60s diff --git a/docker-compose.yml b/docker-compose.yml index ae2afb004..ec5c7a246 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -499,7 +499,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./htadmin/config.ini:/var/www/htadmin/config/config.ini:rw - ./htadmin/metadata:/var/www/htadmin/config/metadata:rw - - ./nginx/htpasswd:/var/www/htadmin/config/auth/htpasswd:rw + - ./nginx/htpasswd:/var/www/htadmin/auth/htpasswd:rw healthcheck: test: ["CMD", "curl", "--silent", "--fail", "http://localhost"] interval: 60s diff --git a/docs/contributing-local-modifications.md b/docs/contributing-local-modifications.md index df1eba5d1..e8cf2b563 100644 --- a/docs/contributing-local-modifications.md +++ b/docs/contributing-local-modifications.md @@ -83,7 +83,7 @@ $ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml - ./nginx/ca-trust:/var/local/ca-trust:ro - ./htadmin/config.ini:/var/www/htadmin/config/config.ini:rw - ./htadmin/metadata:/var/www/htadmin/config/metadata:rw - - ./nginx/htpasswd:/var/www/htadmin/config/htpasswd:rw + - ./nginx/htpasswd:/var/www/htadmin/auth/htpasswd:rw freq: - ./nginx/ca-trust:/var/local/ca-trust:ro api: diff --git a/htadmin/htadmin.sh b/htadmin/htadmin.sh index 0d844ed99..b1c5002c2 100644 --- a/htadmin/htadmin.sh +++ b/htadmin/htadmin.sh @@ -2,12 +2,14 @@ HTADMIN_ENABLED=${NGINX_BASIC_AUTH:-"true"} -if [[ ! -f /var/www/htadmin/config/config.ini ]]; then - cp /var/www/htadmin/config/default/config.ini /var/www/htadmin/config/config.ini +if [[ ! -f /var/www/htadmin/config/config.ini ]] && [[ -f /var/www/htadmin/default/config.ini ]]; then + mkdir -p /var/www/htadmin/config/ + cp /var/www/htadmin/default/config.ini /var/www/htadmin/config/config.ini fi -if [[ ! -f /var/www/htadmin/config/metadata ]]; then - cp /var/www/htadmin/config/default/metadata /var/www/htadmin/config/metadata +if [[ ! -f /var/www/htadmin/config/metadata ]] && [[ -f /var/www/htadmin/default/metadata ]]; then + mkdir -p /var/www/htadmin/config/ + cp /var/www/htadmin/default/metadata /var/www/htadmin/config/metadata fi if [[ "$HTADMIN_ENABLED" == "true" ]]; then diff --git a/htadmin/nginx/sites-available/default b/htadmin/nginx/sites-available/default index e68745b15..7af8821b3 100644 --- a/htadmin/nginx/sites-available/default +++ b/htadmin/nginx/sites-available/default @@ -19,8 +19,16 @@ server { fastcgi_pass unix:/run/php/php7.4-fpm.sock; } + location /auth { + deny all; + return 404; + } location /config { deny all; return 404; } + location /default { + deny all; + return 404; + } } \ No newline at end of file diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml index b988935fb..d4f30e45c 100644 --- a/kubernetes/19-htadmin.yml +++ b/kubernetes/19-htadmin.yml @@ -48,12 +48,12 @@ spec: volumeMounts: - mountPath: /var/local/ca-trust/configmap name: htadmin-var-local-catrust-volume - - mountPath: /var/www/htadmin/config/auth + - mountPath: /var/www/htadmin/auth name: htadmin-config-volume subPath: "auth" - - mountPath: /var/www/htadmin/config/default/configmap - name: htadmin-config-default-volume - - mountPath: /var/www/htadmin/config/ + - mountPath: /var/www/htadmin/default/configmap + name: htadmin-config-default-volume + - mountPath: /var/www/htadmin/config name: htadmin-config-volume subPath: "htadmin" volumes: diff --git a/scripts/control.py b/scripts/control.py index 1ef28888f..9ac44fc5a 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -1123,7 +1123,7 @@ def authSetup(wipe=False): f.write('; Change this to customize your title:\n') f.write('app_title = Malcolm User Management\n\n') f.write('; htpasswd file\n') - f.write('secure_path = ./config/auth/htpasswd\n') + f.write('secure_path = ./auth/htpasswd\n') f.write('; metadata file\n') f.write('metadata_path = ./config/metadata\n\n') f.write('; administrator user/password (htpasswd -b -c -B ...)\n') From 0e9cbdbbf720846354a3932eb66ca4f297db676e Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Wed, 12 Apr 2023 16:49:51 -0600 Subject: [PATCH 131/239] fix htadmin build --- Dockerfiles/htadmin.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/htadmin.Dockerfile b/Dockerfiles/htadmin.Dockerfile index 5283808a0..35c3728e9 100644 --- a/Dockerfiles/htadmin.Dockerfile +++ b/Dockerfiles/htadmin.Dockerfile @@ -67,7 +67,7 @@ RUN apt-get -q update && \ cd /var/www/htadmin && \ ( grep -rhoPi "(src|href)=['\"]https?://.+?['\"]" ./includes/* | sed "s/^[a-zA-Z]*=['\"]*//" | sed "s/['\"]$//" | xargs -r -l curl -s -S -L -J -O ) && \ sed -i "s@http[^'\"]*/@@gI" ./includes/* && \ - mkdir fonts config auth default && \ + mkdir -p fonts config auth default && \ cd fonts && \ curl -s -S -L -J -O "https://maxcdn.bootstrapcdn.com/bootstrap/$BOOTSTRAP_VERSION/fonts/glyphicons-halflings-regular.ttf" && \ curl -s -S -L -J -O "https://maxcdn.bootstrapcdn.com/bootstrap/$BOOTSTRAP_VERSION/fonts/glyphicons-halflings-regular.woff" && \ From 1390d78eaebb6c586ea31d64aafa5420084580ec Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 07:16:28 -0600 Subject: [PATCH 132/239] I've been getting new crashes with filebeat I've never seen before, revert update to beats v8.7.0 for now ---- panic: runtime error: index out of range [1] with length 0 goroutine 101 [running]: github.com/elastic/beats/v7/filebeat/input/tcp.procNetTCP({0xc000452fb0?, 0x1, 0x1}) github.com/elastic/beats/v7/filebeat/input/tcp/input.go:277 +0x47b github.com/elastic/beats/v7/filebeat/input/tcp.(*inputMetrics).poll(0xc000503d50, {0xc000452fb0, 0x1, 0x1}, 0x0?, 0xc000452d80) github.com/elastic/beats/v7/filebeat/input/tcp/input.go:249 +0xd1 created by github.com/elastic/beats/v7/filebeat/input/tcp.newInputMetrics ---- Revert "bump beats to v8.7.0 (https://www.elastic.co/guide/en/beats/libbeat/current/release-notes-8.7.0.html)" This reverts commit c739914fbf6efcd624d733fc9ae153ed75bf2932. --- Dockerfiles/filebeat.Dockerfile | 2 +- sensor-iso/beats/Dockerfile | 2 +- sensor-iso/beats/beat-build.sh | 2 +- sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index f2643ba34..e63eac2e8 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -1,4 +1,4 @@ -FROM docker.elastic.co/beats/filebeat-oss:8.7.0 +FROM docker.elastic.co/beats/filebeat-oss:8.6.2 # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. LABEL maintainer="malcolm@inl.gov" diff --git a/sensor-iso/beats/Dockerfile b/sensor-iso/beats/Dockerfile index eb89a777d..5126c1bb3 100644 --- a/sensor-iso/beats/Dockerfile +++ b/sensor-iso/beats/Dockerfile @@ -41,7 +41,7 @@ RUN set -x && \ go run bootstrap.go ENV BEATS=filebeat -ENV BEATS_VERSION=8.7.0 +ENV BEATS_VERSION=8.6.2 ADD ./build.sh /build.sh RUN [ "chmod", "+x", "/build.sh" ] diff --git a/sensor-iso/beats/beat-build.sh b/sensor-iso/beats/beat-build.sh index e2282a8b5..63ada694c 100755 --- a/sensor-iso/beats/beat-build.sh +++ b/sensor-iso/beats/beat-build.sh @@ -2,7 +2,7 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. -VERSION="8.7.0" +VERSION="8.6.0" THIRD_PARTY_BRANCH="master" while getopts b:v:t: opts; do case ${opts} in diff --git a/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot b/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot index 01d1a807a..d9fdc9deb 100755 --- a/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot +++ b/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot @@ -20,7 +20,7 @@ export PATH="${ZEEK_DIR}"/bin:$PATH SURICATA_RULES_DIR="/etc/suricata/rules" -BEATS_VER="8.7.0" +BEATS_VER="8.6.2" BEATS_OSS="-oss" BEATS_DEB_URL_TEMPLATE_REPLACER="XXXXX" BEATS_DEB_URL_TEMPLATE="https://artifacts.elastic.co/downloads/beats/$BEATS_DEB_URL_TEMPLATE_REPLACER/$BEATS_DEB_URL_TEMPLATE_REPLACER$BEATS_OSS-$BEATS_VER-amd64.deb" From 9767fbd890aee9044702b654f18f9dfb3528aa52 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 07:16:28 -0600 Subject: [PATCH 133/239] I've been getting new crashes with filebeat I've never seen before, revert update to beats v8.7.0 for now ---- panic: runtime error: index out of range [1] with length 0 goroutine 101 [running]: github.com/elastic/beats/v7/filebeat/input/tcp.procNetTCP({0xc000452fb0?, 0x1, 0x1}) github.com/elastic/beats/v7/filebeat/input/tcp/input.go:277 +0x47b github.com/elastic/beats/v7/filebeat/input/tcp.(*inputMetrics).poll(0xc000503d50, {0xc000452fb0, 0x1, 0x1}, 0x0?, 0xc000452d80) github.com/elastic/beats/v7/filebeat/input/tcp/input.go:249 +0xd1 created by github.com/elastic/beats/v7/filebeat/input/tcp.newInputMetrics ---- Revert "bump beats to v8.7.0 (https://www.elastic.co/guide/en/beats/libbeat/current/release-notes-8.7.0.html)" This reverts commit c739914fbf6efcd624d733fc9ae153ed75bf2932. --- Dockerfiles/filebeat.Dockerfile | 2 +- sensor-iso/beats/Dockerfile | 2 +- sensor-iso/beats/beat-build.sh | 2 +- sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index c68695a0e..3090e047e 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -1,4 +1,4 @@ -FROM docker.elastic.co/beats/filebeat-oss:8.7.0 +FROM docker.elastic.co/beats/filebeat-oss:8.6.2 # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. LABEL maintainer="malcolm@inl.gov" diff --git a/sensor-iso/beats/Dockerfile b/sensor-iso/beats/Dockerfile index eb89a777d..5126c1bb3 100644 --- a/sensor-iso/beats/Dockerfile +++ b/sensor-iso/beats/Dockerfile @@ -41,7 +41,7 @@ RUN set -x && \ go run bootstrap.go ENV BEATS=filebeat -ENV BEATS_VERSION=8.7.0 +ENV BEATS_VERSION=8.6.2 ADD ./build.sh /build.sh RUN [ "chmod", "+x", "/build.sh" ] diff --git a/sensor-iso/beats/beat-build.sh b/sensor-iso/beats/beat-build.sh index e2282a8b5..63ada694c 100755 --- a/sensor-iso/beats/beat-build.sh +++ b/sensor-iso/beats/beat-build.sh @@ -2,7 +2,7 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. -VERSION="8.7.0" +VERSION="8.6.0" THIRD_PARTY_BRANCH="master" while getopts b:v:t: opts; do case ${opts} in diff --git a/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot b/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot index 01d1a807a..d9fdc9deb 100755 --- a/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot +++ b/sensor-iso/config/hooks/normal/0910-sensor-build.hook.chroot @@ -20,7 +20,7 @@ export PATH="${ZEEK_DIR}"/bin:$PATH SURICATA_RULES_DIR="/etc/suricata/rules" -BEATS_VER="8.7.0" +BEATS_VER="8.6.2" BEATS_OSS="-oss" BEATS_DEB_URL_TEMPLATE_REPLACER="XXXXX" BEATS_DEB_URL_TEMPLATE="https://artifacts.elastic.co/downloads/beats/$BEATS_DEB_URL_TEMPLATE_REPLACER/$BEATS_DEB_URL_TEMPLATE_REPLACER$BEATS_OSS-$BEATS_VER-amd64.deb" From e32b802afe0bf417eea0d3183844052f735055b1 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 07:51:33 -0600 Subject: [PATCH 134/239] make sure htadmin/nginx auth files get created with correct ownership (idaholab/Malcolm#169) --- Dockerfiles/nginx.Dockerfile | 3 +-- htadmin/htadmin.sh | 36 +++++++++---------------- nginx/scripts/docker_entrypoint.sh | 5 ++-- shared/bin/service_check_passthrough.sh | 2 +- 4 files changed, 18 insertions(+), 28 deletions(-) diff --git a/Dockerfiles/nginx.Dockerfile b/Dockerfiles/nginx.Dockerfile index 5cd0060e1..31ca37f55 100644 --- a/Dockerfiles/nginx.Dockerfile +++ b/Dockerfiles/nginx.Dockerfile @@ -186,8 +186,7 @@ RUN set -x ; \ make -j$(getconf _NPROCESSORS_ONLN) ; \ make install ; \ rm -rf /etc/nginx/html/ ; \ - mkdir -p /etc/nginx/conf.d/ ; \ - mkdir -p /usr/share/nginx/html/ ; \ + mkdir -p /etc/nginx/conf.d/ /etc/nginx/auth/ /usr/share/nginx/html/ ; \ install -m644 html/index.html /usr/share/nginx/html/ ; \ install -m644 html/50x.html /usr/share/nginx/html/ ; \ install -m755 objs/nginx-debug /usr/sbin/nginx-debug ; \ diff --git a/htadmin/htadmin.sh b/htadmin/htadmin.sh index b1c5002c2..9bbbde6b2 100644 --- a/htadmin/htadmin.sh +++ b/htadmin/htadmin.sh @@ -1,32 +1,22 @@ #!/usr/bin/env bash -HTADMIN_ENABLED=${NGINX_BASIC_AUTH:-"true"} +if [[ "${NGINX_BASIC_AUTH:-true}" == "true" ]]; then -if [[ ! -f /var/www/htadmin/config/config.ini ]] && [[ -f /var/www/htadmin/default/config.ini ]]; then - mkdir -p /var/www/htadmin/config/ - cp /var/www/htadmin/default/config.ini /var/www/htadmin/config/config.ini -fi + if [[ ! -f /var/www/htadmin/config/config.ini ]] && [[ -f /var/www/htadmin/default/config.ini ]]; then + cp /var/www/htadmin/default/config.ini /var/www/htadmin/config/config.ini + [[ -n ${PUID} ]] && chown -f ${PUID} /var/www/htadmin/config/config.ini + [[ -n ${PGID} ]] && chown -f :${PGID} /var/www/htadmin/config/config.ini + fi -if [[ ! -f /var/www/htadmin/config/metadata ]] && [[ -f /var/www/htadmin/default/metadata ]]; then - mkdir -p /var/www/htadmin/config/ - cp /var/www/htadmin/default/metadata /var/www/htadmin/config/metadata -fi + if [[ ! -f /var/www/htadmin/config/metadata ]] && [[ -f /var/www/htadmin/default/metadata ]]; then + cp /var/www/htadmin/default/metadata /var/www/htadmin/config/metadata + [[ -n ${PUID} ]] && chown -f ${PUID} /var/www/htadmin/config/metadata + [[ -n ${PGID} ]] && chown -f :${PGID} /var/www/htadmin/config/metadata + fi -if [[ "$HTADMIN_ENABLED" == "true" ]]; then sleep 10 nginx -g "daemon off;" + else - mkdir -p /tmp/htadmin_disabled - pushd /tmp/htadmin_disabled >/dev/null 2>&1 && \ - cat << EOF > index.html - -

Basic Authentication Disabled
- -

Basic HTTP authentication has been disabled.

-

Refer to the Malcolm documentation for details on LDAP authentication.

- - -EOF - python3 -m http.server 80 - popd >/dev/null 2>&1 + /usr/local/bin/service_check_passthrough.sh -d -s htadmin -p 80 -f http fi diff --git a/nginx/scripts/docker_entrypoint.sh b/nginx/scripts/docker_entrypoint.sh index 5932d6fa1..95a7dd83a 100755 --- a/nginx/scripts/docker_entrypoint.sh +++ b/nginx/scripts/docker_entrypoint.sh @@ -238,8 +238,9 @@ EOF fi # basic vs. ldap if [[ ! -f /etc/nginx/auth/htpasswd ]] && [[ -f /tmp/auth/default/htpasswd ]]; then - mkdir -p /etc/nginx/auth/ - cp /tmp/auth/default/htpasswd /etc/nginx/auth/ + cp /tmp/auth/default/htpasswd /etc/nginx/auth/htpasswd + [[ -n ${PUID} ]] && chown -f ${PUID} /etc/nginx/auth/htpasswd + [[ -n ${PGID} ]] && chown -f :${PGID} /etc/nginx/auth/htpasswd rm -rf /tmp/auth/* || true fi diff --git a/shared/bin/service_check_passthrough.sh b/shared/bin/service_check_passthrough.sh index 769994c1b..80a3142e8 100755 --- a/shared/bin/service_check_passthrough.sh +++ b/shared/bin/service_check_passthrough.sh @@ -51,7 +51,7 @@ while getopts 'vds:p:f:' OPTION; do ;; ?) - echo "script usage: $(basename $0) [-v] [-i input]" >&2 + echo "script usage: $(basename $0) [-v (verbose)] [-d (disabled)] [-s ] [-p ] [-f ]" >&2 exit 1 ;; esac From 5483b322df5123b60c962eab49c8c3c47c516029 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 08:03:08 -0600 Subject: [PATCH 135/239] don't build ISO workflows in kubernetes branch --- .github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml | 1 - .github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml b/.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml index c95a3ffd3..fc9a44507 100644 --- a/.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml +++ b/.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml @@ -5,7 +5,6 @@ on: branches: - main - development - - kubernetes paths: - 'malcolm-iso/**' - 'shared/bin/*' diff --git a/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml b/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml index fd9499911..ef45d02cb 100644 --- a/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml +++ b/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml @@ -5,7 +5,6 @@ on: branches: - main - development - - kubernetes paths: - 'sensor-iso/**' - 'shared/bin/*' From 91ebb55581f1e3cbd9bcee0d778aeaacf145b6e1 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 08:46:14 -0600 Subject: [PATCH 136/239] give option for all three authentication modes --- config/filebeat.env.example | 6 +++--- docs/malcolm-hedgehog-e2e-iso-install.md | 4 ++-- docs/ubuntu-install-example.md | 5 ++++- kubernetes/02-opensearch.yml | 2 -- scripts/install.py | 25 ++++++++++++++++-------- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/config/filebeat.env.example b/config/filebeat.env.example index e0c9d9bda..3091946ef 100644 --- a/config/filebeat.env.example +++ b/config/filebeat.env.example @@ -17,14 +17,14 @@ FILEBEAT_WATCHER_POLLING_ASSUME_CLOSED_SEC=10 # https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-tcp.html) FILEBEAT_TCP_LISTEN=false # Log format expected for events sent to the filebeat TCP input listener ('json' or 'raw') -FILEBEAT_TCP_LOG_FORMAT=raw +FILEBEAT_TCP_LOG_FORMAT=json # Source field name to parse (when FILEBEAT_TCP_LOG_FORMAT is 'json') for events sent to the # filebeat TCP input listener FILEBEAT_TCP_PARSE_SOURCE_FIELD=message # Target field name to store decoded JSON fields (when FILEBEAT_TCP_LOG_FORMAT is 'json') for # events sent to the filebeat TCP input listener -FILEBEAT_TCP_PARSE_TARGET_FIELD= +FILEBEAT_TCP_PARSE_TARGET_FIELD=miscbeat # Name of field to drop (if it exists) in events sent to the filebeat TCP input listener -FILEBEAT_TCP_PARSE_DROP_FIELD= +FILEBEAT_TCP_PARSE_DROP_FIELD=message # Tag to append to events sent to the filebeat TCP input listener FILEBEAT_TCP_TAG=_malcolm_beats \ No newline at end of file diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md index d97e1f8b2..7303d9ad4 100644 --- a/docs/malcolm-hedgehog-e2e-iso-install.md +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -159,8 +159,8 @@ The [configuration and tuning](malcolm-config.md#ConfigAndTuning) wizard's quest - See the previous question. If Malcolm is configured behind a remote proxy, Malcolm can prompt you to *Configure labels for Traefik?* to allow it to identify itself to Traefik. * Specify external Docker network name (or leave blank for default networking) - This allows you to configure Malcolm to use [custom Docker networks](https://docs.docker.com/compose/networking/#specify-custom-networks). Leave this blank unless you know you want to do otherwise. -* Authenticate against Lightweight Directory Access Protocol (LDAP) server? - - Answer **N** to use Malcolm's own built-in [local account management](authsetup.md#AuthBasicAccountManagement), or **Y** to use [Lightweight Directory Access Protocol (LDAP) authentication](authsetup.md#AuthLDAP). +* Select authentication method + - Choose **Basic** to use Malcolm's own built-in [local account management](authsetup.md#AuthBasicAccountManagement), **LDAP** to use [Lightweight Directory Access Protocol (LDAP) authentication](authsetup.md#AuthLDAP) or **None** to not require authentication (not recommended) * Select LDAP server compatibility type - This question allows you to specify Microsoft Active Directory compatibility (**winldap**) or generic LDAP compatibility (**openldap**, for OpenLDAP, glauth, etc.) when using [LDAP authentication](authsetup.md#AuthLDAP) * Use StartTLS (rather than LDAPS) for LDAP connection security? diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index 569c0739d..68cca9911 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -121,7 +121,10 @@ Will Malcolm be running behind another reverse proxy (Traefik, Caddy, etc.)? (y/ Specify external Docker network name (or leave blank for default networking) (): -Authenticate against Lightweight Directory Access Protocol (LDAP) server? (y/N): n +1: Basic +2: Lightweight Directory Access Protocol (LDAP) +3: None +Select authentication method (Basic): 1 Store PCAP, log and index files locally under /home/user/Malcolm? (Y/n): y diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index c2e8610dc..58084bdf7 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -47,8 +47,6 @@ spec: - configMapRef: name: opensearch-env env: - - name: OPENSEARCH_DISABLED - value: "true" - name: VIRTUAL_HOST value: "os.malcolm.local" volumeMounts: diff --git a/scripts/install.py b/scripts/install.py index f71d72dcf..b219fe4aa 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -562,12 +562,21 @@ def tweak_malcolm_runtime( 'Specify external Docker network name (or leave blank for default networking)', default="" ) + allowedAuthModes = { + 'Basic': 'true', + 'Lightweight Directory Access Protocol (LDAP)': 'false', + 'None': 'no_authentication', + } + authMode = None + while authMode not in list(allowedAuthModes.keys()): + authMode = InstallerChooseOne( + 'Select authentication method', + choices=[(x, '', x == 'Basic') for x in list(allowedAuthModes.keys())], + ) + ldapStartTLS = False ldapServerType = 'winldap' - useBasicAuth = not InstallerYesOrNo( - 'Authenticate against Lightweight Directory Access Protocol (LDAP) server?', default=False - ) - if not useBasicAuth: + if 'ldap' in authMode.lower(): allowedLdapModes = ('winldap', 'openldap') ldapServerType = None while ldapServerType not in allowedLdapModes: @@ -584,7 +593,7 @@ def tweak_malcolm_runtime( ) as ldapDefaultsFile: print(f"LDAP_SERVER_TYPE='{ldapServerType}'", file=ldapDefaultsFile) print( - f"LDAP_PROTO='{'ldap://' if useBasicAuth or ldapStartTLS else 'ldaps://'}'", + f"LDAP_PROTO='{'ldap://' if ldapStartTLS else 'ldaps://'}'", file=ldapDefaultsFile, ) print(f"LDAP_PORT='{3268 if ldapStartTLS else 3269}'", file=ldapDefaultsFile) @@ -939,17 +948,17 @@ def tweak_malcolm_runtime( 'MANAGE_PCAP_FILES', TrueOrFalseNoQuote(arkimeManagePCAP), ), - # basic (useBasicAuth=True) vs ldap (useBasicAuth=False) + # authentication method: basic (true), ldap (false) or no_authentication EnvValue( os.path.join(args.configDir, 'auth-common.env'), 'NGINX_BASIC_AUTH', - TrueOrFalseNoQuote(useBasicAuth), + allowedAuthModes.get(authMode, TrueOrFalseNoQuote(True)), ), # StartTLS vs. ldap:// or ldaps:// EnvValue( os.path.join(args.configDir, 'auth-common.env'), 'NGINX_LDAP_TLS_STUNNEL', - TrueOrFalseNoQuote(((not useBasicAuth) and ldapStartTLS)), + TrueOrFalseNoQuote(('ldap' in authMode.lower()) and ldapStartTLS), ), # turn on dark mode, or not EnvValue( From 65af50c4f58f8b5591523551413f1b368770cf2b Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 10:27:33 -0600 Subject: [PATCH 137/239] Added SYS_RESOURCE cap --- kubernetes/02-opensearch.yml | 3 ++- kubernetes/13-logstash.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index 58084bdf7..0757a443f 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -36,7 +36,8 @@ spec: securityContext: capabilities: add: - - IPC_LOCK + - IPC_LOCK + - SYS_RESOURCE ports: - containerPort: 9200 envFrom: diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index ab22874bc..21f161356 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -40,7 +40,8 @@ spec: securityContext: capabilities: add: - - IPC_LOCK + - IPC_LOCK + - SYS_RESOURCE ports: - containerPort: 5044 - containerPort: 9600 From 17629874e3fbdb36c0227c648a864bd50fd9152e Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 10:54:16 -0600 Subject: [PATCH 138/239] set rlimits on opensearch/logstash startup --- Dockerfiles/logstash.Dockerfile | 1 + Dockerfiles/opensearch.Dockerfile | 1 + shared/bin/docker-uid-gid-setup.sh | 10 ++++++++++ 3 files changed, 12 insertions(+) diff --git a/Dockerfiles/logstash.Dockerfile b/Dockerfiles/logstash.Dockerfile index bb4bbbc96..df3590061 100644 --- a/Dockerfiles/logstash.Dockerfile +++ b/Dockerfiles/logstash.Dockerfile @@ -16,6 +16,7 @@ ENV DEFAULT_GID $DEFAULT_GID ENV PUSER "logstash" ENV PGROUP "logstash" ENV PUSER_PRIV_DROP true +ENV PUSER_RLIMIT_UNLOCK true ENV DEBIAN_FRONTEND noninteractive ENV TERM xterm diff --git a/Dockerfiles/opensearch.Dockerfile b/Dockerfiles/opensearch.Dockerfile index d295ee392..19c72fda6 100644 --- a/Dockerfiles/opensearch.Dockerfile +++ b/Dockerfiles/opensearch.Dockerfile @@ -18,6 +18,7 @@ ENV PUID $DEFAULT_UID ENV PUSER "opensearch" ENV PGROUP "opensearch" ENV PUSER_PRIV_DROP true +ENV PUSER_RLIMIT_UNLOCK true ENV TERM xterm diff --git a/shared/bin/docker-uid-gid-setup.sh b/shared/bin/docker-uid-gid-setup.sh index f94cbf320..9eec2e5da 100755 --- a/shared/bin/docker-uid-gid-setup.sh +++ b/shared/bin/docker-uid-gid-setup.sh @@ -1,5 +1,15 @@ #!/bin/bash +if [[ "${PUSER_RLIMIT_UNLOCK:-false}" == "true" ]] && command -v ulimit >/dev/null 2>&1; then + ulimit -c 0 >/dev/null 2>&1 + ulimit -l unlimited >/dev/null 2>&1 + ulimit -m unlimited >/dev/null 2>&1 + ulimit -v unlimited >/dev/null 2>&1 + ulimit -x unlimited >/dev/null 2>&1 + ulimit -n 65535 >/dev/null 2>&1 + ulimit -u 262144 >/dev/null 2>&1 +fi + set -e unset ENTRYPOINT_CMD From 72ad8dc542666f0633e449384d654b44c5f2f794 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 11:14:25 -0600 Subject: [PATCH 139/239] set rlimits on opensearch/logstash startup --- shared/bin/docker-uid-gid-setup.sh | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/shared/bin/docker-uid-gid-setup.sh b/shared/bin/docker-uid-gid-setup.sh index 9eec2e5da..8f460d4b9 100755 --- a/shared/bin/docker-uid-gid-setup.sh +++ b/shared/bin/docker-uid-gid-setup.sh @@ -1,15 +1,5 @@ #!/bin/bash -if [[ "${PUSER_RLIMIT_UNLOCK:-false}" == "true" ]] && command -v ulimit >/dev/null 2>&1; then - ulimit -c 0 >/dev/null 2>&1 - ulimit -l unlimited >/dev/null 2>&1 - ulimit -m unlimited >/dev/null 2>&1 - ulimit -v unlimited >/dev/null 2>&1 - ulimit -x unlimited >/dev/null 2>&1 - ulimit -n 65535 >/dev/null 2>&1 - ulimit -u 262144 >/dev/null 2>&1 -fi - set -e unset ENTRYPOINT_CMD @@ -65,8 +55,9 @@ else CONFIG_MAP_FIND_PRUNE_ARGS=() fi # check for CONFIG_MAP_DIR and rsync -# change user/group ownership of any files/directories belonging to the original IDs set +e + +# change user/group ownership of any files/directories belonging to the original IDs if [[ -n ${PUID} ]] && [[ "${PUID}" != "${DEFAULT_UID}" ]]; then find / -path /sys -prune -o -path /proc -prune -o -user ${DEFAULT_UID} -exec chown -f ${PUID} "{}" \; 2>/dev/null fi @@ -136,7 +127,6 @@ if [[ -n ${PUSER_CA_TRUST} ]] && command -v openssl >/dev/null 2>&1; then command -v update-ca-certificates >/dev/null 2>&1 && update-ca-certificates >/dev/null 2>&1 command -v update-ca-trust >/dev/null 2>&1 && update-ca-trust extract >/dev/null 2>&1 fi -set -e # determine if we are now dropping privileges to exec ENTRYPOINT_CMD if [[ "$PUSER_PRIV_DROP" == "true" ]]; then @@ -153,8 +143,17 @@ export USER="${EXEC_USER}" export HOME="${USER_HOME}" whoami id -if [ ! -z "${ENTRYPOINT_CMD}" ]; then - if [ -z "${ENTRYPOINT_ARGS}" ]; then +if [[ "${PUSER_RLIMIT_UNLOCK:-false}" == "true" ]] && command -v ulimit >/dev/null 2>&1; then + ulimit -c 0 >/dev/null 2>&1 + ulimit -l unlimited >/dev/null 2>&1 + ulimit -m unlimited >/dev/null 2>&1 + ulimit -v unlimited >/dev/null 2>&1 + ulimit -x unlimited >/dev/null 2>&1 + ulimit -n 65535 >/dev/null 2>&1 + ulimit -u 262144 >/dev/null 2>&1 +fi +if [[ ! -z "${ENTRYPOINT_CMD}" ]]; then + if [[ -z "${ENTRYPOINT_ARGS}" ]]; then "${ENTRYPOINT_CMD}" else "${ENTRYPOINT_CMD}" $(printf "%q " "${ENTRYPOINT_ARGS[@]}") From 01847fc90a2205cdf87700075a34890bae609f52 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 11:25:01 -0600 Subject: [PATCH 140/239] set rlimits on opensearch/logstash startup --- shared/bin/docker-uid-gid-setup.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/bin/docker-uid-gid-setup.sh b/shared/bin/docker-uid-gid-setup.sh index 8f460d4b9..17812b8be 100755 --- a/shared/bin/docker-uid-gid-setup.sh +++ b/shared/bin/docker-uid-gid-setup.sh @@ -151,6 +151,7 @@ if [[ "${PUSER_RLIMIT_UNLOCK:-false}" == "true" ]] && command -v ulimit >/dev/nu ulimit -x unlimited >/dev/null 2>&1 ulimit -n 65535 >/dev/null 2>&1 ulimit -u 262144 >/dev/null 2>&1 + ulimit -a fi if [[ ! -z "${ENTRYPOINT_CMD}" ]]; then if [[ -z "${ENTRYPOINT_ARGS}" ]]; then From 3e7f732eb398b24764dbc4deb749d50198a4ef24 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 11:28:22 -0600 Subject: [PATCH 141/239] set rlimits on opensearch/logstash startup --- shared/bin/docker-uid-gid-setup.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/shared/bin/docker-uid-gid-setup.sh b/shared/bin/docker-uid-gid-setup.sh index 17812b8be..3b95b2399 100755 --- a/shared/bin/docker-uid-gid-setup.sh +++ b/shared/bin/docker-uid-gid-setup.sh @@ -1,5 +1,16 @@ #!/bin/bash +# attempt to set ulimits (as root) +if [[ "${PUSER_RLIMIT_UNLOCK:-false}" == "true" ]] && command -v ulimit >/dev/null 2>&1; then + ulimit -c 0 >/dev/null 2>&1 + ulimit -l unlimited >/dev/null 2>&1 + ulimit -m unlimited >/dev/null 2>&1 + ulimit -v unlimited >/dev/null 2>&1 + ulimit -x unlimited >/dev/null 2>&1 + ulimit -n 65535 >/dev/null 2>&1 + ulimit -u 262144 >/dev/null 2>&1 +fi + set -e unset ENTRYPOINT_CMD @@ -137,7 +148,7 @@ else USER_HOME="${HOME:-/root}" fi -# execute the entrypoint command specified +# attempt to set ulimits (as user) and execute the entrypoint command specified su -s /bin/bash -p ${EXEC_USER} << EOF export USER="${EXEC_USER}" export HOME="${USER_HOME}" From 6c0b8041933f83db4e9d437b5c31cdbc24d6202d Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 11:46:09 -0600 Subject: [PATCH 142/239] set resource capabilities on containers --- Dockerfiles/arkime.Dockerfile | 1 + Dockerfiles/pcap-capture.Dockerfile | 1 + Dockerfiles/suricata.Dockerfile | 1 + Dockerfiles/zeek.Dockerfile | 1 + kubernetes/06-arkime.yml | 5 +++++ kubernetes/09-zeek.yml | 9 +++++---- kubernetes/10-suricata.yml | 9 +++++---- kubernetes/20-pcap-capture.yml | 9 +++++---- kubernetes/21-zeek-live.yml | 9 +++++---- kubernetes/22-suricata-live.yml | 9 +++++---- 10 files changed, 34 insertions(+), 20 deletions(-) diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index 8186571f0..1b35074c2 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -89,6 +89,7 @@ ENV DEFAULT_GID $DEFAULT_GID ENV PUSER "arkime" ENV PGROUP "arkime" ENV PUSER_PRIV_DROP true +ENV PUSER_RLIMIT_UNLOCK true ENV DEBIAN_FRONTEND noninteractive ENV TERM xterm diff --git a/Dockerfiles/pcap-capture.Dockerfile b/Dockerfiles/pcap-capture.Dockerfile index bbf4688b7..8254959cb 100644 --- a/Dockerfiles/pcap-capture.Dockerfile +++ b/Dockerfiles/pcap-capture.Dockerfile @@ -23,6 +23,7 @@ ENV PGROUP "pcap" # docker-uid-gid-setup.sh will cause them to be lost, so we need # a final check in supervisor.sh before startup ENV PUSER_PRIV_DROP false +ENV PUSER_RLIMIT_UNLOCK true ENV DEBIAN_FRONTEND noninteractive ENV TERM xterm diff --git a/Dockerfiles/suricata.Dockerfile b/Dockerfiles/suricata.Dockerfile index 131bed0e7..d1f8d9891 100644 --- a/Dockerfiles/suricata.Dockerfile +++ b/Dockerfiles/suricata.Dockerfile @@ -26,6 +26,7 @@ ENV PGROUP "suricata" # docker-uid-gid-setup.sh will cause them to be lost, so we need # a final check in docker_entrypoint.sh before startup ENV PUSER_PRIV_DROP false +ENV PUSER_RLIMIT_UNLOCK true ENV SUPERCRONIC_VERSION "0.2.2" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index 5a17d35af..c9adae870 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -28,6 +28,7 @@ ENV PGROUP "zeeker" # docker-uid-gid-setup.sh will cause them to be lost, so we need # a final check in docker_entrypoint.sh before startup ENV PUSER_PRIV_DROP false +ENV PUSER_RLIMIT_UNLOCK true # for download and install ARG ZEEK_LTS= diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index e5a33de8f..546b604d1 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -33,6 +33,11 @@ spec: imagePullPolicy: Always stdin: false tty: true + securityContext: + capabilities: + add: + - IPC_LOCK + - SYS_RESOURCE ports: - containerPort: 8005 envFrom: diff --git a/kubernetes/09-zeek.yml b/kubernetes/09-zeek.yml index 22f00a1e8..094e299b5 100644 --- a/kubernetes/09-zeek.yml +++ b/kubernetes/09-zeek.yml @@ -23,10 +23,11 @@ spec: securityContext: capabilities: add: - - IPC_LOCK - - NET_ADMIN - - NET_RAW - - SYS_ADMIN + - IPC_LOCK + - NET_ADMIN + - NET_RAW + - SYS_ADMIN + - SYS_RESOURCE envFrom: - configMapRef: name: process-env diff --git a/kubernetes/10-suricata.yml b/kubernetes/10-suricata.yml index 45b21d013..e5846a3a0 100644 --- a/kubernetes/10-suricata.yml +++ b/kubernetes/10-suricata.yml @@ -23,10 +23,11 @@ spec: securityContext: capabilities: add: - - IPC_LOCK - - NET_ADMIN - - NET_RAW - - SYS_ADMIN + - IPC_LOCK + - NET_ADMIN + - NET_RAW + - SYS_ADMIN + - SYS_RESOURCE envFrom: - configMapRef: name: process-env diff --git a/kubernetes/20-pcap-capture.yml b/kubernetes/20-pcap-capture.yml index f92a8d35b..fd4905e20 100644 --- a/kubernetes/20-pcap-capture.yml +++ b/kubernetes/20-pcap-capture.yml @@ -23,10 +23,11 @@ spec: securityContext: capabilities: add: - - IPC_LOCK - - NET_ADMIN - - NET_RAW - - SYS_ADMIN + - IPC_LOCK + - NET_ADMIN + - NET_RAW + - SYS_ADMIN + - SYS_RESOURCE envFrom: - configMapRef: name: process-env diff --git a/kubernetes/21-zeek-live.yml b/kubernetes/21-zeek-live.yml index 54746ba24..5d11dd5a1 100644 --- a/kubernetes/21-zeek-live.yml +++ b/kubernetes/21-zeek-live.yml @@ -23,10 +23,11 @@ spec: securityContext: capabilities: add: - - IPC_LOCK - - NET_ADMIN - - NET_RAW - - SYS_ADMIN + - IPC_LOCK + - NET_ADMIN + - NET_RAW + - SYS_ADMIN + - SYS_RESOURCE envFrom: - configMapRef: name: process-env diff --git a/kubernetes/22-suricata-live.yml b/kubernetes/22-suricata-live.yml index d92f9133d..9c2de26aa 100644 --- a/kubernetes/22-suricata-live.yml +++ b/kubernetes/22-suricata-live.yml @@ -23,10 +23,11 @@ spec: securityContext: capabilities: add: - - IPC_LOCK - - NET_ADMIN - - NET_RAW - - SYS_ADMIN + - IPC_LOCK + - NET_ADMIN + - NET_RAW + - SYS_ADMIN + - SYS_RESOURCE envFrom: - configMapRef: name: process-env From 6fc70f2b87eaa8c46b81b013b8358c3d3f390b05 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 12:23:02 -0600 Subject: [PATCH 143/239] don't be so verbose --- shared/bin/docker-uid-gid-setup.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/shared/bin/docker-uid-gid-setup.sh b/shared/bin/docker-uid-gid-setup.sh index 3b95b2399..9b8ec8b28 100755 --- a/shared/bin/docker-uid-gid-setup.sh +++ b/shared/bin/docker-uid-gid-setup.sh @@ -162,7 +162,6 @@ if [[ "${PUSER_RLIMIT_UNLOCK:-false}" == "true" ]] && command -v ulimit >/dev/nu ulimit -x unlimited >/dev/null 2>&1 ulimit -n 65535 >/dev/null 2>&1 ulimit -u 262144 >/dev/null 2>&1 - ulimit -a fi if [[ ! -z "${ENTRYPOINT_CMD}" ]]; then if [[ -z "${ENTRYPOINT_ARGS}" ]]; then From 3283bf90b3ebbaa936e374ecad2014551643437e Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 13:48:20 -0600 Subject: [PATCH 144/239] comment to allow logstash and opensearch to be separated for testing --- kubernetes/13-logstash.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index 21f161356..450712326 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -31,6 +31,16 @@ spec: labels: name: logstash-deployment spec: + # affinity: + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # - labelSelector: + # matchExpressions: + # - key: name + # operator: In + # values: + # - opensearch-deployment + # topologyKey: "kubernetes.io/hostname" containers: - name: logstash-container image: ghcr.io/idaholab/malcolm/logstash-oss:kubernetes From c4ef50b753c78474b99e2f1ee3584ef3dc918d34 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 14:34:51 -0600 Subject: [PATCH 145/239] for idaholab/Malcolm#176, bootstrap opensearch keystore --- Dockerfiles/logstash.Dockerfile | 4 +++- Dockerfiles/opensearch.Dockerfile | 8 +++++--- kubernetes/02-opensearch.yml | 5 +++++ logstash/scripts/logstash-start.sh | 3 +++ shared/bin/keystore-bootstrap.sh | 17 +++++++++++++++++ 5 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 shared/bin/keystore-bootstrap.sh diff --git a/Dockerfiles/logstash.Dockerfile b/Dockerfiles/logstash.Dockerfile index df3590061..81c94f97b 100644 --- a/Dockerfiles/logstash.Dockerfile +++ b/Dockerfiles/logstash.Dockerfile @@ -78,6 +78,7 @@ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic COPY --chmod=755 shared/bin/manuf-oui-parse.py /usr/local/bin/ COPY --chmod=755 shared/bin/jdk-cacerts-auto-import.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/keystore-bootstrap.sh /usr/local/bin/ ADD logstash/maps/*.yaml /etc/ ADD logstash/config/log4j2.properties /usr/share/logstash/config/ ADD logstash/config/logstash.yml /usr/share/logstash/config/logstash.orig.yml @@ -92,9 +93,10 @@ RUN bash -c "chmod --silent 755 /usr/local/bin/*.sh /usr/local/bin/*.py || true" usermod -a -G tty ${PUSER} && \ rm -f /usr/share/logstash/pipeline/logstash.conf && \ rmdir /usr/share/logstash/pipeline && \ - mkdir /logstash-persistent-queue && \ + mkdir -p /logstash-persistent-queue /usr/share/logstash/config/bootstrap && \ chown --silent -R ${PUSER}:root \ /usr/share/logstash/config/logstash*.yml \ + /usr/share/logstash/config/bootstrap \ /usr/share/logstash/malcolm-pipelines \ /usr/share/logstash/malcolm-patterns \ /usr/share/logstash/malcolm-ruby \ diff --git a/Dockerfiles/opensearch.Dockerfile b/Dockerfiles/opensearch.Dockerfile index 19c72fda6..35ef8ad9d 100644 --- a/Dockerfiles/opensearch.Dockerfile +++ b/Dockerfiles/opensearch.Dockerfile @@ -49,13 +49,15 @@ RUN yum install -y openssl util-linux procps rsync && \ echo -e 'cluster.name: "docker-cluster"\nnetwork.host: 0.0.0.0\nbootstrap.memory_lock: true\nhttp.cors.enabled: true\nhttp.cors.allow-origin: "*"\nhttp.cors.allow-methods: OPTIONS, HEAD, GET, POST, PUT, DELETE\nhttp.cors.allow-headers: "kbn-version, Origin, X-Requested-With, Content-Type, Accept, Engaged-Auth-Token Authorization"' > /usr/share/opensearch/config/opensearch.yml && \ sed -i "s/#[[:space:]]*\([0-9]*-[0-9]*:-XX:-\(UseConcMarkSweepGC\|UseCMSInitiatingOccupancyOnly\)\)/\1/" /usr/share/opensearch/config/jvm.options && \ sed -i "s/^[0-9][0-9]*\(-:-XX:\(+UseG1GC\|G1ReservePercent\|InitiatingHeapOccupancyPercent\)\)/$($OPENSEARCH_JAVA_HOME/bin/java -version 2>&1 | grep version | awk '{print $3}' | tr -d '\"' | cut -d. -f1)\1/" /usr/share/opensearch/config/jvm.options && \ - mkdir -p /var/local/ca-trust /opt/opensearch/backup && \ - chown -R $PUSER:$PGROUP /usr/share/opensearch/config/opensearch.yml /var/local/ca-trust /opt/opensearch/backup && \ + mkdir -p /var/local/ca-trust /opt/opensearch/backup /usr/share/opensearch/config/bootstrap && \ + chown -R $PUSER:$PGROUP /usr/share/opensearch/config/opensearch.yml /var/local/ca-trust /opt/opensearch/backup /usr/share/opensearch/config/bootstrap && \ chmod +x /usr/bin/tini && \ - sed -i '/^[[:space:]]*runOpensearch.*/i /usr/local/bin/jdk-cacerts-auto-import.sh || true' /usr/share/opensearch/opensearch-docker-entrypoint.sh + sed -i '/^[[:space:]]*runOpensearch.*/i /usr/local/bin/jdk-cacerts-auto-import.sh || true' /usr/share/opensearch/opensearch-docker-entrypoint.sh && \ + sed -i '/^[[:space:]]*runOpensearch.*/i /usr/local/bin/keystore-bootstrap.sh || true' /usr/share/opensearch/opensearch-docker-entrypoint.sh COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ COPY --chmod=755 shared/bin/jdk-cacerts-auto-import.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/keystore-bootstrap.sh /usr/local/bin/ COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ COPY --from=ghcr.io/mmguero-dev/gostatic --chmod=755 /goStatic /usr/bin/goStatic diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index 0757a443f..6f9e13e01 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -59,6 +59,8 @@ spec: name: opensearch-data-volume - mountPath: "/opt/opensearch/backup" name: opensearch-backup-volume + - name: opensearch-keystore-default-volume + mountPath: /usr/share/opensearch/config/bootstrap/configmap volumes: - name: opensearch-var-local-catrust-volume configMap: @@ -72,3 +74,6 @@ spec: - name: opensearch-backup-volume persistentVolumeClaim: claimName: opensearch-backup-claim + - name: opensearch-keystore-default-volume + configMap: + name: opensearch-keystore \ No newline at end of file diff --git a/logstash/scripts/logstash-start.sh b/logstash/scripts/logstash-start.sh index b80196d7f..01f945272 100755 --- a/logstash/scripts/logstash-start.sh +++ b/logstash/scripts/logstash-start.sh @@ -120,6 +120,9 @@ find "$PIPELINES_DIR" -type f -name "*.conf" -exec sed -i "s/_MALCOLM_LOGSTASH_O # import trusted CA certificates if necessary /usr/local/bin/jdk-cacerts-auto-import.sh || true +# bootstrap keystore file if necessary +/usr/local/bin/keystore-bootstrap.sh || true + # logstash may wish to modify logstash.yml based on some environment variables (e.g., # pipeline.workers), so copy the original onto from the image over the "working copy" before start [[ -r /usr/share/logstash/config/logstash.orig.yml ]] && \ diff --git a/shared/bin/keystore-bootstrap.sh b/shared/bin/keystore-bootstrap.sh new file mode 100644 index 000000000..b4cbcd029 --- /dev/null +++ b/shared/bin/keystore-bootstrap.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +find / -type f -name "*-keystore" -executable | while read KEYSTORE_BIN +do + TOOL_PATH="$(realpath $(dirname "${KEYSTORE_BIN}")/..)" + KEYSTORE_NAME="$(basename "${KEYSTORE_BIN}" | sed 's/-\(keystore\)/.\1/')" + pushd "${TOOL_PATH}" >/dev/null 2>&1 + if ( [[ ! -f ./config/"${KEYSTORE_NAME}" ]] || \ + (( $(stat --format=%s ./config/"${KEYSTORE_NAME}" 2>/dev/null || echo 0) < 200 )) ); then + if [[ -f ./config/bootstrap/"${KEYSTORE_NAME}" ]]; then + cp ./config/bootstrap/"${KEYSTORE_NAME}" ./config/"${KEYSTORE_NAME}" + else + "${KEYSTORE_BIN}" create + fi + fi + popd >/dev/null 2>&1 +done From db35047e9b6c419700c4269450690911310c64cc Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 22:12:08 -0600 Subject: [PATCH 146/239] for idaholab/Malcolm#176, bootstrap opensearch keystore --- Dockerfiles/logstash.Dockerfile | 5 ++- Dockerfiles/opensearch.Dockerfile | 11 +++++- docker-compose-standalone.yml | 2 +- docker-compose.yml | 2 +- kubernetes/02-opensearch.yml | 8 +++- kubernetes/13-logstash.yml | 13 ++++++- shared/bin/keystore-bootstrap.sh | 62 +++++++++++++++++++++++++++---- 7 files changed, 88 insertions(+), 15 deletions(-) diff --git a/Dockerfiles/logstash.Dockerfile b/Dockerfiles/logstash.Dockerfile index 81c94f97b..ac106cc33 100644 --- a/Dockerfiles/logstash.Dockerfile +++ b/Dockerfiles/logstash.Dockerfile @@ -93,10 +93,13 @@ RUN bash -c "chmod --silent 755 /usr/local/bin/*.sh /usr/local/bin/*.py || true" usermod -a -G tty ${PUSER} && \ rm -f /usr/share/logstash/pipeline/logstash.conf && \ rmdir /usr/share/logstash/pipeline && \ - mkdir -p /logstash-persistent-queue /usr/share/logstash/config/bootstrap && \ + mkdir -p /logstash-persistent-queue \ + /usr/share/logstash/config/bootstrap \ + /usr/share/logstash/config/persist && \ chown --silent -R ${PUSER}:root \ /usr/share/logstash/config/logstash*.yml \ /usr/share/logstash/config/bootstrap \ + /usr/share/logstash/config/persist \ /usr/share/logstash/malcolm-pipelines \ /usr/share/logstash/malcolm-patterns \ /usr/share/logstash/malcolm-ruby \ diff --git a/Dockerfiles/opensearch.Dockerfile b/Dockerfiles/opensearch.Dockerfile index 35ef8ad9d..a65b0a1c5 100644 --- a/Dockerfiles/opensearch.Dockerfile +++ b/Dockerfiles/opensearch.Dockerfile @@ -49,8 +49,15 @@ RUN yum install -y openssl util-linux procps rsync && \ echo -e 'cluster.name: "docker-cluster"\nnetwork.host: 0.0.0.0\nbootstrap.memory_lock: true\nhttp.cors.enabled: true\nhttp.cors.allow-origin: "*"\nhttp.cors.allow-methods: OPTIONS, HEAD, GET, POST, PUT, DELETE\nhttp.cors.allow-headers: "kbn-version, Origin, X-Requested-With, Content-Type, Accept, Engaged-Auth-Token Authorization"' > /usr/share/opensearch/config/opensearch.yml && \ sed -i "s/#[[:space:]]*\([0-9]*-[0-9]*:-XX:-\(UseConcMarkSweepGC\|UseCMSInitiatingOccupancyOnly\)\)/\1/" /usr/share/opensearch/config/jvm.options && \ sed -i "s/^[0-9][0-9]*\(-:-XX:\(+UseG1GC\|G1ReservePercent\|InitiatingHeapOccupancyPercent\)\)/$($OPENSEARCH_JAVA_HOME/bin/java -version 2>&1 | grep version | awk '{print $3}' | tr -d '\"' | cut -d. -f1)\1/" /usr/share/opensearch/config/jvm.options && \ - mkdir -p /var/local/ca-trust /opt/opensearch/backup /usr/share/opensearch/config/bootstrap && \ - chown -R $PUSER:$PGROUP /usr/share/opensearch/config/opensearch.yml /var/local/ca-trust /opt/opensearch/backup /usr/share/opensearch/config/bootstrap && \ + mkdir -p /var/local/ca-trust \ + /opt/opensearch/backup \ + /usr/share/opensearch/config/bootstrap \ + /usr/share/opensearch/config/persist && \ + chown -R $PUSER:$PGROUP /usr/share/opensearch/config/opensearch.yml \ + /var/local/ca-trust \ + /opt/opensearch/backup \ + /usr/share/opensearch/config/bootstrap \ + /usr/share/opensearch/config/persist && \ chmod +x /usr/bin/tini && \ sed -i '/^[[:space:]]*runOpensearch.*/i /usr/local/bin/jdk-cacerts-auto-import.sh || true' /usr/share/opensearch/opensearch-docker-entrypoint.sh && \ sed -i '/^[[:space:]]*runOpensearch.*/i /usr/local/bin/keystore-bootstrap.sh || true' /usr/share/opensearch/opensearch-docker-entrypoint.sh diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 762c4af0c..17f8cd9ad 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -29,7 +29,7 @@ services: - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro - ./opensearch:/usr/share/opensearch/data:delegated - ./opensearch-backup:/opt/opensearch/backup:delegated - - ./opensearch/opensearch.keystore:/usr/share/opensearch/config/opensearch.keystore:rw + - ./opensearch/opensearch.keystore:/usr/share/opensearch/config/persist/opensearch.keystore:rw healthcheck: test: ["CMD", "curl", "--silent", "--fail", "http://localhost:9200"] interval: 30s diff --git a/docker-compose.yml b/docker-compose.yml index ec5c7a246..67db5a300 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,7 +32,7 @@ services: - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro - ./opensearch:/usr/share/opensearch/data:delegated - ./opensearch-backup:/opt/opensearch/backup:delegated - - ./opensearch/opensearch.keystore:/usr/share/opensearch/config/opensearch.keystore:rw + - ./opensearch/opensearch.keystore:/usr/share/opensearch/config/persist/opensearch.keystore:rw healthcheck: test: ["CMD", "curl", "--silent", "--fail", "http://localhost:9200"] interval: 30s diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index 6f9e13e01..42f867bd3 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -61,6 +61,9 @@ spec: name: opensearch-backup-volume - name: opensearch-keystore-default-volume mountPath: /usr/share/opensearch/config/bootstrap/configmap + - name: opensearch-config-persist-volume + mountPath: /usr/share/opensearch/config/persist + subPath: "opensearch" volumes: - name: opensearch-var-local-catrust-volume configMap: @@ -76,4 +79,7 @@ spec: claimName: opensearch-backup-claim - name: opensearch-keystore-default-volume configMap: - name: opensearch-keystore \ No newline at end of file + name: opensearch-keystore + - name: opensearch-config-persist-volume + persistentVolumeClaim: + claimName: config-claim \ No newline at end of file diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index 450712326..ef8d2544a 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -91,6 +91,11 @@ spec: name: logstash-certs-volume - mountPath: /etc/configmap name: logstash-maps-volume + - name: logstash-keystore-default-volume + mountPath: /usr/share/logstash/config/bootstrap/configmap + - name: logstash-config-persist-volume + mountPath: /usr/share/logstash/config/persist + subPath: "logstash" volumes: - name: logstash-var-local-catrust-volume configMap: @@ -103,4 +108,10 @@ spec: name: logstash-certs - name: logstash-maps-volume configMap: - name: logstash-maps \ No newline at end of file + name: logstash-maps + - name: logstash-keystore-default-volume + configMap: + name: logstash-keystore + - name: logstash-config-persist-volume + persistentVolumeClaim: + claimName: config-claim \ No newline at end of file diff --git a/shared/bin/keystore-bootstrap.sh b/shared/bin/keystore-bootstrap.sh index b4cbcd029..778faa522 100644 --- a/shared/bin/keystore-bootstrap.sh +++ b/shared/bin/keystore-bootstrap.sh @@ -1,17 +1,63 @@ #!/usr/bin/env bash -find / -type f -name "*-keystore" -executable | while read KEYSTORE_BIN -do +# Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. + +# make sure the keystore file used by the tool (e.g., foobar) is copied or created +# into the correct location before the tool. starts up. +# +# example: +# - /usr/share/foobar/config/persist/foobar.keystore +# - "real" location of keystore file used by foobar +# - /usr/share/foobar/config/bootstrap/foobar.keystore +# - if "persist" keystore does't exist, but "bootstrap" keystore +# does, then copy "bootstrap" keystore to "persist" keystore +# - /usr/share/foobar/config/foobar.keystore +# - symlinks to "persist" keystore (foobar requires this +# to be the file it actually looks at) +# +KEYSTORE_FILE_MIN_BYTES=196 + +# for each "*-keystore" executable in the filesystem... +find / -type f -name "*-keystore" -executable | while read KEYSTORE_BIN; do + + # TOOL_PATH is parent of keystore bin, e.g., /usr/share/foobar TOOL_PATH="$(realpath $(dirname "${KEYSTORE_BIN}")/..)" + + # keystore bin is like foobar-keystore, keystore file is foobar.keystore KEYSTORE_NAME="$(basename "${KEYSTORE_BIN}" | sed 's/-\(keystore\)/.\1/')" + + # chdir to tool directory pushd "${TOOL_PATH}" >/dev/null 2>&1 - if ( [[ ! -f ./config/"${KEYSTORE_NAME}" ]] || \ - (( $(stat --format=%s ./config/"${KEYSTORE_NAME}" 2>/dev/null || echo 0) < 200 )) ); then - if [[ -f ./config/bootstrap/"${KEYSTORE_NAME}" ]]; then - cp ./config/bootstrap/"${KEYSTORE_NAME}" ./config/"${KEYSTORE_NAME}" + + # since ./config/foobar.keystore is going to just be a symlink to ./config/persist/foobar.keystore, + # get it out of the way now if for some reason it already exists + rm -f ./config/"${KEYSTORE_NAME}" + + # does ./config/persist/foobar.keystore exist, and is it big enough to be a real keystore file? ... + if [[ ! -f ./config/persist/"${KEYSTORE_NAME}" ]] || \ + (( $(stat --format=%s ./config/persist/"${KEYSTORE_NAME}" 2>/dev/null || echo 0) < ${KEYSTORE_FILE_MIN_BYTES} )); then + + # ... no, it does not! if there was something there (too small/empty file) remove it + rm -f ./config/persist/"${KEYSTORE_NAME}" + + # does ./config/bootstrap/foobar.keystore exist, and is it big enough to be copied into ./config/persist? ... + if [[ -f ./config/bootstrap/"${KEYSTORE_NAME}" ]] && \ + (( $(stat --format=%s ./config/bootstrap/"${KEYSTORE_NAME}" 2>/dev/null || echo 0) >= ${KEYSTORE_FILE_MIN_BYTES} )); then + + # ... yes, it does! bootstrap becomes the new persist keystore + cp ./config/bootstrap/"${KEYSTORE_NAME}" ./config/persist/"${KEYSTORE_NAME}" + else + # ... no, it doe not! create a fresh/empty .keystore file at ./config/foobar.keystore and move it to ./config/persist/ "${KEYSTORE_BIN}" create + mv ./config/"${KEYSTORE_NAME}" ./config/persist/"${KEYSTORE_NAME}" fi - fi + + fi # check for ./config/persist/foobar.keystore + + # symlink ./config/foobar.keystore to ./config/persist/foobar.keystore + ln -s -r ./config/persist/"${KEYSTORE_NAME}" ./config/"${KEYSTORE_NAME}" + popd >/dev/null 2>&1 -done +done # loop over keystore executables (probably just one) + From 3ce1e93a89772b0e3ac9f3d8f501fc0448f7434d Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 22:25:02 -0600 Subject: [PATCH 147/239] don't gripe about not being able to find files in some directories --- shared/bin/keystore-bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/bin/keystore-bootstrap.sh b/shared/bin/keystore-bootstrap.sh index 778faa522..04f1251a3 100644 --- a/shared/bin/keystore-bootstrap.sh +++ b/shared/bin/keystore-bootstrap.sh @@ -18,7 +18,7 @@ KEYSTORE_FILE_MIN_BYTES=196 # for each "*-keystore" executable in the filesystem... -find / -type f -name "*-keystore" -executable | while read KEYSTORE_BIN; do +find / -type f -name "*-keystore" -executable 2>/dev/null | while read KEYSTORE_BIN; do # TOOL_PATH is parent of keystore bin, e.g., /usr/share/foobar TOOL_PATH="$(realpath $(dirname "${KEYSTORE_BIN}")/..)" From ef69bdcd6813afb6b51e7b2ce1e5a04a2c5f13af Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 22:42:17 -0600 Subject: [PATCH 148/239] for idaholab/Malcolm#176, bootstrap opensearch keystore --- shared/bin/keystore-bootstrap.sh | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/shared/bin/keystore-bootstrap.sh b/shared/bin/keystore-bootstrap.sh index 04f1251a3..03758933f 100644 --- a/shared/bin/keystore-bootstrap.sh +++ b/shared/bin/keystore-bootstrap.sh @@ -15,7 +15,11 @@ # - symlinks to "persist" keystore (foobar requires this # to be the file it actually looks at) # -KEYSTORE_FILE_MIN_BYTES=196 +declare -A KEYSTORE_FILE_MIN_BYTES +KEYSTORE_FILE_MIN_BYTES[opensearch]=196 +KEYSTORE_FILE_MIN_BYTES[logstash]=465 +KEYSTORE_FILE_MIN_BYTES[filebeat]=130 +KEYSTORE_FILE_MIN_BYTES[none]=128 # for each "*-keystore" executable in the filesystem... find / -type f -name "*-keystore" -executable 2>/dev/null | while read KEYSTORE_BIN; do @@ -23,8 +27,13 @@ find / -type f -name "*-keystore" -executable 2>/dev/null | while read KEYSTORE_ # TOOL_PATH is parent of keystore bin, e.g., /usr/share/foobar TOOL_PATH="$(realpath $(dirname "${KEYSTORE_BIN}")/..)" + # tool name is just the part before -keystore + TOOL_NAME="$(basename "${KEYSTORE_BIN}" | sed 's/-keystore$//')" + # keystore bin is like foobar-keystore, keystore file is foobar.keystore - KEYSTORE_NAME="$(basename "${KEYSTORE_BIN}" | sed 's/-\(keystore\)/.\1/')" + KEYSTORE_NAME="${TOOL_NAME}.keystore" + + [[ -v "KEYSTORE_FILE_MIN_BYTES["${TOOL_NAME}"]" ]] && MIN_BYTES=${KEYSTORE_FILE_MIN_BYTES["${TOOL_NAME}"]} || MIN_BYTES=${KEYSTORE_FILE_MIN_BYTES[none]} # chdir to tool directory pushd "${TOOL_PATH}" >/dev/null 2>&1 @@ -35,14 +44,14 @@ find / -type f -name "*-keystore" -executable 2>/dev/null | while read KEYSTORE_ # does ./config/persist/foobar.keystore exist, and is it big enough to be a real keystore file? ... if [[ ! -f ./config/persist/"${KEYSTORE_NAME}" ]] || \ - (( $(stat --format=%s ./config/persist/"${KEYSTORE_NAME}" 2>/dev/null || echo 0) < ${KEYSTORE_FILE_MIN_BYTES} )); then + (( $(stat --format=%s ./config/persist/"${KEYSTORE_NAME}" 2>/dev/null || echo 0) < ${MIN_BYTES} )); then # ... no, it does not! if there was something there (too small/empty file) remove it rm -f ./config/persist/"${KEYSTORE_NAME}" # does ./config/bootstrap/foobar.keystore exist, and is it big enough to be copied into ./config/persist? ... if [[ -f ./config/bootstrap/"${KEYSTORE_NAME}" ]] && \ - (( $(stat --format=%s ./config/bootstrap/"${KEYSTORE_NAME}" 2>/dev/null || echo 0) >= ${KEYSTORE_FILE_MIN_BYTES} )); then + (( $(stat --format=%s ./config/bootstrap/"${KEYSTORE_NAME}" 2>/dev/null || echo 0) >= ${MIN_BYTES} )); then # ... yes, it does! bootstrap becomes the new persist keystore cp ./config/bootstrap/"${KEYSTORE_NAME}" ./config/persist/"${KEYSTORE_NAME}" From f4934f2311a8b5e16740363cb4979d0e442e02fb Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 22:43:43 -0600 Subject: [PATCH 149/239] for idaholab/Malcolm#176, bootstrap opensearch keystore --- .gitignore | 1 + shared/bin/keystore-bootstrap.sh | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e2afa4bfc..c803ce8be 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ /nginx/nginx_ldap*.conf /htadmin/config.ini /htadmin/metadata +*.keystore # runtime .tmp diff --git a/shared/bin/keystore-bootstrap.sh b/shared/bin/keystore-bootstrap.sh index 03758933f..db10a7ae6 100644 --- a/shared/bin/keystore-bootstrap.sh +++ b/shared/bin/keystore-bootstrap.sh @@ -18,7 +18,6 @@ declare -A KEYSTORE_FILE_MIN_BYTES KEYSTORE_FILE_MIN_BYTES[opensearch]=196 KEYSTORE_FILE_MIN_BYTES[logstash]=465 -KEYSTORE_FILE_MIN_BYTES[filebeat]=130 KEYSTORE_FILE_MIN_BYTES[none]=128 # for each "*-keystore" executable in the filesystem... From 8e13221081df87a318f2a81bc7c4bb06239c2699 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 13 Apr 2023 22:57:15 -0600 Subject: [PATCH 150/239] for idaholab/Malcolm#176, bootstrap opensearch keystore --- shared/bin/keystore-bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/bin/keystore-bootstrap.sh b/shared/bin/keystore-bootstrap.sh index db10a7ae6..f24243523 100644 --- a/shared/bin/keystore-bootstrap.sh +++ b/shared/bin/keystore-bootstrap.sh @@ -32,7 +32,7 @@ find / -type f -name "*-keystore" -executable 2>/dev/null | while read KEYSTORE_ # keystore bin is like foobar-keystore, keystore file is foobar.keystore KEYSTORE_NAME="${TOOL_NAME}.keystore" - [[ -v "KEYSTORE_FILE_MIN_BYTES["${TOOL_NAME}"]" ]] && MIN_BYTES=${KEYSTORE_FILE_MIN_BYTES["${TOOL_NAME}"]} || MIN_BYTES=${KEYSTORE_FILE_MIN_BYTES[none]} + [[ -z "${KEYSTORE_FILE_MIN_BYTES["${TOOL_NAME}"]+unset}" ]] && MIN_BYTES=${KEYSTORE_FILE_MIN_BYTES[none]} || MIN_BYTES=${KEYSTORE_FILE_MIN_BYTES["${TOOL_NAME}"]} # chdir to tool directory pushd "${TOOL_PATH}" >/dev/null 2>&1 From 1f9190edafb08b52d9f2f383cd868ca63f39cb7c Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 14 Apr 2023 09:38:58 -0600 Subject: [PATCH 151/239] beginning work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- .dockerignore | 1 + docs/ubuntu-install-example.md | 2 +- malcolm-iso/build.sh | 1 + scripts/control.py | 507 +++++++++++++++----------- scripts/malcolm_appliance_packager.sh | 2 + scripts/malcolm_common.py | 4 + scripts/malcolm_kubernetes.py | 224 ++++++++++++ scripts/malcolm_utils.py | 10 + 8 files changed, 541 insertions(+), 210 deletions(-) create mode 100644 scripts/malcolm_kubernetes.py diff --git a/.dockerignore b/.dockerignore index bd42311d6..d485d44e3 100644 --- a/.dockerignore +++ b/.dockerignore @@ -32,6 +32,7 @@ pcap _site scripts !scripts/malcolm_common.py +!scripts/malcolm_kubernetes.py !scripts/malcolm_utils.py zeek-logs suricata-logs diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index 68cca9911..a3c0ea965 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -12,7 +12,7 @@ To install Malcolm from the latest Malcolm release, browse to the [Malcolm relea ``` user@host:~$ cd Downloads/ user@host:~/Downloads$ ls -malcolm_common.py malcolm_utils.py install.py malcolm_20190611_095410_ce2d8de.tar.gz +malcolm_common.py malcolm_kubernetes.py malcolm_utils.py install.py malcolm_20190611_095410_ce2d8de.tar.gz ``` If you are obtaining Malcolm using `git` instead, run the following command to clone Malcolm into a local working copy: diff --git a/malcolm-iso/build.sh b/malcolm-iso/build.sh index dc78f5876..1d2e0445b 100755 --- a/malcolm-iso/build.sh +++ b/malcolm-iso/build.sh @@ -140,6 +140,7 @@ if [ -d "$WORKDIR" ]; then ln -s ./control.py wipe popd >/dev/null 2>&1 cp ./scripts/malcolm_common.py "$MALCOLM_DEST_DIR/scripts/" + cp ./scripts/malcolm_kubernetes.py "$MALCOLM_DEST_DIR/scripts/" cp ./scripts/malcolm_utils.py "$MALCOLM_DEST_DIR/scripts/" cp ./logstash/certs/*.conf "$MALCOLM_DEST_DIR/logstash/certs/" cp ./logstash/maps/malcolm_severity.yaml "$MALCOLM_DEST_DIR/logstash/maps/" diff --git a/scripts/control.py b/scripts/control.py index 9ac44fc5a..3b84bfbe3 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -30,6 +30,7 @@ DisplayMessage, DisplayProgramBox, GetUidGidFromComposeFile, + KubernetesDynamic, LocalPathForContainerBindMount, MainDialog, MalcolmAuthFilesExist, @@ -55,8 +56,10 @@ str2bool, pushd, ) +import malcolm_kubernetes from base64 import b64encode from collections import defaultdict, namedtuple +from enum import Flag, auto from subprocess import PIPE, STDOUT, DEVNULL, Popen, TimeoutExpired from urllib.parse import urlparse @@ -83,9 +86,23 @@ def __exit__(self, *args): args = None dockerBin = None dockerComposeBin = None +dockerComposeYaml = None +kubeClient = None +kubeImported = None opensslBin = None +orchMode = None yamlImported = None -dockerComposeYaml = None + + +################################################################################################### +class OrchestrationFramework(Flag): + UNKNOWN = auto() + DOCKER_COMPOSE = auto() + KUBERNETES = auto() + + +OrchestrationFrameworksSupported = OrchestrationFramework.DOCKER_COMPOSE | OrchestrationFramework.KUBERNETES + ################################################################################################### try: @@ -98,7 +115,53 @@ def __exit__(self, *args): ################################################################################################### -# perform a service-keystore operation in a Docker container +# determine if a YAML file looks like a docker-compose.yml file or a kubeconfig file +def determineYamlFileFormat(inputFileName): + global yamlImported + + result = OrchestrationFramework.UNKNOWN + try: + with open(inputFileName, 'r') as cf: + orchestrationYaml = yamlImported.safe_load(cf) + + if isinstance(orchestrationYaml, dict): + if any(key in orchestrationYaml for key in ('apiVersion', 'clusters', 'contexts', 'kind')): + result = OrchestrationFramework.KUBERNETES + elif 'services' in orchestrationYaml: + result = OrchestrationFramework.DOCKER_COMPOSE + + except Exception as e: + eprint(f'Error deciphering {args.composeFile}: {e}') + + return result + + +################################################################################################### +def checkEnvFilesExist(): + global args + + # first, if the configDir is completely empty, then populate from defaults + defaultConfigDir = os.path.join(MalcolmPath, 'config') + if ( + (args.configDir is not None) + and os.path.isdir(args.configDir) + and os.path.isdir(defaultConfigDir) + and (not same_file_or_dir(defaultConfigDir, args.configDir)) + and (not os.listdir(args.configDir)) + ): + for defaultEnvExampleFile in glob.glob(os.path.join(defaultConfigDir, '*.env.example')): + shutil.copy2(defaultEnvExampleFile, args.configDir) + + # if a specific config/*.env file doesn't exist, use the *.example.env files as defaults + envExampleFiles = glob.glob(os.path.join(args.configDir, '*.env.example')) + for envExampleFile in envExampleFiles: + envFile = envExampleFile[: -len('.example')] + if not os.path.isfile(envFile): + shutil.copyfile(envExampleFile, envFile) + + +################################################################################################### +# perform a service-keystore operation in a container # # service - the service in the docker-compose YML file # keystore_args - arguments to pass to the service-keystore binary in the container @@ -110,223 +173,220 @@ def keystore_op(service, dropPriv=False, *keystore_args, **run_process_kwargs): global args global dockerBin global dockerComposeBin + global orchMode err = -1 results = [] - # the opensearch containers all follow the same naming pattern for these executables - keystoreBinProc = f"/usr/share/{service}/bin/{service}-keystore" + if orchMode is OrchestrationFramework.DOCKER_COMPOSE: + # the opensearch containers all follow the same naming pattern for these executables + keystoreBinProc = f"/usr/share/{service}/bin/{service}-keystore" - # if we're using docker-uid-gid-setup.sh to drop privileges as we spin up a container - dockerUidGuidSetup = "/usr/local/bin/docker-uid-gid-setup.sh" + # if we're using docker-uid-gid-setup.sh to drop privileges as we spin up a container + dockerUidGuidSetup = "/usr/local/bin/docker-uid-gid-setup.sh" - # docker-compose use local temporary path - osEnv = os.environ.copy() - osEnv['TMPDIR'] = MalcolmTmpPath + # docker-compose use local temporary path + osEnv = os.environ.copy() + osEnv['TMPDIR'] = MalcolmTmpPath - # open up the docker-compose file and "grep" for the line where the keystore file - # is bind-mounted into the service container (once and only once). the bind - # mount needs to exist in the YML file and the local directory containing the - # keystore file needs to exist (although the file itself might not yet). - # also get PUID and PGID variables from the docker-compose file. - localKeystore = None - localKeystoreDir = None - localKeystorePreExists = False - volumeKeystore = None - volumeKeystoreDir = None - uidGidDict = None + # open up the docker-compose file and "grep" for the line where the keystore file + # is bind-mounted into the service container (once and only once). the bind + # mount needs to exist in the YML file and the local directory containing the + # keystore file needs to exist (although the file itself might not yet). + # also get PUID and PGID variables from the docker-compose file. + localKeystore = None + localKeystoreDir = None + localKeystorePreExists = False + volumeKeystore = None + volumeKeystoreDir = None + uidGidDict = None - try: - uidGidDict = GetUidGidFromComposeFile(args.composeFile) + try: + uidGidDict = GetUidGidFromComposeFile(args.composeFile) - composeFileLines = list() - with open(args.composeFile, 'r') as f: - allLines = f.readlines() - composeFileLines = [x for x in allLines if re.search(fr'-.*?{service}.keystore\s*:.*{service}.keystore', x)] + composeFileLines = list() + with open(args.composeFile, 'r') as f: + allLines = f.readlines() + composeFileLines = [ + x for x in allLines if re.search(fr'-.*?{service}.keystore\s*:.*{service}.keystore', x) + ] - if (len(composeFileLines) == 1) and (len(composeFileLines[0]) > 0): - matches = re.search( - fr'-\s*(?P.*?{service}.keystore)\s*:\s*(?P.*?{service}.keystore)', - composeFileLines[0], - ) - if matches: - localKeystore = os.path.realpath(matches.group('localKeystore')) - localKeystoreDir = os.path.dirname(localKeystore) - volumeKeystore = matches.group('volumeKeystore') - volumeKeystoreDir = os.path.dirname(volumeKeystore) + if (len(composeFileLines) == 1) and (len(composeFileLines[0]) > 0): + matches = re.search( + fr'-\s*(?P.*?{service}.keystore)\s*:\s*(?P.*?{service}.keystore)', + composeFileLines[0], + ) + if matches: + localKeystore = os.path.realpath(matches.group('localKeystore')) + localKeystoreDir = os.path.dirname(localKeystore) + volumeKeystore = matches.group('volumeKeystore') + volumeKeystoreDir = os.path.dirname(volumeKeystore) - if (localKeystore is not None) and (volumeKeystore is not None) and os.path.isdir(localKeystoreDir): - localKeystorePreExists = os.path.isfile(localKeystore) + if (localKeystore is not None) and (volumeKeystore is not None) and os.path.isdir(localKeystoreDir): + localKeystorePreExists = os.path.isfile(localKeystore) - dockerCmd = None + dockerCmd = None - # determine if Malcolm is running; if so, we'll use docker-compose exec, other wise we'll use docker run - err, out = run_process( - [dockerComposeBin, '-f', args.composeFile, 'ps', '-q', service], env=osEnv, debug=args.debug - ) - out[:] = [x for x in out if x] - if (err == 0) and (len(out) > 0): - # Malcolm is running, we can use an existing container - - # assemble the service-keystore command - dockerCmd = [ - dockerComposeBin, - '-f', - args.composeFile, - 'exec', - # if using stdin, indicate the container is "interactive", else noop (duplicate --rm) - '-T' if ('stdin' in run_process_kwargs and run_process_kwargs['stdin']) else '', - # execute as UID:GID in docker-compose.yml file - '-u', - f'{uidGidDict["PUID"]}:{uidGidDict["PGID"]}', - # the work directory in the container is the directory to contain the keystore file - '-w', - volumeKeystoreDir, - # the service name - service, - # the executable filespec - keystoreBinProc, - ] + # determine if Malcolm is running; if so, we'll use docker-compose exec, other wise we'll use docker run + err, out = run_process( + [dockerComposeBin, '-f', args.composeFile, 'ps', '-q', service], env=osEnv, debug=args.debug + ) + out[:] = [x for x in out if x] + if (err == 0) and (len(out) > 0): + # Malcolm is running, we can use an existing container - else: - # Malcolm isn't running, do 'docker run' to spin up a temporary container to run the ocmmand - - # "grep" the docker image out of the service's image: value from the docker-compose YML file - serviceImage = None - composeFileLines = list() - with open(args.composeFile, 'r') as f: - composeFileLines = [x for x in f.readlines() if f'image: ghcr.io/idaholab/malcolm/{service}' in x] - if (len(composeFileLines) > 0) and (len(composeFileLines[0]) > 0): - imageLineValues = composeFileLines[0].split() - if len(imageLineValues) > 1: - serviceImage = imageLineValues[1] - - if serviceImage is not None: # assemble the service-keystore command dockerCmd = [ - dockerBin, - 'run', - # remove the container when complete - '--rm', - # if using stdin, indicate the container is "interactive", else noop - '-i' if ('stdin' in run_process_kwargs and run_process_kwargs['stdin']) else '', - # if dropPriv, dockerUidGuidSetup will take care of dropping privileges for the correct UID/GID - # if NOT dropPriv, enter with the keystore executable directly - '--entrypoint', - dockerUidGuidSetup if dropPriv else keystoreBinProc, - '--env', - f'PUID={uidGidDict["PUID"]}', - '--env', - f'DEFAULT_UID={uidGidDict["PUID"]}', - '--env', - f'PGID={uidGidDict["PGID"]}', - '--env', - f'DEFAULT_GID={uidGidDict["PGID"]}', - '--env', - f'PUSER_CHOWN={volumeKeystoreDir}', - # rw bind mount the local directory to contain the keystore file to the container directory - '-v', - f'{localKeystoreDir}:{volumeKeystoreDir}:rw', + dockerComposeBin, + '-f', + args.composeFile, + 'exec', + # if using stdin, indicate the container is "interactive", else noop (duplicate --rm) + '-T' if ('stdin' in run_process_kwargs and run_process_kwargs['stdin']) else '', + # execute as UID:GID in docker-compose.yml file + '-u', + f'{uidGidDict["PUID"]}:{uidGidDict["PGID"]}', # the work directory in the container is the directory to contain the keystore file '-w', volumeKeystoreDir, - # if dropPriv, execute as root, as docker-uid-gid-setup.sh will drop privileges for us - # if NOT dropPriv, execute as UID:GID in docker-compose.yml file - '-u', - 'root' if dropPriv else f'{uidGidDict["PUID"]}:{uidGidDict["PGID"]}', - # the service image name grepped from the YML file - serviceImage, + # the service name + service, + # the executable filespec + keystoreBinProc, ] - if dropPriv: - # the keystore executable filespec (as we used dockerUidGuidSetup as the entrypoint) - dockerCmd.append(keystoreBinProc) - else: - raise Exception(f'Unable to identify docker image for {service} in {args.composeFile}') + # Malcolm isn't running, do 'docker run' to spin up a temporary container to run the ocmmand + + # "grep" the docker image out of the service's image: value from the docker-compose YML file + serviceImage = None + composeFileLines = list() + with open(args.composeFile, 'r') as f: + composeFileLines = [ + x for x in f.readlines() if f'image: ghcr.io/idaholab/malcolm/{service}' in x + ] + if (len(composeFileLines) > 0) and (len(composeFileLines[0]) > 0): + imageLineValues = composeFileLines[0].split() + if len(imageLineValues) > 1: + serviceImage = imageLineValues[1] + + if serviceImage is not None: + # assemble the service-keystore command + dockerCmd = [ + dockerBin, + 'run', + # remove the container when complete + '--rm', + # if using stdin, indicate the container is "interactive", else noop + '-i' if ('stdin' in run_process_kwargs and run_process_kwargs['stdin']) else '', + # if dropPriv, dockerUidGuidSetup will take care of dropping privileges for the correct UID/GID + # if NOT dropPriv, enter with the keystore executable directly + '--entrypoint', + dockerUidGuidSetup if dropPriv else keystoreBinProc, + '--env', + f'PUID={uidGidDict["PUID"]}', + '--env', + f'DEFAULT_UID={uidGidDict["PUID"]}', + '--env', + f'PGID={uidGidDict["PGID"]}', + '--env', + f'DEFAULT_GID={uidGidDict["PGID"]}', + '--env', + f'PUSER_CHOWN={volumeKeystoreDir}', + # rw bind mount the local directory to contain the keystore file to the container directory + '-v', + f'{localKeystoreDir}:{volumeKeystoreDir}:rw', + # the work directory in the container is the directory to contain the keystore file + '-w', + volumeKeystoreDir, + # if dropPriv, execute as root, as docker-uid-gid-setup.sh will drop privileges for us + # if NOT dropPriv, execute as UID:GID in docker-compose.yml file + '-u', + 'root' if dropPriv else f'{uidGidDict["PUID"]}:{uidGidDict["PGID"]}', + # the service image name grepped from the YML file + serviceImage, + ] + + if dropPriv: + # the keystore executable filespec (as we used dockerUidGuidSetup as the entrypoint) + dockerCmd.append(keystoreBinProc) - if dockerCmd is not None: - # append whatever other arguments to pass to the executable filespec - if keystore_args: - dockerCmd.extend(list(keystore_args)) + else: + raise Exception(f'Unable to identify docker image for {service} in {args.composeFile}') - dockerCmd[:] = [x for x in dockerCmd if x] + if dockerCmd is not None: + # append whatever other arguments to pass to the executable filespec + if keystore_args: + dockerCmd.extend(list(keystore_args)) - # execute the command, passing through run_process_kwargs to run_process as expanded keyword arguments - err, results = run_process(dockerCmd, env=osEnv, debug=args.debug, **run_process_kwargs) - if (err != 0) or (not os.path.isfile(localKeystore)): - raise Exception(f'Error processing command {service} keystore: {results}') + dockerCmd[:] = [x for x in dockerCmd if x] - else: - raise Exception(f'Unable formulate keystore command for {service} in {args.composeFile}') + # execute the command, passing through run_process_kwargs to run_process as expanded keyword arguments + err, results = run_process(dockerCmd, env=osEnv, debug=args.debug, **run_process_kwargs) + if (err != 0) or (not os.path.isfile(localKeystore)): + raise Exception(f'Error processing command {service} keystore: {results}') - else: - raise Exception(f'Unable to identify a unique keystore file bind mount for {service} in {args.composeFile}') + else: + raise Exception(f'Unable formulate keystore command for {service} in {args.composeFile}') - except Exception as e: - if err == 0: - err = -1 - - # don't be so whiny if the "create" failed just because it already existed or a 'remove' failed on a nonexistant item - if ( - (not args.debug) - and list(keystore_args) - and (len(list(keystore_args)) > 0) - and (list(keystore_args)[0].lower() in ('create', 'remove')) - and localKeystorePreExists - ): - pass - else: - eprint(e) + else: + raise Exception( + f'Unable to identify a unique keystore file bind mount for {service} in {args.composeFile}' + ) + + except Exception as e: + if err == 0: + err = -1 + + # don't be so whiny if the "create" failed just because it already existed or a 'remove' failed on a nonexistant item + if ( + (not args.debug) + and list(keystore_args) + and (len(list(keystore_args)) > 0) + and (list(keystore_args)[0].lower() in ('create', 'remove')) + and localKeystorePreExists + ): + pass + else: + eprint(e) # success = (error == 0) return (err == 0), results -################################################################################################### -def checkEnvFilesExist(): - global args - - # first, if the configDir is completely empty, then populate from defaults - defaultConfigDir = os.path.join(MalcolmPath, 'config') - if ( - (args.configDir is not None) - and os.path.isdir(args.configDir) - and os.path.isdir(defaultConfigDir) - and (not same_file_or_dir(defaultConfigDir, args.configDir)) - and (not os.listdir(args.configDir)) - ): - for defaultEnvExampleFile in glob.glob(os.path.join(defaultConfigDir, '*.env.example')): - shutil.copy2(defaultEnvExampleFile, args.configDir) - - # if a specific config/*.env file doesn't exist, use the *.example.env files as defaults - envExampleFiles = glob.glob(os.path.join(args.configDir, '*.env.example')) - for envExampleFile in envExampleFiles: - envFile = envExampleFile[: -len('.example')] - if not os.path.isfile(envFile): - shutil.copyfile(envExampleFile, envFile) - - ################################################################################################### def status(): global args global dockerComposeBin + global orchMode + global kubeClient - # docker-compose use local temporary path - osEnv = os.environ.copy() - osEnv['TMPDIR'] = MalcolmTmpPath + if orchMode is OrchestrationFramework.DOCKER_COMPOSE: + # docker-compose use local temporary path + osEnv = os.environ.copy() + osEnv['TMPDIR'] = MalcolmTmpPath - err, out = run_process( - [dockerComposeBin, '-f', args.composeFile, 'ps', args.service][: 5 if args.service is not None else -1], - env=osEnv, - debug=args.debug, - ) - if err == 0: - print("\n".join(out)) - else: - eprint("Failed to display Malcolm status\n") - eprint("\n".join(out)) - exit(err) + err, out = run_process( + [dockerComposeBin, '-f', args.composeFile, 'ps', args.service][: 5 if args.service is not None else -1], + env=osEnv, + debug=args.debug, + ) + if err == 0: + print("\n".join(out)) + else: + eprint("Failed to display Malcolm status\n") + eprint("\n".join(out)) + exit(err) + + elif orchMode is OrchestrationFramework.KUBERNETES: + try: + malcolm_kubernetes.PrintNodeStatus() + print() + malcolm_kubernetes.PrintPodStatus(namespace=args.namespace) + print() + except Exception as e: + eprint(f'Error listing {args.namespace} pods: {e}') + exit(-1) ################################################################################################### @@ -1578,10 +1638,13 @@ def authSetup(wipe=False): # main def main(): global args + global orchMode global dockerBin global dockerComposeBin global opensslBin global yamlImported + global kubeClient + global kubeImported global dockerComposeYaml # extract arguments from the command line @@ -1609,7 +1672,7 @@ def main(): metavar='', type=str, default='docker-compose.yml', - help='docker-compose YML file', + help='docker-compose or kubeconfig YML file', ) parser.add_argument( '-e', @@ -1621,6 +1684,16 @@ def main(): default=None, help="Directory containing Malcolm's .env files", ) + parser.add_argument( + '-n', + '--namespace', + required=False, + dest='namespace', + metavar='', + type=str, + default='malcolm', + help="Kubernetes namespace", + ) parser.add_argument( '-s', '--service', @@ -1642,7 +1715,6 @@ def main(): help="Tail Malcolm logs", ) parser.add_argument( - '-n', '--lines', dest='logLineCount', type=posInt, @@ -1739,12 +1811,15 @@ def main(): else: sys.tracebacklimit = 0 - yamlImported = YAMLDynamic(debug=args.debug) + yamlImported = YAMLDynamic(debug=args.debug, forceInteraction=sys.__stdin__.isatty()) if args.debug: eprint(f"Imported yaml: {yamlImported}") if not yamlImported: exit(2) + if not ((orchMode := determineYamlFileFormat(args.composeFile)) and (orchMode in OrchestrationFrameworksSupported)): + raise Exception(f'{args.composeFile} must be a docker-compose or kubeconfig YAML file') + with pushd(MalcolmPath): # don't run this as root if (pyPlatform != PLATFORM_WINDOWS) and ( @@ -1781,32 +1856,46 @@ def main(): osEnv = os.environ.copy() osEnv['TMPDIR'] = MalcolmTmpPath - # make sure docker/docker-compose is available - dockerBin = 'docker.exe' if ((pyPlatform == PLATFORM_WINDOWS) and which('docker.exe')) else 'docker' - if (pyPlatform == PLATFORM_WINDOWS) and which('docker-compose.exe'): - dockerComposeBin = 'docker-compose.exe' - elif which('docker-compose'): - dockerComposeBin = 'docker-compose' - elif os.path.isfile('/usr/libexec/docker/cli-plugins/docker-compose'): - dockerComposeBin = '/usr/libexec/docker/cli-plugins/docker-compose' - elif os.path.isfile('/usr/local/opt/docker-compose/bin/docker-compose'): - dockerComposeBin = '/usr/local/opt/docker-compose/bin/docker-compose' - elif os.path.isfile('/usr/local/bin/docker-compose'): - dockerComposeBin = '/usr/local/bin/docker-compose' - elif os.path.isfile('/usr/bin/docker-compose'): - dockerComposeBin = '/usr/bin/docker-compose' - else: - dockerComposeBin = 'docker-compose' - err, out = run_process([dockerBin, 'info'], debug=args.debug) - if err != 0: - raise Exception(f'{ScriptName} requires docker, please run install.py') - err, out = run_process([dockerComposeBin, '-f', args.composeFile, 'version'], env=osEnv, debug=args.debug) - if err != 0: - raise Exception(f'{ScriptName} requires docker-compose, please run install.py') - - # load compose file YAML (used to find some volume bind mount locations) - with open(args.composeFile, 'r') as cf: - dockerComposeYaml = yamlImported.safe_load(cf) + if orchMode is OrchestrationFramework.DOCKER_COMPOSE: + # make sure docker/docker-compose is available + dockerBin = 'docker.exe' if ((pyPlatform == PLATFORM_WINDOWS) and which('docker.exe')) else 'docker' + if (pyPlatform == PLATFORM_WINDOWS) and which('docker-compose.exe'): + dockerComposeBin = 'docker-compose.exe' + elif which('docker-compose'): + dockerComposeBin = 'docker-compose' + elif os.path.isfile('/usr/libexec/docker/cli-plugins/docker-compose'): + dockerComposeBin = '/usr/libexec/docker/cli-plugins/docker-compose' + elif os.path.isfile('/usr/local/opt/docker-compose/bin/docker-compose'): + dockerComposeBin = '/usr/local/opt/docker-compose/bin/docker-compose' + elif os.path.isfile('/usr/local/bin/docker-compose'): + dockerComposeBin = '/usr/local/bin/docker-compose' + elif os.path.isfile('/usr/bin/docker-compose'): + dockerComposeBin = '/usr/bin/docker-compose' + else: + dockerComposeBin = 'docker-compose' + err, out = run_process([dockerBin, 'info'], debug=args.debug) + if err != 0: + raise Exception(f'{ScriptName} requires docker, please run install.py') + err, out = run_process([dockerComposeBin, '-f', args.composeFile, 'version'], env=osEnv, debug=args.debug) + if err != 0: + raise Exception(f'{ScriptName} requires docker-compose, please run install.py') + + # load compose file YAML (used to find some volume bind mount locations) + with open(args.composeFile, 'r') as cf: + dockerComposeYaml = yamlImported.safe_load(cf) + + elif orchMode is OrchestrationFramework.KUBERNETES: + kubeImported = KubernetesDynamic(debug=args.debug, forceInteraction=sys.__stdin__.isatty()) + if args.debug: + eprint(f"Imported kubernetes: {kubeImported}") + if kubeImported: + kubeImported.config.load_kube_config(args.composeFile) + kubeClient = kubeImported.client.CoreV1Api() + kubeClient.list_node() + else: + raise Exception( + f'{ScriptName} requires the official Python client library for kubernetes for {orchMode} mode' + ) # identify openssl binary opensslBin = 'openssl.exe' if ((pyPlatform == PLATFORM_WINDOWS) and which('openssl.exe')) else 'openssl' diff --git a/scripts/malcolm_appliance_packager.sh b/scripts/malcolm_appliance_packager.sh index 932f303b5..8855e03ef 100755 --- a/scripts/malcolm_appliance_packager.sh +++ b/scripts/malcolm_appliance_packager.sh @@ -96,6 +96,7 @@ if mkdir "$DESTDIR"; then cp $VERBOSE ./scripts/install.py "$DESTDIR/scripts/" cp $VERBOSE ./scripts/control.py "$DESTDIR/scripts/" cp $VERBOSE ./scripts/malcolm_common.py "$DESTDIR/scripts/" + cp $VERBOSE ./scripts/malcolm_kubernetes.py "$DESTDIR/scripts/" cp $VERBOSE ./scripts/malcolm_utils.py "$DESTDIR/scripts/" cp $VERBOSE ./README.md "$DESTDIR/" cp $VERBOSE ./logstash/certs/*.conf "$DESTDIR/logstash/certs/" @@ -121,6 +122,7 @@ if mkdir "$DESTDIR"; then README="$RUN_PATH/$(basename $DESTDIR).README.txt" cp $VERBOSE "$SCRIPT_PATH/install.py" "$RUN_PATH/" cp $VERBOSE "$SCRIPT_PATH/malcolm_common.py" "$RUN_PATH/" + cp $VERBOSE "$SCRIPT_PATH/malcolm_kubernetes.py" "$RUN_PATH/" cp $VERBOSE "$SCRIPT_PATH/malcolm_utils.py" "$RUN_PATH/" tar -czf $VERBOSE "$DESTNAME" "./$(basename $DESTDIR)/" echo "Packaged Malcolm to \"$DESTNAME\"" diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index a319bb5f4..baab2b868 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -556,6 +556,10 @@ def YAMLDynamic(debug=False, forceInteraction=False): return DoDynamicImport("yaml", "pyyaml", interactive=forceInteraction, debug=debug) +def KubernetesDynamic(debug=False, forceInteraction=False): + return DoDynamicImport("kubernetes", "kubernetes", interactive=forceInteraction, debug=debug) + + def DotEnvDynamic(debug=False, forceInteraction=False): return DoDynamicImport("dotenv", "python-dotenv", interactive=forceInteraction, debug=debug) diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py new file mode 100644 index 000000000..517a22f42 --- /dev/null +++ b/scripts/malcolm_kubernetes.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. + +MALCOLM_IMAGE_PREFIX = 'ghcr.io/idaholab/malcolm/' + +from concurrent.futures import ThreadPoolExecutor, as_completed + +from malcolm_common import ( + KubernetesDynamic, +) +from malcolm_utils import ( + tablify, +) + + +def _nanocore_to_millicore(n): + n = int(n[:-1]) + return str(round(n / 1000000, 2)) + 'm' + + +def _core_to_millicore(n): + n = int(n) + return str(n * 1000) + 'm' + + +def _percent_cpu(tcpu, ccpu): + tcpu = float(tcpu[:-1]) + ccpu = float(ccpu[:-1]) + return str(round((ccpu / tcpu) * 100, 2)) + '%' + + +def _to_gibibyte_or_mebibyte(n): + if n[-2:] == 'Ki': + n = float(n[:-2]) + if str(round(n * 0.000000953674316, 2)).split('.')[0] == '0': + return str(round(n * 0.0009765625, 2)) + 'Mi' + return str(round(n * 0.000000953674316, 2)) + 'Gi' + elif n[-2:] == 'Mi' or n[-2:] == 'Gi': + return n + + +def load_node_list(): + nodes = [] + + if ( + (kubeImported := KubernetesDynamic()) + and (stats_api := kubeImported.client.CustomObjectsApi()) + and (node_stats := stats_api.list_cluster_custom_object("metrics.k8s.io", "v1beta1", "nodes")) + ): + for stat in node_stats['items']: + nodes.append(stat['metadata']['name']) + + return nodes + + +def node_stats(node): + node_dict = {} + if kubeImported := KubernetesDynamic(): + k8s_api = kubeImported.client.CoreV1Api() + api_response = k8s_api.read_node_status(node) + stats_api = kubeImported.client.CustomObjectsApi() + node_stats = stats_api.list_cluster_custom_object("metrics.k8s.io", "v1beta1", "nodes/{}".format(node)) + field_selector = 'spec.nodeName=' + node + pods = k8s_api.list_pod_for_all_namespaces(watch=False, field_selector=field_selector) + node_dict[node] = [ + api_response.metadata.name, + api_response.spec.provider_id.split('/')[-1], + api_response.metadata.labels['node.kubernetes.io/instance-type'], + _core_to_millicore(api_response.status.capacity['cpu']), + _nanocore_to_millicore(node_stats['usage']['cpu']), + _percent_cpu( + _core_to_millicore(api_response.status.capacity['cpu']), + _nanocore_to_millicore(node_stats['usage']['cpu']), + ), + _to_gibibyte_or_mebibyte(api_response.status.capacity['memory']), + _to_gibibyte_or_mebibyte(node_stats['usage']['memory']), + _to_gibibyte_or_mebibyte(api_response.status.capacity['ephemeral-storage']), + len(pods.items), + ] + + return node_dict + + +def pod_stats(node, namespace): + pod_dict = {} + if kubeImported := KubernetesDynamic(): + k8s_api = kubeImported.client.CoreV1Api() + stats_api = kubeImported.client.CustomObjectsApi() + field_selector = 'spec.nodeName=' + node + if namespace: + pods = k8s_api.list_namespaced_pod(namespace, watch=False, field_selector=field_selector) + else: + pods = k8s_api.list_pod_for_all_namespaces(watch=False, field_selector=field_selector) + for pod in pods.items: + pod_name = pod.metadata.name + namespace = pod.metadata.namespace + phase = pod.status.phase + pod_ip = pod.status.pod_ip + if not pod.metadata.owner_references: + pod_kind = None + else: + pod_kind = pod.metadata.owner_references[0].kind + worker_node = pod.spec.node_name + try: + cpu = 0 + mem = 0 + cpu_mem = stats_api.get_namespaced_custom_object( + "metrics.k8s.io", "v1beta1", namespace, "pods", pod_name + ) + for c in cpu_mem['containers']: + if c['usage']['cpu'] == '0': + pass + else: + cpu = +int(c['usage']['cpu'][:-1]) + cpu = str(cpu) + 'n' + cpu = _nanocore_to_millicore(cpu) + for m in cpu_mem['containers']: + mem = +int(m['usage']['memory'][:-2]) + mem = str(mem) + 'Ki' + mem = _to_gibibyte_or_mebibyte(mem) + except kubeImported.client.rest.ApiException as x: + if x.status == 404: + cpu = 'Not Found' + mem = 'Not Found' + container_name = [] + if not pod.status.container_statuses: + container_name = None + container_image = None + else: + for container in range(len(pod.status.container_statuses)): + container_name.append( + '{}:{}'.format( + pod.status.container_statuses[container].name, + pod.status.container_statuses[container].restart_count, + ) + ) + container_image = [] + for container in range(len(pod.status.container_statuses)): + container_image.append( + pod.status.container_statuses[container].image.replace(MALCOLM_IMAGE_PREFIX, '') + ) + pod_dict[pod_name] = [ + pod_name, + namespace, + phase, + pod_ip, + pod_kind, + worker_node, + cpu, + mem, + ','.join(container_name), + ','.join(container_image), + ] + if namespace: + del pod_dict[pod_name][1] + + return pod_dict + + +def PrintNodeStatus(): + node_list = load_node_list() + with ThreadPoolExecutor() as executor: + futures = [] + for node in node_list: + futures.append(executor.submit(node_stats, node)) + node_summary = {} + for future in as_completed(futures): + a = future.result() + node_summary.update(a) + + statusRows = [ + [ + 'Node Name', + 'Provide ID', + 'Instance Type', + 'Total CPU', + 'CPU Usage', + 'Percent CPU', + 'Total Memory', + 'Memory Usage', + 'Total Storage', + 'Current PODs', + ] + ] + for node in node_summary: + statusRows.append([str(x) for x in node_summary[node]]) + + tablify(statusRows) + + +def PrintPodStatus(namespace=None): + node_list = load_node_list() + with ThreadPoolExecutor() as executor: + futures = [] + for node in node_list: + futures.append(executor.submit(pod_stats, node, namespace)) + pod_summary = {} + for future in as_completed(futures): + a = future.result() + pod_summary.update(a) + + statusRows = [ + [ + 'Pod Name', + 'Namespace', + 'Phase', + 'POD IP', + 'POD Kind', + 'Worker Node', + 'CPU Usage', + 'Mem Usage', + 'Container Name:Restart', + 'Container Image', + ] + ] + if namespace: + del statusRows[0][1] + + for pod in pod_summary: + statusRows.append([str(x) for x in pod_summary[pod]]) + + tablify(statusRows) diff --git a/scripts/malcolm_utils.py b/scripts/malcolm_utils.py index 860ba873e..05ace70a0 100644 --- a/scripts/malcolm_utils.py +++ b/scripts/malcolm_utils.py @@ -438,6 +438,16 @@ def str2bool(v): raise ValueError("Boolean value expected") +################################################################################################### +# tablify +def tablify(matrix, file=sys.stdout): + colMaxLen = {i: max(map(len, inner)) for i, inner in enumerate(zip(*matrix))} + for row in matrix: + for col, data in enumerate(row): + print(f"{data:{colMaxLen[col]}}", end=" | ", file=file) + print(file=file) + + ################################################################################################### # a context manager returning a temporary filename which is deleted upon leaving the context @contextlib.contextmanager From 835c3510ebcd4bae960f9e50306deb2c937d3cdd Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 14 Apr 2023 09:57:51 -0600 Subject: [PATCH 152/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/control.py | 2 +- scripts/malcolm_kubernetes.py | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index 3b84bfbe3..1569c9099 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -385,7 +385,7 @@ def status(): malcolm_kubernetes.PrintPodStatus(namespace=args.namespace) print() except Exception as e: - eprint(f'Error listing {args.namespace} pods: {e}') + eprint(f'Error getting {args.namespace} status: {e}') exit(-1) diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index 517a22f42..fa45b9d93 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -66,6 +66,8 @@ def node_stats(node): pods = k8s_api.list_pod_for_all_namespaces(watch=False, field_selector=field_selector) node_dict[node] = [ api_response.metadata.name, + ','.join(list(set([x.address for x in api_response.status.addresses if not x.type.endswith('IP')]))), + ','.join(list(set([x.address for x in api_response.status.addresses if x.type.endswith('IP')]))), api_response.spec.provider_id.split('/')[-1], api_response.metadata.labels['node.kubernetes.io/instance-type'], _core_to_millicore(api_response.status.capacity['cpu']), @@ -173,7 +175,9 @@ def PrintNodeStatus(): statusRows = [ [ 'Node Name', - 'Provide ID', + 'Hostname', + 'IP', + 'Provider ID', 'Instance Type', 'Total CPU', 'CPU Usage', @@ -181,8 +185,8 @@ def PrintNodeStatus(): 'Total Memory', 'Memory Usage', 'Total Storage', - 'Current PODs', - ] + 'Current Pods', + ], ] for node in node_summary: statusRows.append([str(x) for x in node_summary[node]]) @@ -205,15 +209,15 @@ def PrintPodStatus(namespace=None): [ 'Pod Name', 'Namespace', - 'Phase', - 'POD IP', - 'POD Kind', + 'State', + 'Pod IP', + 'Pod Kind', 'Worker Node', 'CPU Usage', - 'Mem Usage', - 'Container Name:Restart', + 'Memory Usage', + 'Container Name:Restarts', 'Container Image', - ] + ], ] if namespace: del statusRows[0][1] From a376bb52f353b92db886fd61ae7ad2f1b9f52a31 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 14 Apr 2023 10:11:17 -0600 Subject: [PATCH 153/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/control.py | 827 ++++++++++++++++++++++++--------------------- 1 file changed, 433 insertions(+), 394 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index 1569c9099..8f76d21df 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -87,7 +87,6 @@ def __exit__(self, *args): dockerBin = None dockerComposeBin = None dockerComposeYaml = None -kubeClient = None kubeImported = None opensslBin = None orchMode = None @@ -350,6 +349,9 @@ def keystore_op(service, dropPriv=False, *keystore_args, **run_process_kwargs): else: eprint(e) + else: + raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') + # success = (error == 0) return (err == 0), results @@ -359,7 +361,6 @@ def status(): global args global dockerComposeBin global orchMode - global kubeClient if orchMode is OrchestrationFramework.DOCKER_COMPOSE: # docker-compose use local temporary path @@ -388,67 +389,26 @@ def status(): eprint(f'Error getting {args.namespace} status: {e}') exit(-1) + else: + raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') + ################################################################################################### def netboxBackup(backupFileName=None): global args global dockerComposeBin + global orchMode - # docker-compose use local temporary path - osEnv = os.environ.copy() - osEnv['TMPDIR'] = MalcolmTmpPath - - uidGidDict = GetUidGidFromComposeFile(args.composeFile) - - dockerCmd = [ - dockerComposeBin, - '-f', - args.composeFile, - 'exec', - # disable pseudo-TTY allocation - '-T', - # execute as UID:GID in docker-compose.yml file - '-u', - f'{uidGidDict["PUID"]}:{uidGidDict["PGID"]}', - 'netbox-postgres', - 'pg_dump', - '-U', - 'netbox', - '-d', - 'netbox', - ] - - err, results = run_process(dockerCmd, env=osEnv, debug=args.debug, stdout=True, stderr=False) - if (err != 0) or (len(results) == 0): - raise Exception('Error creating NetBox configuration database backup') - - if (backupFileName is None) or (len(backupFileName) == 0): - backupFileName = f"malcolm_netbox_backup_{time.strftime('%Y%m%d-%H%M%S')}.gz" - - with gzip.GzipFile(backupFileName, "wb") as f: - f.write(bytes('\n'.join(results), 'utf-8')) - - backupFileParts = os.path.splitext(backupFileName) - backupMediaFileName = backupFileParts[0] + ".media.tar.gz" - with tarfile.open(backupMediaFileName, 'w:gz') as t: - t.add(os.path.join(os.path.join(MalcolmPath, 'netbox'), 'media'), arcname='.') - - return backupFileName, backupMediaFileName - - -################################################################################################### -def netboxRestore(backupFileName=None): - global args - global dockerComposeBin + backupFileName, backupMediaFileName = None, None - if backupFileName and os.path.isfile(backupFileName): + if orchMode is OrchestrationFramework.DOCKER_COMPOSE: # docker-compose use local temporary path osEnv = os.environ.copy() osEnv['TMPDIR'] = MalcolmTmpPath uidGidDict = GetUidGidFromComposeFile(args.composeFile) - dockerCmdBase = [ + dockerCmd = [ dockerComposeBin, '-f', args.composeFile, @@ -458,47 +418,108 @@ def netboxRestore(backupFileName=None): # execute as UID:GID in docker-compose.yml file '-u', f'{uidGidDict["PUID"]}:{uidGidDict["PGID"]}', + 'netbox-postgres', + 'pg_dump', + '-U', + 'netbox', + '-d', + 'netbox', ] - # if the netbox_init.py process is happening, interrupt it - dockerCmd = dockerCmdBase + ['netbox', 'bash', '-c', 'pgrep -f /usr/local/bin/netbox_init.py | xargs -r kill'] - err, results = run_process(dockerCmd, env=osEnv, debug=args.debug) - if (err != 0) and args.debug: - eprint(f'Error interrupting netbox_init.py: {results}') - - # drop the existing netbox database - dockerCmd = dockerCmdBase + ['netbox-postgres', 'dropdb', '-U', 'netbox', 'netbox', '--force'] - err, results = run_process(dockerCmd, env=osEnv, debug=args.debug) - if ((err != 0) or (len(results) == 0)) and args.debug: - eprint(f'Error dropping NetBox database: {results}') - - # create a new netbox database - dockerCmd = dockerCmdBase + ['netbox-postgres', 'createdb', '-U', 'netbox', 'netbox'] - err, results = run_process(dockerCmd, env=osEnv, debug=args.debug) - if err != 0: - raise Exception('Error creating new NetBox database') - - # load the backed-up psql dump - dockerCmd = dockerCmdBase + ['netbox-postgres', 'psql', '-U', 'netbox'] - with gzip.open(backupFileName, 'rt') as f: - err, results = run_process(dockerCmd, env=osEnv, debug=args.debug, stdin=f.read()) + err, results = run_process(dockerCmd, env=osEnv, debug=args.debug, stdout=True, stderr=False) if (err != 0) or (len(results) == 0): - raise Exception('Error loading NetBox database') + raise Exception('Error creating NetBox configuration database backup') - # migrations if needed - dockerCmd = dockerCmdBase + ['netbox', '/opt/netbox/netbox/manage.py', 'migrate'] - err, results = run_process(dockerCmd, env=osEnv, debug=args.debug) - if (err != 0) or (len(results) == 0): - raise Exception('Error performing NetBox migration') + if (backupFileName is None) or (len(backupFileName) == 0): + backupFileName = f"malcolm_netbox_backup_{time.strftime('%Y%m%d-%H%M%S')}.gz" + + with gzip.GzipFile(backupFileName, "wb") as f: + f.write(bytes('\n'.join(results), 'utf-8')) - # restore media directory backupFileParts = os.path.splitext(backupFileName) backupMediaFileName = backupFileParts[0] + ".media.tar.gz" - mediaPath = os.path.join(os.path.join(MalcolmPath, 'netbox'), 'media') - if os.path.isfile(backupMediaFileName) and os.path.isdir(mediaPath): - RemoveEmptyFolders(mediaPath, removeRoot=False) - with tarfile.open(backupMediaFileName) as t: - t.extractall(mediaPath) + with tarfile.open(backupMediaFileName, 'w:gz') as t: + t.add(os.path.join(os.path.join(MalcolmPath, 'netbox'), 'media'), arcname='.') + + else: + raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') + + return backupFileName, backupMediaFileName + + +################################################################################################### +def netboxRestore(backupFileName=None): + global args + global dockerComposeBin + global orchMode + + if backupFileName and os.path.isfile(backupFileName): + if orchMode is OrchestrationFramework.DOCKER_COMPOSE: + # docker-compose use local temporary path + osEnv = os.environ.copy() + osEnv['TMPDIR'] = MalcolmTmpPath + + uidGidDict = GetUidGidFromComposeFile(args.composeFile) + + dockerCmdBase = [ + dockerComposeBin, + '-f', + args.composeFile, + 'exec', + # disable pseudo-TTY allocation + '-T', + # execute as UID:GID in docker-compose.yml file + '-u', + f'{uidGidDict["PUID"]}:{uidGidDict["PGID"]}', + ] + + # if the netbox_init.py process is happening, interrupt it + dockerCmd = dockerCmdBase + [ + 'netbox', + 'bash', + '-c', + 'pgrep -f /usr/local/bin/netbox_init.py | xargs -r kill', + ] + err, results = run_process(dockerCmd, env=osEnv, debug=args.debug) + if (err != 0) and args.debug: + eprint(f'Error interrupting netbox_init.py: {results}') + + # drop the existing netbox database + dockerCmd = dockerCmdBase + ['netbox-postgres', 'dropdb', '-U', 'netbox', 'netbox', '--force'] + err, results = run_process(dockerCmd, env=osEnv, debug=args.debug) + if ((err != 0) or (len(results) == 0)) and args.debug: + eprint(f'Error dropping NetBox database: {results}') + + # create a new netbox database + dockerCmd = dockerCmdBase + ['netbox-postgres', 'createdb', '-U', 'netbox', 'netbox'] + err, results = run_process(dockerCmd, env=osEnv, debug=args.debug) + if err != 0: + raise Exception('Error creating new NetBox database') + + # load the backed-up psql dump + dockerCmd = dockerCmdBase + ['netbox-postgres', 'psql', '-U', 'netbox'] + with gzip.open(backupFileName, 'rt') as f: + err, results = run_process(dockerCmd, env=osEnv, debug=args.debug, stdin=f.read()) + if (err != 0) or (len(results) == 0): + raise Exception('Error loading NetBox database') + + # migrations if needed + dockerCmd = dockerCmdBase + ['netbox', '/opt/netbox/netbox/manage.py', 'migrate'] + err, results = run_process(dockerCmd, env=osEnv, debug=args.debug) + if (err != 0) or (len(results) == 0): + raise Exception('Error performing NetBox migration') + + # restore media directory + backupFileParts = os.path.splitext(backupFileName) + backupMediaFileName = backupFileParts[0] + ".media.tar.gz" + mediaPath = os.path.join(os.path.join(MalcolmPath, 'netbox'), 'media') + if os.path.isfile(backupMediaFileName) and os.path.isdir(mediaPath): + RemoveEmptyFolders(mediaPath, removeRoot=False) + with tarfile.open(backupMediaFileName) as t: + t.extractall(mediaPath) + + else: + raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') ################################################################################################### @@ -506,6 +527,7 @@ def logs(): global args global dockerBin global dockerComposeBin + global orchMode urlUserPassRegEx = re.compile(r'(\w+://[^/]+?:)[^/]+?(@[^/]+)') @@ -580,153 +602,164 @@ def logs(): finishedStartingRegEx = re.compile(r'.+Pipelines\s+running\s+\{.*:non_running_pipelines=>\[\]\}') finishedStarting = False - # increase COMPOSE_HTTP_TIMEOUT to be ridiculously large so docker-compose never times out the TTY doing debug output - osEnv = os.environ.copy() - osEnv['COMPOSE_HTTP_TIMEOUT'] = '100000000' - # docker-compose use local temporary path - osEnv['TMPDIR'] = MalcolmTmpPath + if orchMode is OrchestrationFramework.DOCKER_COMPOSE: + # increase COMPOSE_HTTP_TIMEOUT to be ridiculously large so docker-compose never times out the TTY doing debug output + osEnv = os.environ.copy() + osEnv['COMPOSE_HTTP_TIMEOUT'] = '100000000' + # docker-compose use local temporary path + osEnv['TMPDIR'] = MalcolmTmpPath - err, out = run_process( - [dockerComposeBin, '-f', args.composeFile, 'ps', args.service][: 5 if args.service is not None else -1], - env=osEnv, - debug=args.debug, - ) - print("\n".join(out)) + err, out = run_process( + [dockerComposeBin, '-f', args.composeFile, 'ps', args.service][: 5 if args.service is not None else -1], + env=osEnv, + debug=args.debug, + ) + print("\n".join(out)) + + if args.logLineCount is None: + args.logLineCount = 'all' + + process = Popen( + [ + dockerComposeBin, + '-f', + args.composeFile, + 'logs', + '--tail', + str(args.logLineCount), + '-f', + args.service, + ][: 8 if args.service is not None else -1], + env=osEnv, + stdout=PIPE, + stderr=None if args.debug else DEVNULL, + ) + while True: + output = process.stdout.readline() + if (len(output) == 0) and (process.poll() is not None): + break + if output: + outputStr = urlUserPassRegEx.sub(r"\1xxxxxxxx\2", output.decode().strip()) + outputStrEscaped = EscapeAnsi(outputStr) + if ignoreRegEx.match(outputStrEscaped): + # print(f'!!!!!!!: {outputStr}') + pass + elif ( + (args.cmdStart or args.cmdRestart) + and (not args.cmdLogs) + and finishedStartingRegEx.match(outputStrEscaped) + ): + finishedStarting = True + else: + serviceMatch = serviceRegEx.search(outputStrEscaped) + serviceMatchFmt = serviceRegEx.search(outputStr) if coloramaImported else serviceMatch + serviceStr = serviceMatchFmt.group('service') if (serviceMatchFmt is not None) else '' + + messageStr = serviceMatch.group('message') if (serviceMatch is not None) else '' + messageStrSplit = messageStr.split(' ') + messageTimeMatch = iso8601TimeRegEx.match(messageStrSplit[0]) + if (messageTimeMatch is None) or (len(messageStrSplit) <= 1): + messageStrToTestJson = messageStr + else: + messageStrToTestJson = messageStrSplit[1:].join(' ') + + outputJson = LoadStrIfJson(messageStrToTestJson) + if isinstance(outputJson, dict): + # if there's a timestamp, move it outside of the JSON to the beginning of the log string + timeKey = None + if 'time' in outputJson: + timeKey = 'time' + elif 'timestamp' in outputJson: + timeKey = 'timestamp' + elif '@timestamp' in outputJson: + timeKey = '@timestamp' + timeStr = '' + if timeKey is not None: + timeStr = f"{outputJson[timeKey]} " + outputJson.pop(timeKey, None) + elif messageTimeMatch is not None: + timeStr = f"{messageTimeMatch[0]} " - if args.logLineCount is None: - args.logLineCount = 'all' + if ( + ('job.schedule' in outputJson) + and ('job.position' in outputJson) + and ('job.command' in outputJson) + ): + # this is a status output line from supercronic, let's format and clean it up so it fits in better with the rest of the logs - process = Popen( - [ - dockerComposeBin, - '-f', - args.composeFile, - 'logs', - '--tail', - str(args.logLineCount), - '-f', - args.service, - ][: 8 if args.service is not None else -1], - env=osEnv, - stdout=PIPE, - stderr=None if args.debug else DEVNULL, - ) - while True: - output = process.stdout.readline() - if (len(output) == 0) and (process.poll() is not None): - break - if output: - outputStr = urlUserPassRegEx.sub(r"\1xxxxxxxx\2", output.decode().strip()) - outputStrEscaped = EscapeAnsi(outputStr) - if ignoreRegEx.match(outputStrEscaped): - # print(f'!!!!!!!: {outputStr}') - pass - elif ( - (args.cmdStart or args.cmdRestart) - and (not args.cmdLogs) - and finishedStartingRegEx.match(outputStrEscaped) - ): - finishedStarting = True - else: - serviceMatch = serviceRegEx.search(outputStrEscaped) - serviceMatchFmt = serviceRegEx.search(outputStr) if coloramaImported else serviceMatch - serviceStr = serviceMatchFmt.group('service') if (serviceMatchFmt is not None) else '' - - messageStr = serviceMatch.group('message') if (serviceMatch is not None) else '' - messageStrSplit = messageStr.split(' ') - messageTimeMatch = iso8601TimeRegEx.match(messageStrSplit[0]) - if (messageTimeMatch is None) or (len(messageStrSplit) <= 1): - messageStrToTestJson = messageStr - else: - messageStrToTestJson = messageStrSplit[1:].join(' ') - - outputJson = LoadStrIfJson(messageStrToTestJson) - if isinstance(outputJson, dict): - # if there's a timestamp, move it outside of the JSON to the beginning of the log string - timeKey = None - if 'time' in outputJson: - timeKey = 'time' - elif 'timestamp' in outputJson: - timeKey = 'timestamp' - elif '@timestamp' in outputJson: - timeKey = '@timestamp' - timeStr = '' - if timeKey is not None: - timeStr = f"{outputJson[timeKey]} " - outputJson.pop(timeKey, None) - elif messageTimeMatch is not None: - timeStr = f"{messageTimeMatch[0]} " - - if ( - ('job.schedule' in outputJson) - and ('job.position' in outputJson) - and ('job.command' in outputJson) - ): - # this is a status output line from supercronic, let's format and clean it up so it fits in better with the rest of the logs - - # remove some clutter for the display - for noisyKey in ['level', 'channel', 'iteration', 'job.position', 'job.schedule']: - outputJson.pop(noisyKey, None) - - # if it's just command and message, format those NOT as JSON - jobCmd = outputJson['job.command'] - jobStatus = outputJson['msg'] - if (len(outputJson.keys()) == 2) and ('job.command' in outputJson) and ('msg' in outputJson): - # if it's the most common status (starting or job succeeded) then don't print unless debug mode - if args.debug or ((jobStatus != 'starting') and (jobStatus != 'job succeeded')): + # remove some clutter for the display + for noisyKey in ['level', 'channel', 'iteration', 'job.position', 'job.schedule']: + outputJson.pop(noisyKey, None) + + # if it's just command and message, format those NOT as JSON + jobCmd = outputJson['job.command'] + jobStatus = outputJson['msg'] + if ( + (len(outputJson.keys()) == 2) + and ('job.command' in outputJson) + and ('msg' in outputJson) + ): + # if it's the most common status (starting or job succeeded) then don't print unless debug mode + if args.debug or ((jobStatus != 'starting') and (jobStatus != 'job succeeded')): + print( + f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr} {jobCmd}: {jobStatus}" + ) + else: + pass + + else: + # standardize and print the JSON output print( - f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr} {jobCmd}: {jobStatus}" + f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" ) - else: - pass - else: + elif 'dashboards' in serviceStr: + # this is an output line from dashboards, let's clean it up a bit: remove some clutter for the display + for noisyKey in ['type', 'tags', 'pid', 'method', 'prevState', 'prevMsg']: + outputJson.pop(noisyKey, None) + # standardize and print the JSON output print( f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" ) - elif 'dashboards' in serviceStr: - # this is an output line from dashboards, let's clean it up a bit: remove some clutter for the display - for noisyKey in ['type', 'tags', 'pid', 'method', 'prevState', 'prevMsg']: - outputJson.pop(noisyKey, None) - - # standardize and print the JSON output - print( - f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" - ) + elif 'filebeat' in serviceStr: + # this is an output line from filebeat, let's clean it up a bit: remove some clutter for the display + for noisyKey in [ + 'ecs.version', + 'harvester_id', + 'input_id', + 'log.level', + 'log.logger', + 'log.origin', + 'os_id', + 'service.name', + 'state_id', + ]: + outputJson.pop(noisyKey, None) + + # we'll fancify a couple of common things from filebeat + if ( + (len(outputJson.keys()) == 3) + and ('message' in outputJson) + and ('source_file' in outputJson) + and ('finished' in outputJson) + ): + print( + f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{outputJson['message'].rstrip('.')}: {outputJson['source_file']}" + ) - elif 'filebeat' in serviceStr: - # this is an output line from filebeat, let's clean it up a bit: remove some clutter for the display - for noisyKey in [ - 'ecs.version', - 'harvester_id', - 'input_id', - 'log.level', - 'log.logger', - 'log.origin', - 'os_id', - 'service.name', - 'state_id', - ]: - outputJson.pop(noisyKey, None) - - # we'll fancify a couple of common things from filebeat - if ( - (len(outputJson.keys()) == 3) - and ('message' in outputJson) - and ('source_file' in outputJson) - and ('finished' in outputJson) - ): - print( - f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{outputJson['message'].rstrip('.')}: {outputJson['source_file']}" - ) + elif len(outputJson.keys()) == 1: + outputKey = next(iter(outputJson)) + print( + f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{outputKey + ': ' if outputKey != 'message' else ''}{outputJson[outputKey]}" + ) + else: + # standardize and print the JSON output + print( + f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" + ) - elif len(outputJson.keys()) == 1: - outputKey = next(iter(outputJson)) - print( - f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{outputKey + ': ' if outputKey != 'message' else ''}{outputJson[outputKey]}" - ) else: # standardize and print the JSON output print( @@ -734,38 +767,35 @@ def logs(): ) else: - # standardize and print the JSON output - print( - f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" - ) + # just a regular non-JSON string, print as-is + print(outputStr if coloramaImported else outputStrEscaped) - else: - # just a regular non-JSON string, print as-is - print(outputStr if coloramaImported else outputStrEscaped) + else: + time.sleep(0.5) + + if finishedStarting: + process.terminate() + try: + process.wait(timeout=5.0) + except TimeoutExpired: + process.kill() + # # TODO: Replace 'localhost' with an outwards-facing IP since I doubt anybody is + # accessing these from the Malcolm server. + print("\nStarted Malcolm\n\n") + print("Malcolm services can be accessed via the following URLs:") + print("------------------------------------------------------------------------------") + print(" - Arkime: https://localhost/") + print(" - OpenSearch Dashboards: https://localhost/dashboards/") + print(" - PCAP upload (web): https://localhost/upload/") + print(" - PCAP upload (sftp): sftp://username@127.0.0.1:8022/files/") + print(" - NetBox: https://localhost/netbox/\n") + print(" - Account management: https://localhost/auth/\n") + print(" - Documentation: https://localhost/readme/\n") + + process.poll() - else: - time.sleep(0.5) - - if finishedStarting: - process.terminate() - try: - process.wait(timeout=5.0) - except TimeoutExpired: - process.kill() - # # TODO: Replace 'localhost' with an outwards-facing IP since I doubt anybody is - # accessing these from the Malcolm server. - print("\nStarted Malcolm\n\n") - print("Malcolm services can be accessed via the following URLs:") - print("------------------------------------------------------------------------------") - print(" - Arkime: https://localhost/") - print(" - OpenSearch Dashboards: https://localhost/dashboards/") - print(" - PCAP upload (web): https://localhost/upload/") - print(" - PCAP upload (sftp): sftp://username@127.0.0.1:8022/files/") - print(" - NetBox: https://localhost/netbox/\n") - print(" - Account management: https://localhost/auth/\n") - print(" - Documentation: https://localhost/readme/\n") - - process.poll() + else: + raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') ################################################################################################### @@ -774,88 +804,95 @@ def stop(wipe=False): global dockerBin global dockerComposeBin global dockerComposeYaml + global orchMode - # docker-compose use local temporary path - osEnv = os.environ.copy() - osEnv['TMPDIR'] = MalcolmTmpPath + if orchMode is OrchestrationFramework.DOCKER_COMPOSE: + # docker-compose use local temporary path + osEnv = os.environ.copy() + osEnv['TMPDIR'] = MalcolmTmpPath - # if stop.sh is being called with wipe.sh (after the docker-compose file) - # then also remove named and anonymous volumes (not external volumes, of course) - err, out = run_process( - [dockerComposeBin, '-f', args.composeFile, 'down', '--volumes'][: 5 if wipe else -1], - env=osEnv, - debug=args.debug, - ) - if err == 0: - eprint("Stopped Malcolm\n") - else: - eprint("Malcolm failed to stop\n") - eprint("\n".join(out)) - exit(err) - - if wipe: - # there is some overlap here among some of these containers, but it doesn't matter - boundPathsToWipe = ( - BoundPath("arkime", "/opt/arkime/logs", True, None, None), - BoundPath("arkime", "/opt/arkime/raw", True, None, None), - BoundPath("filebeat", "/zeek", True, None, None), - BoundPath("file-monitor", "/zeek/logs", True, None, None), - BoundPath("netbox", "/opt/netbox/netbox/media", True, None, ["."]), - BoundPath("netbox-postgres", "/var/lib/postgresql/data", True, None, ["."]), - BoundPath("netbox-redis", "/data", True, None, ["."]), - BoundPath("opensearch", "/usr/share/opensearch/data", True, ["nodes"], None), - BoundPath("pcap-monitor", "/pcap", True, ["processed", "upload"], None), - BoundPath("suricata", "/var/log/suricata", True, None, ["."]), - BoundPath("upload", "/var/www/upload/server/php/chroot/files", True, None, None), - BoundPath("zeek", "/zeek/extract_files", True, None, None), - BoundPath("zeek", "/zeek/upload", True, None, None), - BoundPath("zeek-live", "/zeek/live", True, ["spool"], None), - BoundPath( - "filebeat", - "/zeek", - False, - ["processed", "current", "live"], - ["processed", "current", "live"], - ), + # if stop.sh is being called with wipe.sh (after the docker-compose file) + # then also remove named and anonymous volumes (not external volumes, of course) + err, out = run_process( + [dockerComposeBin, '-f', args.composeFile, 'down', '--volumes'][: 5 if wipe else -1], + env=osEnv, + debug=args.debug, ) - for boundPath in boundPathsToWipe: - localPath = LocalPathForContainerBindMount( - boundPath.service, - dockerComposeYaml, - boundPath.container_dir, - MalcolmPath, + if err == 0: + eprint("Stopped Malcolm\n") + else: + eprint("Malcolm failed to stop\n") + eprint("\n".join(out)) + exit(err) + + if wipe: + # there is some overlap here among some of these containers, but it doesn't matter + boundPathsToWipe = ( + BoundPath("arkime", "/opt/arkime/logs", True, None, None), + BoundPath("arkime", "/opt/arkime/raw", True, None, None), + BoundPath("filebeat", "/zeek", True, None, None), + BoundPath("file-monitor", "/zeek/logs", True, None, None), + BoundPath("netbox", "/opt/netbox/netbox/media", True, None, ["."]), + BoundPath("netbox-postgres", "/var/lib/postgresql/data", True, None, ["."]), + BoundPath("netbox-redis", "/data", True, None, ["."]), + BoundPath("opensearch", "/usr/share/opensearch/data", True, ["nodes"], None), + BoundPath("pcap-monitor", "/pcap", True, ["processed", "upload"], None), + BoundPath("suricata", "/var/log/suricata", True, None, ["."]), + BoundPath("upload", "/var/www/upload/server/php/chroot/files", True, None, None), + BoundPath("zeek", "/zeek/extract_files", True, None, None), + BoundPath("zeek", "/zeek/upload", True, None, None), + BoundPath("zeek-live", "/zeek/live", True, ["spool"], None), + BoundPath( + "filebeat", + "/zeek", + False, + ["processed", "current", "live"], + ["processed", "current", "live"], + ), ) - if localPath and os.path.isdir(localPath): - # delete files - if boundPath.files: - if args.debug: - eprint(f'Walking "{localPath}" for file deletion') - for root, dirnames, filenames in os.walk(localPath, topdown=True, onerror=None): - for file in filenames: - fileSpec = os.path.join(root, file) - if (os.path.isfile(fileSpec) or os.path.islink(fileSpec)) and (not file.startswith('.git')): - try: - os.remove(fileSpec) - except Exception: - pass - # delete whole directories - if boundPath.relative_dirs: - for relDir in get_iterable(boundPath.relative_dirs): - tmpPath = os.path.join(localPath, relDir) - if os.path.isdir(tmpPath): - if args.debug: - eprint(f'Performing rmtree on "{tmpPath}"') - shutil.rmtree(tmpPath, ignore_errors=True) - # cleanup empty directories - if boundPath.clean_empty_dirs: - for cleanDir in get_iterable(boundPath.clean_empty_dirs): - tmpPath = os.path.join(localPath, cleanDir) - if os.path.isdir(tmpPath): - if args.debug: - eprint(f'Performing RemoveEmptyFolders on "{tmpPath}"') - RemoveEmptyFolders(tmpPath, removeRoot=False) + for boundPath in boundPathsToWipe: + localPath = LocalPathForContainerBindMount( + boundPath.service, + dockerComposeYaml, + boundPath.container_dir, + MalcolmPath, + ) + if localPath and os.path.isdir(localPath): + # delete files + if boundPath.files: + if args.debug: + eprint(f'Walking "{localPath}" for file deletion') + for root, dirnames, filenames in os.walk(localPath, topdown=True, onerror=None): + for file in filenames: + fileSpec = os.path.join(root, file) + if (os.path.isfile(fileSpec) or os.path.islink(fileSpec)) and ( + not file.startswith('.git') + ): + try: + os.remove(fileSpec) + except Exception: + pass + # delete whole directories + if boundPath.relative_dirs: + for relDir in get_iterable(boundPath.relative_dirs): + tmpPath = os.path.join(localPath, relDir) + if os.path.isdir(tmpPath): + if args.debug: + eprint(f'Performing rmtree on "{tmpPath}"') + shutil.rmtree(tmpPath, ignore_errors=True) + # cleanup empty directories + if boundPath.clean_empty_dirs: + for cleanDir in get_iterable(boundPath.clean_empty_dirs): + tmpPath = os.path.join(localPath, cleanDir) + if os.path.isdir(tmpPath): + if args.debug: + eprint(f'Performing RemoveEmptyFolders on "{tmpPath}"') + RemoveEmptyFolders(tmpPath, removeRoot=False) + + eprint("Malcolm has been stopped and its data cleared\n") - eprint("Malcolm has been stopped and its data cleared\n") + else: + raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') ################################################################################################### @@ -863,6 +900,7 @@ def start(): global args global dockerBin global dockerComposeBin + global orchMode # touch the htadmin metadata file and .opensearch.*.curlrc files open(os.path.join(MalcolmPath, os.path.join('htadmin', 'metadata')), 'a').close() @@ -904,81 +942,85 @@ def start(): # chmod 600 envFile os.chmod(envFile, stat.S_IRUSR | stat.S_IWUSR) - # make sure some directories exist before we start - boundPathsToCreate = ( - BoundPath("arkime", "/opt/arkime/logs", False, None, None), - BoundPath("arkime", "/opt/arkime/raw", False, None, None), - BoundPath("file-monitor", "/zeek/logs", False, None, None), - BoundPath("nginx-proxy", "/var/local/ca-trust", False, None, None), - BoundPath("netbox", "/opt/netbox/netbox/media", False, None, None), - BoundPath("netbox-postgres", "/var/lib/postgresql/data", False, None, None), - BoundPath("netbox-redis", "/data", False, None, None), - BoundPath("opensearch", "/usr/share/opensearch/data", False, ["nodes"], None), - BoundPath("opensearch", "/opt/opensearch/backup", False, None, None), - BoundPath("pcap-monitor", "/pcap", False, ["processed", "upload"], None), - BoundPath("suricata", "/var/log/suricata", False, ["live"], None), - BoundPath("upload", "/var/www/upload/server/php/chroot/files", False, None, None), - BoundPath("zeek", "/zeek/extract_files", False, None, None), - BoundPath("zeek", "/zeek/upload", False, None, None), - BoundPath("zeek", "/opt/zeek/share/zeek/site/intel", False, ["MISP", "STIX"], None), - BoundPath("zeek-live", "/zeek/live", False, ["spool"], None), - BoundPath("filebeat", "/zeek", False, ["processed", "current", "live", "extract_files", "upload"], None), - ) - for boundPath in boundPathsToCreate: - localPath = LocalPathForContainerBindMount( - boundPath.service, - dockerComposeYaml, - boundPath.container_dir, - MalcolmPath, - ) - if localPath: - try: - if args.debug: - eprint(f'Ensuring "{localPath}" exists') - os.makedirs(localPath) - except OSError as exc: - if (exc.errno == errno.EEXIST) and os.path.isdir(localPath): - pass - else: - raise - if boundPath.relative_dirs: - for relDir in get_iterable(boundPath.relative_dirs): - tmpPath = os.path.join(localPath, relDir) - try: - if args.debug: - eprint(f'Ensuring "{tmpPath}" exists') - os.makedirs(tmpPath) - except OSError as exc: - if (exc.errno == errno.EEXIST) and os.path.isdir(tmpPath): - pass - else: - raise - # touch the zeek intel file open(os.path.join(MalcolmPath, os.path.join('zeek', os.path.join('intel', '__load__.zeek'))), 'a').close() # clean up any leftover intel update locks shutil.rmtree(os.path.join(MalcolmPath, os.path.join('zeek', os.path.join('intel', 'lock'))), ignore_errors=True) - # increase COMPOSE_HTTP_TIMEOUT to be ridiculously large so docker-compose never times out the TTY doing debug output - osEnv = os.environ.copy() - osEnv['COMPOSE_HTTP_TIMEOUT'] = '100000000' - # docker-compose use local temporary path - osEnv['TMPDIR'] = MalcolmTmpPath + if orchMode is OrchestrationFramework.DOCKER_COMPOSE: + # make sure some directories exist before we start + boundPathsToCreate = ( + BoundPath("arkime", "/opt/arkime/logs", False, None, None), + BoundPath("arkime", "/opt/arkime/raw", False, None, None), + BoundPath("file-monitor", "/zeek/logs", False, None, None), + BoundPath("nginx-proxy", "/var/local/ca-trust", False, None, None), + BoundPath("netbox", "/opt/netbox/netbox/media", False, None, None), + BoundPath("netbox-postgres", "/var/lib/postgresql/data", False, None, None), + BoundPath("netbox-redis", "/data", False, None, None), + BoundPath("opensearch", "/usr/share/opensearch/data", False, ["nodes"], None), + BoundPath("opensearch", "/opt/opensearch/backup", False, None, None), + BoundPath("pcap-monitor", "/pcap", False, ["processed", "upload"], None), + BoundPath("suricata", "/var/log/suricata", False, ["live"], None), + BoundPath("upload", "/var/www/upload/server/php/chroot/files", False, None, None), + BoundPath("zeek", "/zeek/extract_files", False, None, None), + BoundPath("zeek", "/zeek/upload", False, None, None), + BoundPath("zeek", "/opt/zeek/share/zeek/site/intel", False, ["MISP", "STIX"], None), + BoundPath("zeek-live", "/zeek/live", False, ["spool"], None), + BoundPath("filebeat", "/zeek", False, ["processed", "current", "live", "extract_files", "upload"], None), + ) + for boundPath in boundPathsToCreate: + localPath = LocalPathForContainerBindMount( + boundPath.service, + dockerComposeYaml, + boundPath.container_dir, + MalcolmPath, + ) + if localPath: + try: + if args.debug: + eprint(f'Ensuring "{localPath}" exists') + os.makedirs(localPath) + except OSError as exc: + if (exc.errno == errno.EEXIST) and os.path.isdir(localPath): + pass + else: + raise + if boundPath.relative_dirs: + for relDir in get_iterable(boundPath.relative_dirs): + tmpPath = os.path.join(localPath, relDir) + try: + if args.debug: + eprint(f'Ensuring "{tmpPath}" exists') + os.makedirs(tmpPath) + except OSError as exc: + if (exc.errno == errno.EEXIST) and os.path.isdir(tmpPath): + pass + else: + raise + + # increase COMPOSE_HTTP_TIMEOUT to be ridiculously large so docker-compose never times out the TTY doing debug output + osEnv = os.environ.copy() + osEnv['COMPOSE_HTTP_TIMEOUT'] = '100000000' + # docker-compose use local temporary path + osEnv['TMPDIR'] = MalcolmTmpPath - # start docker - err, out = run_process([dockerComposeBin, '-f', args.composeFile, 'up', '--detach'], env=osEnv, debug=args.debug) - if err != 0: - eprint("Malcolm failed to start\n") - eprint("\n".join(out)) - exit(err) + # start docker + err, out = run_process( + [dockerComposeBin, '-f', args.composeFile, 'up', '--detach'], env=osEnv, debug=args.debug + ) + if err != 0: + eprint("Malcolm failed to start\n") + eprint("\n".join(out)) + exit(err) + + else: + raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') ################################################################################################### def authSetup(wipe=False): global args - global dockerBin - global dockerComposeBin global opensslBin # for beats/logstash self-signed certificates @@ -1638,14 +1680,13 @@ def authSetup(wipe=False): # main def main(): global args - global orchMode global dockerBin global dockerComposeBin + global dockerComposeYaml + global kubeImported global opensslBin + global orchMode global yamlImported - global kubeClient - global kubeImported - global dockerComposeYaml # extract arguments from the command line # print (sys.argv[1:]); @@ -1890,8 +1931,6 @@ def main(): eprint(f"Imported kubernetes: {kubeImported}") if kubeImported: kubeImported.config.load_kube_config(args.composeFile) - kubeClient = kubeImported.client.CoreV1Api() - kubeClient.list_node() else: raise Exception( f'{ScriptName} requires the official Python client library for kubernetes for {orchMode} mode' From cceb0852fb30539344a60b332666f1aea9b8e823 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 14 Apr 2023 10:22:32 -0600 Subject: [PATCH 154/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/control.py | 51 +++++++++-------------------------- scripts/malcolm_common.py | 33 ++++++++++++++++++++++- scripts/malcolm_kubernetes.py | 8 ++++-- 3 files changed, 51 insertions(+), 41 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index 8f76d21df..ede737f9b 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -22,11 +22,11 @@ import time from malcolm_common import ( - MalcolmTmpPath, AskForPassword, AskForString, BoundPath, ChooseOne, + DetermineYamlFileFormat, DisplayMessage, DisplayProgramBox, GetUidGidFromComposeFile, @@ -35,6 +35,9 @@ MainDialog, MalcolmAuthFilesExist, MalcolmPath, + MalcolmTmpPath, + OrchestrationFramework, + OrchestrationFrameworksSupported, PLATFORM_WINDOWS, posInt, ScriptPath, @@ -56,10 +59,14 @@ str2bool, pushd, ) -import malcolm_kubernetes + +from malcolm_kubernetes import ( + PrintPodStatus, + PrintNodeStatus, +) + from base64 import b64encode from collections import defaultdict, namedtuple -from enum import Flag, auto from subprocess import PIPE, STDOUT, DEVNULL, Popen, TimeoutExpired from urllib.parse import urlparse @@ -93,16 +100,6 @@ def __exit__(self, *args): yamlImported = None -################################################################################################### -class OrchestrationFramework(Flag): - UNKNOWN = auto() - DOCKER_COMPOSE = auto() - KUBERNETES = auto() - - -OrchestrationFrameworksSupported = OrchestrationFramework.DOCKER_COMPOSE | OrchestrationFramework.KUBERNETES - - ################################################################################################### try: from colorama import init as ColoramaInit, Fore, Back, Style @@ -113,28 +110,6 @@ class OrchestrationFramework(Flag): coloramaImported = False -################################################################################################### -# determine if a YAML file looks like a docker-compose.yml file or a kubeconfig file -def determineYamlFileFormat(inputFileName): - global yamlImported - - result = OrchestrationFramework.UNKNOWN - try: - with open(inputFileName, 'r') as cf: - orchestrationYaml = yamlImported.safe_load(cf) - - if isinstance(orchestrationYaml, dict): - if any(key in orchestrationYaml for key in ('apiVersion', 'clusters', 'contexts', 'kind')): - result = OrchestrationFramework.KUBERNETES - elif 'services' in orchestrationYaml: - result = OrchestrationFramework.DOCKER_COMPOSE - - except Exception as e: - eprint(f'Error deciphering {args.composeFile}: {e}') - - return result - - ################################################################################################### def checkEnvFilesExist(): global args @@ -381,9 +356,9 @@ def status(): elif orchMode is OrchestrationFramework.KUBERNETES: try: - malcolm_kubernetes.PrintNodeStatus() + PrintNodeStatus() print() - malcolm_kubernetes.PrintPodStatus(namespace=args.namespace) + PrintPodStatus(namespace=args.namespace) print() except Exception as e: eprint(f'Error getting {args.namespace} status: {e}') @@ -1858,7 +1833,7 @@ def main(): if not yamlImported: exit(2) - if not ((orchMode := determineYamlFileFormat(args.composeFile)) and (orchMode in OrchestrationFrameworksSupported)): + if not ((orchMode := DetermineYamlFileFormat(args.composeFile)) and (orchMode in OrchestrationFrameworksSupported)): raise Exception(f'{args.composeFile} must be a docker-compose or kubeconfig YAML file') with pushd(MalcolmPath): diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index baab2b868..dc741628c 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -15,7 +15,7 @@ from malcolm_utils import eprint, str2bool, run_process, deep_get from collections import defaultdict, namedtuple -from enum import IntFlag, auto +from enum import Flag, IntFlag, auto try: from pwd import getpwuid @@ -81,6 +81,15 @@ class UserInterfaceMode(IntFlag): HOMEBREW_INSTALL_URLS = defaultdict(lambda: 'https://brew.sh/') +class OrchestrationFramework(Flag): + UNKNOWN = auto() + DOCKER_COMPOSE = auto() + KUBERNETES = auto() + + +OrchestrationFrameworksSupported = OrchestrationFramework.DOCKER_COMPOSE | OrchestrationFramework.KUBERNETES + + ################################################################################################## def ReplaceBindMountLocation(line, location, linePrefix): if os.path.isdir(location): @@ -585,6 +594,28 @@ def MalcolmAuthFilesExist(configDir=None): ) +################################################################################################### +# determine if a YAML file looks like a docker-compose.yml file or a kubeconfig file +def DetermineYamlFileFormat(inputFileName): + result = OrchestrationFramework.UNKNOWN + + if yamlImported := YAMLDynamic(): + try: + with open(inputFileName, 'r') as cf: + orchestrationYaml = yamlImported.safe_load(cf) + + if isinstance(orchestrationYaml, dict): + if any(key in orchestrationYaml for key in ('apiVersion', 'clusters', 'contexts', 'kind')): + result = OrchestrationFramework.KUBERNETES + elif 'services' in orchestrationYaml: + result = OrchestrationFramework.DOCKER_COMPOSE + + except Exception as e: + eprint(f'Error deciphering {inputFileName}: {e}') + + return result + + ################################################################################################### # download to file def DownloadToFile(url, local_filename, debug=False): diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index fa45b9d93..617de8ba8 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -3,18 +3,22 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. -MALCOLM_IMAGE_PREFIX = 'ghcr.io/idaholab/malcolm/' - from concurrent.futures import ThreadPoolExecutor, as_completed from malcolm_common import ( KubernetesDynamic, ) from malcolm_utils import ( + eprint, tablify, ) +################################################################################################### +MALCOLM_IMAGE_PREFIX = 'ghcr.io/idaholab/malcolm/' + + +################################################################################################### def _nanocore_to_millicore(n): n = int(n[:-1]) return str(round(n / 1000000, 2)) + 'm' From d7c461cee978894f9a1708163884abb2efc9be71 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 14 Apr 2023 11:26:56 -0600 Subject: [PATCH 155/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/control.py | 4 +- scripts/install.py | 1861 +++++++++++++++++++++++--------------------- 2 files changed, 977 insertions(+), 888 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index ede737f9b..1178dd0ba 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -1827,7 +1827,7 @@ def main(): else: sys.tracebacklimit = 0 - yamlImported = YAMLDynamic(debug=args.debug, forceInteraction=sys.__stdin__.isatty()) + yamlImported = YAMLDynamic(debug=args.debug) if args.debug: eprint(f"Imported yaml: {yamlImported}") if not yamlImported: @@ -1901,7 +1901,7 @@ def main(): dockerComposeYaml = yamlImported.safe_load(cf) elif orchMode is OrchestrationFramework.KUBERNETES: - kubeImported = KubernetesDynamic(debug=args.debug, forceInteraction=sys.__stdin__.isatty()) + kubeImported = KubernetesDynamic(debug=args.debug) if args.debug: eprint(f"Imported kubernetes: {kubeImported}") if kubeImported: diff --git a/scripts/install.py b/scripts/install.py index b219fe4aa..8b333f7f9 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -32,14 +32,18 @@ AskForString, ChooseMultiple, ChooseOne, + DetermineYamlFileFormat, DisplayMessage, DOCKER_COMPOSE_INSTALL_URLS, DOCKER_INSTALL_URLS, DotEnvDynamic, DownloadToFile, HOMEBREW_INSTALL_URLS, + KubernetesDynamic, MalcolmCfgRunOnceFile, MalcolmPath, + OrchestrationFramework, + OrchestrationFrameworksSupported, PLATFORM_LINUX, PLATFORM_LINUX_CENTOS, PLATFORM_LINUX_DEBIAN, @@ -85,6 +89,7 @@ args = None requests_imported = None yaml_imported = None +kube_imported = None dotenv_imported = None ################################################################################################### @@ -203,7 +208,8 @@ def InstallerDisplayMessage( ################################################################################################### class Installer(object): # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - def __init__(self, debug=False, configOnly=False): + def __init__(self, orchMode, debug=False, configOnly=False): + self.orchMode = orchMode self.debug = debug self.configOnly = configOnly @@ -282,18 +288,21 @@ def install_required_packages(self): # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def install_docker_images(self, docker_image_file): result = False - if ( - docker_image_file - and os.path.isfile(docker_image_file) - and InstallerYesOrNo( - f'Load Malcolm Docker images from {docker_image_file}', default=True, forceInteraction=True - ) - ): - ecode, out = self.run_process(['docker', 'load', '-q', '-i', docker_image_file], privileged=True) - if ecode == 0: - result = True - else: - eprint(f"Loading Malcolm Docker images failed: {out}") + + if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: + if ( + docker_image_file + and os.path.isfile(docker_image_file) + and InstallerYesOrNo( + f'Load Malcolm Docker images from {docker_image_file}', default=True, forceInteraction=True + ) + ): + ecode, out = self.run_process(['docker', 'load', '-q', '-i', docker_image_file], privileged=True) + if ecode == 0: + result = True + else: + eprint(f"Loading Malcolm Docker images failed: {out}") + return result # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -372,17 +381,25 @@ def tweak_malcolm_runtime( global args global dotenv_imported - composeFiles = [] + configFiles = [] + + if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: + # determine docker-compose files + if not args.configFile: + # get a list of all of the docker-compose files + configFiles = glob.glob(os.path.join(malcolm_install_path, 'docker-compose*.yml')) - # determine docker-compose file(s) - if not args.configFile: - # get a list of all of the docker-compose files - composeFiles = glob.glob(os.path.join(malcolm_install_path, 'docker-compose*.yml')) + elif os.path.isfile(args.configFile): + # single docker-compose file explicitly specified + configFiles = [os.path.realpath(args.configFile)] + malcolm_install_path = os.path.dirname(configFiles[0]) - elif os.path.isfile(args.configFile): - # single docker-compose file explicitly specified - composeFiles = [os.path.realpath(args.configFile)] - malcolm_install_path = os.path.dirname(composeFiles[0]) + elif self.orchMode is OrchestrationFramework.KUBERNETES: + if args.configFile and os.path.isfile(args.configFile): + configFiles = [os.path.realpath(args.configFile)] + malcolm_install_path = os.path.realpath(os.path.join(ScriptPath, "..")) + else: + raise Exception(f"{self.orchMode} requires specifying kubeconfig file via -f/--config-file") if (not args.configDir) or (not os.path.isdir(args.configDir)): raise Exception("Could not determine configuration directory containing Malcolm's .env files") @@ -412,36 +429,40 @@ def tweak_malcolm_runtime( puid = InstallerAskForString('Enter user ID (UID) for running non-root Malcolm processes') pgid = InstallerAskForString('Enter group ID (GID) for running non-root Malcolm processes') - # guestimate how much memory we should use based on total system memory + if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: + # guestimate how much memory we should use based on total system memory - if self.debug: - eprint( - f'{malcolm_install_path} with "{composeFiles}" and "{args.configDir}", system memory is {self.totalMemoryGigs} GiB' - ) + if self.debug: + eprint( + f'{malcolm_install_path} with "{configFiles}" and "{args.configDir}", system memory is {self.totalMemoryGigs} GiB' + ) - if self.totalMemoryGigs >= 63.0: - osMemory = '30g' - lsMemory = '6g' - elif self.totalMemoryGigs >= 31.0: - osMemory = '16g' - lsMemory = '3g' - elif self.totalMemoryGigs >= 15.0: - osMemory = '10g' - lsMemory = '2500m' - elif self.totalMemoryGigs >= 11.0: - osMemory = '6g' - lsMemory = '2500m' - elif self.totalMemoryGigs >= 7.0: - eprint(f"Detected only {self.totalMemoryGigs} GiB of memory; performance will be suboptimal") - osMemory = '4g' - lsMemory = '2500m' - elif self.totalMemoryGigs > 0.0: - eprint(f"Detected only {self.totalMemoryGigs} GiB of memory; performance will be suboptimal") - osMemory = '3500m' - lsMemory = '2g' + if self.totalMemoryGigs >= 63.0: + osMemory = '30g' + lsMemory = '6g' + elif self.totalMemoryGigs >= 31.0: + osMemory = '16g' + lsMemory = '3g' + elif self.totalMemoryGigs >= 15.0: + osMemory = '10g' + lsMemory = '2500m' + elif self.totalMemoryGigs >= 11.0: + osMemory = '6g' + lsMemory = '2500m' + elif self.totalMemoryGigs >= 7.0: + eprint(f"Detected only {self.totalMemoryGigs} GiB of memory; performance will be suboptimal") + osMemory = '4g' + lsMemory = '2500m' + elif self.totalMemoryGigs > 0.0: + eprint(f"Detected only {self.totalMemoryGigs} GiB of memory; performance will be suboptimal") + osMemory = '3500m' + lsMemory = '2g' + else: + eprint("Failed to determine system memory size, using defaults; performance may be suboptimal") + osMemory = '8g' + lsMemory = '3g' else: - eprint("Failed to determine system memory size, using defaults; performance may be suboptimal") - osMemory = '8g' + osMemory = '16g' lsMemory = '3g' # see Tuning and Profiling Logstash Performance @@ -449,12 +470,15 @@ def tweak_malcolm_runtime( # - https://www.elastic.co/guide/en/logstash/current/logstash-settings-file.html # - https://www.elastic.co/guide/en/logstash/current/multiple-pipelines.html # we don't want it too high, as in Malcolm Logstash also competes with OpenSearch, etc. for resources - if self.totalCores > 16: - lsWorkers = 10 - elif self.totalCores >= 12: - lsWorkers = 6 + if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: + if self.totalCores > 16: + lsWorkers = 10 + elif self.totalCores >= 12: + lsWorkers = 6 + else: + lsWorkers = 3 else: - lsWorkers = 3 + lsWorkers = 6 opensearchPrimaryRemote = False opensearchPrimaryUrl = 'http://opensearch:9200' @@ -462,6 +486,7 @@ def tweak_malcolm_runtime( opensearchSecondaryRemote = False opensearchSecondaryUrl = '' opensearchSecondarySslVerify = False + indexSnapshotCompressed = False opensearchPrimaryRemote = not InstallerYesOrNo( 'Should Malcolm use and maintain its own OpenSearch instance?', @@ -477,6 +502,7 @@ def tweak_malcolm_runtime( 'Require SSL certificate validation for communication with primary OpenSearch instance?', default=False, ) + indexSnapshotCompressed = InstallerYesOrNo('Compress OpenSearch index snapshots?', default=False) opensearchSecondaryRemote = InstallerYesOrNo( 'Forward Logstash logs to a secondary remote OpenSearch instance?', @@ -511,7 +537,9 @@ def tweak_malcolm_runtime( restartMode = None allowedRestartModes = ('no', 'on-failure', 'always', 'unless-stopped') - if InstallerYesOrNo('Restart Malcolm upon system or Docker daemon restart?', default=restart_mode_default): + if (self.orchMode is OrchestrationFramework.DOCKER_COMPOSE) and InstallerYesOrNo( + 'Restart Malcolm upon system or Docker daemon restart?', default=restart_mode_default + ): while restartMode not in allowedRestartModes: restartMode = InstallerChooseOne( 'Select Malcolm restart behavior', @@ -534,33 +562,34 @@ def tweak_malcolm_runtime( traefikEntrypoint = "" traefikResolver = "" - behindReverseProxy = InstallerYesOrNo( + behindReverseProxy = (self.orchMode is OrchestrationFramework.KUBERNETES) or InstallerYesOrNo( 'Will Malcolm be running behind another reverse proxy (Traefik, Caddy, etc.)?', default=(not nginxSSL) ) - if behindReverseProxy: - traefikLabels = InstallerYesOrNo('Configure labels for Traefik?', default=False) - if traefikLabels: - while len(traefikHost) <= 1: - traefikHost = InstallerAskForString( - 'Enter request domain (host header value) for Malcolm interface Traefik router (e.g., malcolm.example.org)' - ) - while (len(traefikOpenSearchHost) <= 1) or (traefikOpenSearchHost == traefikHost): - traefikOpenSearchHost = InstallerAskForString( - f'Enter request domain (host header value) for OpenSearch Traefik router (e.g., opensearch.{traefikHost})' - ) - while len(traefikEntrypoint) <= 1: - traefikEntrypoint = InstallerAskForString( - 'Enter Traefik router entrypoint (e.g., websecure)', default="websecure" - ) - while len(traefikResolver) <= 1: - traefikResolver = InstallerAskForString( - 'Enter Traefik router resolver (e.g., myresolver)', default="myresolver" - ) + if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: + if behindReverseProxy: + traefikLabels = InstallerYesOrNo('Configure labels for Traefik?', default=False) + if traefikLabels: + while len(traefikHost) <= 1: + traefikHost = InstallerAskForString( + 'Enter request domain (host header value) for Malcolm interface Traefik router (e.g., malcolm.example.org)' + ) + while (len(traefikOpenSearchHost) <= 1) or (traefikOpenSearchHost == traefikHost): + traefikOpenSearchHost = InstallerAskForString( + f'Enter request domain (host header value) for OpenSearch Traefik router (e.g., opensearch.{traefikHost})' + ) + while len(traefikEntrypoint) <= 1: + traefikEntrypoint = InstallerAskForString( + 'Enter Traefik router entrypoint (e.g., websecure)', default="websecure" + ) + while len(traefikResolver) <= 1: + traefikResolver = InstallerAskForString( + 'Enter Traefik router resolver (e.g., myresolver)', default="myresolver" + ) - dockerNetworkExternalName = InstallerAskForString( - 'Specify external Docker network name (or leave blank for default networking)', default="" - ) + dockerNetworkExternalName = InstallerAskForString( + 'Specify external Docker network name (or leave blank for default networking)', default="" + ) allowedAuthModes = { 'Basic': 'true', @@ -622,121 +651,120 @@ def tweak_malcolm_runtime( zeekLogDirDefault = os.path.join(malcolm_install_path, zeekLogDir) zeekLogDirFull = os.path.realpath(zeekLogDirDefault) - if not InstallerYesOrNo( - 'Store PCAP, log and index files locally under {}?'.format(malcolm_install_path), - default=True, - ): - # PCAP directory + if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: if not InstallerYesOrNo( - 'Store PCAP files locally in {}?'.format(pcapDirDefault), + 'Store PCAP, log and index files locally under {}?'.format(malcolm_install_path), default=True, ): - while True: - pcapDir = InstallerAskForString('Enter PCAP directory') - if (len(pcapDir) > 1) and os.path.isdir(pcapDir): - pcapDirFull = os.path.realpath(pcapDir) - pcapDir = ( - f"./{os.path.relpath(pcapDirDefault, malcolm_install_path)}" - if same_file_or_dir(pcapDirDefault, pcapDirFull) - else pcapDirFull - ) - break - - # Zeek log directory - if not InstallerYesOrNo( - 'Store Zeek logs locally in {}?'.format(zeekLogDirDefault), - default=True, - ): - while True: - zeekLogDir = InstallerAskForString('Enter Zeek log directory') - if (len(zeekLogDir) > 1) and os.path.isdir(zeekLogDir): - zeekLogDirFull = os.path.realpath(zeekLogDir) - zeekLogDir = ( - f"./{os.path.relpath(zeekLogDirDefault, malcolm_install_path)}" - if same_file_or_dir(zeekLogDirDefault, zeekLogDirFull) - else zeekLogDirFull - ) - break - - # Suricata log directory - if not InstallerYesOrNo( - 'Store Suricata logs locally in {}?'.format(suricataLogDirDefault), - default=True, - ): - while True: - suricataLogDir = InstallerAskForString('Enter Suricata log directory') - if (len(suricataLogDir) > 1) and os.path.isdir(suricataLogDir): - suricataLogDirFull = os.path.realpath(suricataLogDir) - suricataLogDir = ( - f"./{os.path.relpath(suricataLogDirDefault, malcolm_install_path)}" - if same_file_or_dir(suricataLogDirDefault, suricataLogDirFull) - else suricataLogDirFull - ) - break - - if not opensearchPrimaryRemote: - # opensearch index directory + # PCAP directory if not InstallerYesOrNo( - 'Store OpenSearch indices locally in {}?'.format(indexDirDefault), + 'Store PCAP files locally in {}?'.format(pcapDirDefault), default=True, ): while True: - indexDir = InstallerAskForString('Enter OpenSearch index directory') - if (len(indexDir) > 1) and os.path.isdir(indexDir): - indexDirFull = os.path.realpath(indexDir) - indexDir = ( - f"./{os.path.relpath(indexDirDefault, malcolm_install_path)}" - if same_file_or_dir(indexDirDefault, indexDirFull) - else indexDirFull + pcapDir = InstallerAskForString('Enter PCAP directory') + if (len(pcapDir) > 1) and os.path.isdir(pcapDir): + pcapDirFull = os.path.realpath(pcapDir) + pcapDir = ( + f"./{os.path.relpath(pcapDirDefault, malcolm_install_path)}" + if same_file_or_dir(pcapDirDefault, pcapDirFull) + else pcapDirFull ) break - # opensearch snapshot repository directory and compression + # Zeek log directory if not InstallerYesOrNo( - 'Store OpenSearch index snapshots locally in {}?'.format(indexSnapshotDirDefault), + 'Store Zeek logs locally in {}?'.format(zeekLogDirDefault), default=True, ): while True: - indexSnapshotDir = InstallerAskForString('Enter OpenSearch index snapshot directory') - if (len(indexSnapshotDir) > 1) and os.path.isdir(indexSnapshotDir): - indexSnapshotDirFull = os.path.realpath(indexSnapshotDir) - indexSnapshotDir = ( - f"./{os.path.relpath(indexSnapshotDirDefault, malcolm_install_path)}" - if same_file_or_dir(indexSnapshotDirDefault, indexSnapshotDirFull) - else indexSnapshotDirFull + zeekLogDir = InstallerAskForString('Enter Zeek log directory') + if (len(zeekLogDir) > 1) and os.path.isdir(zeekLogDir): + zeekLogDirFull = os.path.realpath(zeekLogDir) + zeekLogDir = ( + f"./{os.path.relpath(zeekLogDirDefault, malcolm_install_path)}" + if same_file_or_dir(zeekLogDirDefault, zeekLogDirFull) + else zeekLogDirFull ) break - # make sure paths specified (and their necessary children) exist - for pathToCreate in ( - indexDirFull, - indexSnapshotDirFull, - os.path.join(pcapDirFull, 'processed'), - os.path.join(pcapDirFull, 'upload'), - os.path.join(suricataLogDirFull, 'live'), - os.path.join(zeekLogDirFull, 'current'), - os.path.join(zeekLogDirFull, 'live'), - os.path.join(zeekLogDirFull, 'upload'), - os.path.join(zeekLogDirFull, os.path.join('extract_files', 'preserved')), - os.path.join(zeekLogDirFull, os.path.join('extract_files', 'quarantine')), - ): - try: - if args.debug: - eprint(f"Creating {pathToCreate}") - pathlib.Path(pathToCreate).mkdir(parents=True, exist_ok=True) - if ( - ((self.platform == PLATFORM_LINUX) or (self.platform == PLATFORM_MAC)) - and (self.scriptUser == "root") - and (getpwuid(os.stat(pathToCreate).st_uid).pw_name == self.scriptUser) + # Suricata log directory + if not InstallerYesOrNo( + 'Store Suricata logs locally in {}?'.format(suricataLogDirDefault), + default=True, ): - if args.debug: - eprint(f"Setting permissions of {pathToCreate} to {puid}:{pgid}") - # change ownership of newly-created directory to match puid/pgid - os.chown(pathToCreate, int(puid), int(pgid)) - except Exception as e: - eprint(f"Creating {pathToCreate} failed: {e}") + while True: + suricataLogDir = InstallerAskForString('Enter Suricata log directory') + if (len(suricataLogDir) > 1) and os.path.isdir(suricataLogDir): + suricataLogDirFull = os.path.realpath(suricataLogDir) + suricataLogDir = ( + f"./{os.path.relpath(suricataLogDirDefault, malcolm_install_path)}" + if same_file_or_dir(suricataLogDirDefault, suricataLogDirFull) + else suricataLogDirFull + ) + break - indexSnapshotCompressed = InstallerYesOrNo('Compress OpenSearch index snapshots?', default=False) + if not opensearchPrimaryRemote: + # opensearch index directory + if not InstallerYesOrNo( + 'Store OpenSearch indices locally in {}?'.format(indexDirDefault), + default=True, + ): + while True: + indexDir = InstallerAskForString('Enter OpenSearch index directory') + if (len(indexDir) > 1) and os.path.isdir(indexDir): + indexDirFull = os.path.realpath(indexDir) + indexDir = ( + f"./{os.path.relpath(indexDirDefault, malcolm_install_path)}" + if same_file_or_dir(indexDirDefault, indexDirFull) + else indexDirFull + ) + break + + # opensearch snapshot repository directory and compression + if not InstallerYesOrNo( + 'Store OpenSearch index snapshots locally in {}?'.format(indexSnapshotDirDefault), + default=True, + ): + while True: + indexSnapshotDir = InstallerAskForString('Enter OpenSearch index snapshot directory') + if (len(indexSnapshotDir) > 1) and os.path.isdir(indexSnapshotDir): + indexSnapshotDirFull = os.path.realpath(indexSnapshotDir) + indexSnapshotDir = ( + f"./{os.path.relpath(indexSnapshotDirDefault, malcolm_install_path)}" + if same_file_or_dir(indexSnapshotDirDefault, indexSnapshotDirFull) + else indexSnapshotDirFull + ) + break + + # make sure paths specified (and their necessary children) exist + for pathToCreate in ( + indexDirFull, + indexSnapshotDirFull, + os.path.join(pcapDirFull, 'processed'), + os.path.join(pcapDirFull, 'upload'), + os.path.join(suricataLogDirFull, 'live'), + os.path.join(zeekLogDirFull, 'current'), + os.path.join(zeekLogDirFull, 'live'), + os.path.join(zeekLogDirFull, 'upload'), + os.path.join(zeekLogDirFull, os.path.join('extract_files', 'preserved')), + os.path.join(zeekLogDirFull, os.path.join('extract_files', 'quarantine')), + ): + try: + if args.debug: + eprint(f"Creating {pathToCreate}") + pathlib.Path(pathToCreate).mkdir(parents=True, exist_ok=True) + if ( + ((self.platform == PLATFORM_LINUX) or (self.platform == PLATFORM_MAC)) + and (self.scriptUser == "root") + and (getpwuid(os.stat(pathToCreate).st_uid).pw_name == self.scriptUser) + ): + if args.debug: + eprint(f"Setting permissions of {pathToCreate} to {puid}:{pgid}") + # change ownership of newly-created directory to match puid/pgid + os.chown(pathToCreate, int(puid), int(pgid)) + except Exception as e: + eprint(f"Creating {pathToCreate} failed: {e}") # delete oldest indexes based on index pattern size indexPruneSizeLimit = '0' @@ -752,28 +780,47 @@ def tweak_malcolm_runtime( 'Determine oldest indices by name (instead of creation time)?', default=True ) + # let Arkime delete old PCAP files based on available storage + arkimeManagePCAP = InstallerYesOrNo( + 'Should Arkime delete PCAP files based on available storage (see https://arkime.com/faq#pcap-deletion)?', + default=False, + ) + autoSuricata = InstallerYesOrNo('Automatically analyze all PCAP files with Suricata?', default=True) suricataRuleUpdate = autoSuricata and InstallerYesOrNo( 'Download updated Suricata signatures periodically?', default=False ) autoZeek = InstallerYesOrNo('Automatically analyze all PCAP files with Zeek?', default=True) + zeekICSBestGuess = (autoZeek or liveZeek) and InstallerYesOrNo( + 'Should Malcolm use "best guess" to identify potential OT/ICS traffic with Zeek?', default=False + ) reverseDns = InstallerYesOrNo( 'Perform reverse DNS lookup locally for source and destination IP addresses in logs?', default=False ) autoOui = InstallerYesOrNo('Perform hardware vendor OUI lookups for MAC addresses?', default=True) autoFreq = InstallerYesOrNo('Perform string randomness scoring on some fields?', default=True) - opensearchOpen = (not opensearchPrimaryRemote) and InstallerYesOrNo( - 'Expose OpenSearch port to external hosts?', default=expose_opensearch_default - ) - logstashOpen = InstallerYesOrNo('Expose Logstash port to external hosts?', default=expose_logstash_default) - filebeatTcpOpen = InstallerYesOrNo( - 'Expose Filebeat TCP port to external hosts?', default=expose_filebeat_default - ) - filebeatTcpSourceField = '' - filebeatTcpTargetField = '' - filebeatTcpDropField = '' + + if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: + opensearchOpen = (not opensearchPrimaryRemote) and InstallerYesOrNo( + 'Expose OpenSearch port to external hosts?', default=expose_opensearch_default + ) + logstashOpen = InstallerYesOrNo('Expose Logstash port to external hosts?', default=expose_logstash_default) + filebeatTcpOpen = InstallerYesOrNo( + 'Expose Filebeat TCP port to external hosts?', default=expose_filebeat_default + ) + else: + opensearchOpen = not opensearchPrimaryRemote + logstashOpen = True + filebeatTcpOpen = True + + filebeatTcpFormat = 'json' + filebeatTcpSourceField = 'message' + filebeatTcpTargetField = 'miscbeat' + filebeatTcpDropField = filebeatTcpSourceField filebeatTcpTag = '_malcolm_beats' - if filebeatTcpOpen: + if filebeatTcpOpen and not InstallerYesOrNo( + 'Use default field values for Filebeat TCP listener?', default=True + ): allowedFilebeatTcpFormats = ('json', 'raw') filebeatTcpFormat = 'unset' while filebeatTcpFormat not in allowedFilebeatTcpFormats: @@ -784,11 +831,11 @@ def tweak_malcolm_runtime( if filebeatTcpFormat == 'json': filebeatTcpSourceField = InstallerAskForString( 'Source field to parse for messages sent to Filebeat TCP listener', - default="message", + default=filebeatTcpSourceField, ) filebeatTcpTargetField = InstallerAskForString( 'Target field under which to store decoded JSON fields for messages sent to Filebeat TCP listener', - default="miscbeat", + default=filebeatTcpTargetField, ) filebeatTcpDropField = InstallerAskForString( 'Field to drop from events sent to Filebeat TCP listener', @@ -798,10 +845,8 @@ def tweak_malcolm_runtime( 'Tag to apply to messages sent to Filebeat TCP listener', default=filebeatTcpTag, ) - else: - filebeatTcpFormat = 'raw' - sftpOpen = InstallerYesOrNo( + sftpOpen = (self.orchMode is OrchestrationFramework.DOCKER_COMPOSE) and InstallerYesOrNo( 'Expose SFTP server (for PCAP upload) to external hosts?', default=expose_sftp_default ) @@ -882,30 +927,22 @@ def tweak_malcolm_runtime( pcapNetSniff = False pcapTcpDump = False liveZeek = False - zeekICSBestGuess = False liveSuricata = False pcapIface = 'lo' tweakIface = False pcapFilter = '' - arkimeManagePCAP = False - if InstallerYesOrNo( - 'Should Malcolm capture live network traffic to PCAP files for analysis with Arkime?', default=False - ): - pcapNetSniff = InstallerYesOrNo('Capture packets using netsniff-ng?', default=True) - if not pcapNetSniff: - pcapTcpDump = InstallerYesOrNo('Capture packets using tcpdump?', default=True) - arkimeManagePCAP = InstallerYesOrNo( - 'Should Arkime delete PCAP files based on available storage (see https://arkime.com/faq#pcap-deletion)?', - default=False, - ) - - liveSuricata = InstallerYesOrNo('Should Malcolm analyze live network traffic with Suricata?', default=False) - liveZeek = InstallerYesOrNo('Should Malcolm analyze live network traffic with Zeek?', default=False) - - zeekICSBestGuess = (autoZeek or liveZeek) and InstallerYesOrNo( - 'Should Malcolm use "best guess" to identify potential OT/ICS traffic with Zeek?', default=False - ) + if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: + if InstallerYesOrNo( + 'Should Malcolm capture live network traffic to PCAP files for analysis with Arkime?', default=False + ): + pcapNetSniff = InstallerYesOrNo('Capture packets using netsniff-ng?', default=True) + if not pcapNetSniff: + pcapTcpDump = InstallerYesOrNo('Capture packets using tcpdump?', default=True) + liveSuricata = InstallerYesOrNo( + 'Should Malcolm analyze live network traffic with Suricata?', default=False + ) + liveZeek = InstallerYesOrNo('Should Malcolm analyze live network traffic with Zeek?', default=False) if pcapNetSniff or pcapTcpDump or liveZeek or liveSuricata: pcapIface = '' @@ -1290,6 +1327,22 @@ def tweak_malcolm_runtime( 'ZEEK_AUTO_ANALYZE_PCAP_FILES', TrueOrFalseNoQuote(autoZeek), ), + # Use polling for file watching vs. native + EnvValue( + os.path.join(args.configDir, 'zeek.env'), + 'EXTRACTED_FILE_WATCHER_POLLING', + TrueOrFalseNoQuote(self.orchMode is OrchestrationFramework.KUBERNETES), + ), + EnvValue( + os.path.join(args.configDir, 'upload-common.env'), + 'PCAP_PIPELINE_POLLING', + TrueOrFalseNoQuote(self.orchMode is OrchestrationFramework.KUBERNETES), + ), + EnvValue( + os.path.join(args.configDir, 'filebeat.env'), + 'FILEBEAT_WATCHER_POLLING', + TrueOrFalseNoQuote(self.orchMode is OrchestrationFramework.KUBERNETES), + ), ] # now, go through and modify the values in the .env files @@ -1310,361 +1363,362 @@ def tweak_malcolm_runtime( except Exception as e: eprint(f"Setting value for {val.key} in {val.envFile} module failed: {e}") - # modify docker-compose specific values (port mappings, volume bind mounts, etc.) in-place in docker-compose files - for composeFile in composeFiles: - # save off owner of original files - composeFileStat = os.stat(composeFile) - origUid, origGuid = composeFileStat[4], composeFileStat[5] - composeFileHandle = fileinput.FileInput(composeFile, inplace=True, backup=None) - try: - sectionIndents = defaultdict(lambda: ' ') - currentSection = None - currentService = None - networkWritten = False - - for line in composeFileHandle: - line = line.rstrip("\n") - skipLine = False - sectionStartLine = False - serviceStartLine = False - - # it would be cleaner to use something like PyYAML to do this, but I want to have as few dependencies - # as possible so we're going to do it janky instead. Also, as of right now pyyaml doesn't preserve - # comments, which is a big deal for this complicated docker-compose file. There is - # https://pypi.org/project/ruamel.yaml to possibly consider if we're comfortable with the dependency. - - # determine which section of the compose file we are in (e.g., services, networks, volumes, etc.) - sectionMatch = re.match(r'^([^\s#]+):\s*(#.*)?$', line) - if sectionMatch is not None: - currentSection = sectionMatch.group(1) - sectionStartLine = True - currentService = None - - # determine indentation for each compose file section (assumes YML file is consistently indented) - if (currentSection is not None) and (currentSection not in sectionIndents): - indentMatch = re.search(r'^(\s+)\S+\s*:\s*$', line) - if indentMatch is not None: - sectionIndents[currentSection] = indentMatch.group(1) - - # determine which service we're currently processing in the YML file - if currentSection == 'services': - serviceMatch = re.search(fr'^{sectionIndents[currentSection]}(\S+)\s*:\s*$', line) - if serviceMatch is not None: - currentService = serviceMatch.group(1).lower() - serviceStartLine = True - - if (currentSection == 'services') and (not serviceStartLine) and (currentService is not None): - # down in the individual services sections of the compose file - - if re.match(r'^\s*restart\s*:.*$', line): - # whether or not to restart services automatically (on boot, etc.) - line = f"{sectionIndents[currentSection] * 2}restart: {restartMode}" - - elif currentService == 'arkime': - # stuff specifically in the arkime section - if re.match(r'^\s*-.+:/data/pcap(:.+)?\s*$', line): - # Arkime's reference to the PCAP directory - line = ReplaceBindMountLocation( - line, - pcapDir, - sectionIndents[currentSection] * 3, - ) - - elif currentService == 'filebeat': - # stuff specifically in the filebeat section - if re.match(r'^[\s#]*-\s*"([\d\.]+:)?\d+:\d+"\s*$', line): - # set bind IP based on whether it should be externally exposed or not - line = re.sub( - r'^([\s#]*-\s*")([\d\.]+:)?(\d+:\d+"\s*)$', - fr"\g<1>{'0.0.0.0' if filebeatTcpOpen else '127.0.0.1'}:\g<3>", - line, - ) - - elif re.match(r'^\s*-.+:/suricata(:.+)?\s*$', line): - # filebeat's reference to the suricata-logs directory - line = ReplaceBindMountLocation( - line, - suricataLogDir, - sectionIndents[currentSection] * 3, - ) - - elif re.match(r'^\s*-.+:/zeek(:.+)?\s*$', line): - # filebeat's reference to the zeek-logs directory - line = ReplaceBindMountLocation( - line, - zeekLogDir, - sectionIndents[currentSection] * 3, - ) - - elif currentService == 'file-monitor': - # stuff specifically in the file-monitor section - if re.match(r'^\s*-.+:/zeek/extract_files(:.+)?\s*$', line): - # file-monitor's reference to the zeek-logs/extract_files directory - line = ReplaceBindMountLocation( - line, - os.path.join(zeekLogDir, 'extract_files'), - sectionIndents[currentSection] * 3, - ) - - elif re.match(r'^\s*-.+:/zeek/logs(:.+)?\s*$', line): - # zeek's reference to the zeek-logs/current directory - line = ReplaceBindMountLocation( - line, - os.path.join(zeekLogDir, 'current'), - sectionIndents[currentSection] * 3, - ) - - elif currentService == 'logstash': - # stuff specifically in the logstash section - if re.match(r'^[\s#]*-\s*"([\d\.]+:)?\d+:\d+"\s*$', line): - # set bind IP based on whether it should be externally exposed or not - line = re.sub( - r'^([\s#]*-\s*")([\d\.]+:)?(\d+:\d+"\s*)$', - fr"\g<1>{'0.0.0.0' if logstashOpen else '127.0.0.1'}:\g<3>", - line, - ) + if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: + # modify docker-compose specific values (port mappings, volume bind mounts, etc.) in-place in docker-compose files + for composeFile in configFiles: + # save off owner of original files + composeFileStat = os.stat(composeFile) + origUid, origGuid = composeFileStat[4], composeFileStat[5] + composeFileHandle = fileinput.FileInput(composeFile, inplace=True, backup=None) + try: + sectionIndents = defaultdict(lambda: ' ') + currentSection = None + currentService = None + networkWritten = False + + for line in composeFileHandle: + line = line.rstrip("\n") + skipLine = False + sectionStartLine = False + serviceStartLine = False + + # it would be cleaner to use something like PyYAML to do this, but I want to have as few dependencies + # as possible so we're going to do it janky instead. Also, as of right now pyyaml doesn't preserve + # comments, which is a big deal for this complicated docker-compose file. There is + # https://pypi.org/project/ruamel.yaml to possibly consider if we're comfortable with the dependency. + + # determine which section of the compose file we are in (e.g., services, networks, volumes, etc.) + sectionMatch = re.match(r'^([^\s#]+):\s*(#.*)?$', line) + if sectionMatch is not None: + currentSection = sectionMatch.group(1) + sectionStartLine = True + currentService = None + + # determine indentation for each compose file section (assumes YML file is consistently indented) + if (currentSection is not None) and (currentSection not in sectionIndents): + indentMatch = re.search(r'^(\s+)\S+\s*:\s*$', line) + if indentMatch is not None: + sectionIndents[currentSection] = indentMatch.group(1) + + # determine which service we're currently processing in the YML file + if currentSection == 'services': + serviceMatch = re.search(fr'^{sectionIndents[currentSection]}(\S+)\s*:\s*$', line) + if serviceMatch is not None: + currentService = serviceMatch.group(1).lower() + serviceStartLine = True + + if (currentSection == 'services') and (not serviceStartLine) and (currentService is not None): + # down in the individual services sections of the compose file + + if re.match(r'^\s*restart\s*:.*$', line): + # whether or not to restart services automatically (on boot, etc.) + line = f"{sectionIndents[currentSection] * 2}restart: {restartMode}" + + elif currentService == 'arkime': + # stuff specifically in the arkime section + if re.match(r'^\s*-.+:/data/pcap(:.+)?\s*$', line): + # Arkime's reference to the PCAP directory + line = ReplaceBindMountLocation( + line, + pcapDir, + sectionIndents[currentSection] * 3, + ) - elif currentService == 'opensearch': - # stuff specifically in the opensearch section - if re.match(r'^\s*-.+:/usr/share/opensearch/data(:.+)?\s*$', line): - # OpenSearch indexes directory - line = ReplaceBindMountLocation( - line, - indexDir, - sectionIndents[currentSection] * 3, - ) + elif currentService == 'filebeat': + # stuff specifically in the filebeat section + if re.match(r'^[\s#]*-\s*"([\d\.]+:)?\d+:\d+"\s*$', line): + # set bind IP based on whether it should be externally exposed or not + line = re.sub( + r'^([\s#]*-\s*")([\d\.]+:)?(\d+:\d+"\s*)$', + fr"\g<1>{'0.0.0.0' if filebeatTcpOpen else '127.0.0.1'}:\g<3>", + line, + ) - elif re.match(r'^\s*-.+:/opt/opensearch/backup(:.+)?\s*$', line): - # OpenSearch backup directory - line = ReplaceBindMountLocation( - line, - indexSnapshotDir, - sectionIndents[currentSection] * 3, - ) + elif re.match(r'^\s*-.+:/suricata(:.+)?\s*$', line): + # filebeat's reference to the suricata-logs directory + line = ReplaceBindMountLocation( + line, + suricataLogDir, + sectionIndents[currentSection] * 3, + ) - elif currentService == 'pcap-capture': - # stuff specifically in the pcap-capture section - if re.match(r'^\s*-.+:/pcap(:.+)?\s*$', line): - # pcap-capture's reference to the PCAP directory - line = ReplaceBindMountLocation( - line, - os.path.join(pcapDir, 'upload'), - sectionIndents[currentSection] * 3, - ) + elif re.match(r'^\s*-.+:/zeek(:.+)?\s*$', line): + # filebeat's reference to the zeek-logs directory + line = ReplaceBindMountLocation( + line, + zeekLogDir, + sectionIndents[currentSection] * 3, + ) - elif currentService == 'pcap-monitor': - # stuff specifically in the pcap-monitor section - if re.match(r'^\s*-.+:/pcap(:.+)?\s*$', line): - # pcap-monitor's reference to the PCAP directory - line = ReplaceBindMountLocation( - line, - pcapDir, - sectionIndents[currentSection] * 3, - ) + elif currentService == 'file-monitor': + # stuff specifically in the file-monitor section + if re.match(r'^\s*-.+:/zeek/extract_files(:.+)?\s*$', line): + # file-monitor's reference to the zeek-logs/extract_files directory + line = ReplaceBindMountLocation( + line, + os.path.join(zeekLogDir, 'extract_files'), + sectionIndents[currentSection] * 3, + ) - elif re.match(r'^\s*-.+:/zeek(:.+)?\s*$', line): - # pcap-monitor's reference to the zeek-logs directory - line = ReplaceBindMountLocation( - line, - zeekLogDir, - sectionIndents[currentSection] * 3, - ) + elif re.match(r'^\s*-.+:/zeek/logs(:.+)?\s*$', line): + # zeek's reference to the zeek-logs/current directory + line = ReplaceBindMountLocation( + line, + os.path.join(zeekLogDir, 'current'), + sectionIndents[currentSection] * 3, + ) - elif currentService == 'suricata': - # stuff specifically in the suricata section - if re.match(r'^\s*-.+:/data/pcap(:.+)?\s*$', line): - # Suricata's reference to the PCAP directory - line = ReplaceBindMountLocation( - line, - pcapDir, - sectionIndents[currentSection] * 3, - ) + elif currentService == 'logstash': + # stuff specifically in the logstash section + if re.match(r'^[\s#]*-\s*"([\d\.]+:)?\d+:\d+"\s*$', line): + # set bind IP based on whether it should be externally exposed or not + line = re.sub( + r'^([\s#]*-\s*")([\d\.]+:)?(\d+:\d+"\s*)$', + fr"\g<1>{'0.0.0.0' if logstashOpen else '127.0.0.1'}:\g<3>", + line, + ) - elif re.match(r'^\s*-.+:/var/log/suricata(:.+)?\s*$', line): - # suricata's reference to the suricata-logs directory - line = ReplaceBindMountLocation( - line, - suricataLogDir, - sectionIndents[currentSection] * 3, - ) + elif currentService == 'opensearch': + # stuff specifically in the opensearch section + if re.match(r'^\s*-.+:/usr/share/opensearch/data(:.+)?\s*$', line): + # OpenSearch indexes directory + line = ReplaceBindMountLocation( + line, + indexDir, + sectionIndents[currentSection] * 3, + ) - elif currentService == 'suricata-live': - # stuff specifically in the suricata-live section - if re.match(r'^\s*-.+:/var/log/suricata(:.+)?\s*$', line): - # suricata-live's reference to the suricata-logs directory - line = ReplaceBindMountLocation( - line, - suricataLogDir, - sectionIndents[currentSection] * 3, - ) + elif re.match(r'^\s*-.+:/opt/opensearch/backup(:.+)?\s*$', line): + # OpenSearch backup directory + line = ReplaceBindMountLocation( + line, + indexSnapshotDir, + sectionIndents[currentSection] * 3, + ) - elif currentService == 'upload': - # stuff specifically in the upload section - if re.match(r'^[\s#]*-\s*"([\d\.]+:)?\d+:\d+"\s*$', line): - # set bind IP based on whether it should be externally exposed or not - line = re.sub( - r'^([\s#]*-\s*")([\d\.]+:)?(\d+:\d+"\s*)$', - fr"\g<1>{'0.0.0.0' if sftpOpen else '127.0.0.1'}:\g<3>", - line, - ) + elif currentService == 'pcap-capture': + # stuff specifically in the pcap-capture section + if re.match(r'^\s*-.+:/pcap(:.+)?\s*$', line): + # pcap-capture's reference to the PCAP directory + line = ReplaceBindMountLocation( + line, + os.path.join(pcapDir, 'upload'), + sectionIndents[currentSection] * 3, + ) - elif re.match(r'^\s*-.+:/var/www/upload/server/php/chroot/files(:.+)?\s*$', line): - # upload's reference to the PCAP directory - line = ReplaceBindMountLocation( - line, - os.path.join(pcapDir, 'upload'), - sectionIndents[currentSection] * 3, - ) + elif currentService == 'pcap-monitor': + # stuff specifically in the pcap-monitor section + if re.match(r'^\s*-.+:/pcap(:.+)?\s*$', line): + # pcap-monitor's reference to the PCAP directory + line = ReplaceBindMountLocation( + line, + pcapDir, + sectionIndents[currentSection] * 3, + ) - elif currentService == 'zeek': - # stuff specifically in the zeek section - if re.match(r'^\s*-.+:/pcap(:.+)?\s*$', line): - # Zeek's reference to the PCAP directory - line = ReplaceBindMountLocation( - line, - pcapDir, - sectionIndents[currentSection] * 3, - ) + elif re.match(r'^\s*-.+:/zeek(:.+)?\s*$', line): + # pcap-monitor's reference to the zeek-logs directory + line = ReplaceBindMountLocation( + line, + zeekLogDir, + sectionIndents[currentSection] * 3, + ) - elif re.match(r'^\s*-.+:/zeek/upload(:.+)?\s*$', line): - # zeek's reference to the zeek-logs/upload directory - line = ReplaceBindMountLocation( - line, - os.path.join(zeekLogDir, 'upload'), - sectionIndents[currentSection] * 3, - ) + elif currentService == 'suricata': + # stuff specifically in the suricata section + if re.match(r'^\s*-.+:/data/pcap(:.+)?\s*$', line): + # Suricata's reference to the PCAP directory + line = ReplaceBindMountLocation( + line, + pcapDir, + sectionIndents[currentSection] * 3, + ) - elif re.match(r'^\s*-.+:/zeek/extract_files(:.+)?\s*$', line): - # zeek's reference to the zeek-logs/extract_files directory - line = ReplaceBindMountLocation( - line, - os.path.join(zeekLogDir, 'extract_files'), - sectionIndents[currentSection] * 3, - ) + elif re.match(r'^\s*-.+:/var/log/suricata(:.+)?\s*$', line): + # suricata's reference to the suricata-logs directory + line = ReplaceBindMountLocation( + line, + suricataLogDir, + sectionIndents[currentSection] * 3, + ) - elif currentService == 'zeek-live': - # stuff specifically in the zeek-live section - if re.match(r'^\s*-.+:/zeek/live(:.+)?\s*$', line): - # zeek-live's reference to the zeek-logs/live directory - line = ReplaceBindMountLocation( - line, - os.path.join(zeekLogDir, 'live'), - sectionIndents[currentSection] * 3, - ) + elif currentService == 'suricata-live': + # stuff specifically in the suricata-live section + if re.match(r'^\s*-.+:/var/log/suricata(:.+)?\s*$', line): + # suricata-live's reference to the suricata-logs directory + line = ReplaceBindMountLocation( + line, + suricataLogDir, + sectionIndents[currentSection] * 3, + ) - elif re.match(r'^\s*-.+:/zeek/extract_files(:.+)?\s*$', line): - # zeek-lives's reference to the zeek-logs/extract_files directory - line = ReplaceBindMountLocation( - line, - os.path.join(zeekLogDir, 'extract_files'), - sectionIndents[currentSection] * 3, - ) + elif currentService == 'upload': + # stuff specifically in the upload section + if re.match(r'^[\s#]*-\s*"([\d\.]+:)?\d+:\d+"\s*$', line): + # set bind IP based on whether it should be externally exposed or not + line = re.sub( + r'^([\s#]*-\s*")([\d\.]+:)?(\d+:\d+"\s*)$', + fr"\g<1>{'0.0.0.0' if sftpOpen else '127.0.0.1'}:\g<3>", + line, + ) - elif currentService == 'nginx-proxy': - # stuff specifically in the nginx-proxy section + elif re.match(r'^\s*-.+:/var/www/upload/server/php/chroot/files(:.+)?\s*$', line): + # upload's reference to the PCAP directory + line = ReplaceBindMountLocation( + line, + os.path.join(pcapDir, 'upload'), + sectionIndents[currentSection] * 3, + ) - if re.match(r'^\s*test\s*:', line): - # set nginx-proxy health check based on whether they're using HTTPS or not - line = re.sub( - r'https?://localhost:\d+', - fr"{'https' if nginxSSL else 'http'}://localhost:443", - line, - ) + elif currentService == 'zeek': + # stuff specifically in the zeek section + if re.match(r'^\s*-.+:/pcap(:.+)?\s*$', line): + # Zeek's reference to the PCAP directory + line = ReplaceBindMountLocation( + line, + pcapDir, + sectionIndents[currentSection] * 3, + ) - elif re.match(r'^[\s#]*-\s*"([\d\.]+:)?\d+:\d+"\s*$', line): - # set bind IPs and ports based on whether it should be externally exposed or not - line = re.sub( - r'^([\s#]*-\s*")([\d\.]+:)?(\d+:\d+"\s*)$', - fr"\g<1>{'0.0.0.0' if nginxSSL and (((not '9200:9200' in line) and (not '5601:5601' in line)) or opensearchOpen) else '127.0.0.1'}:\g<3>", - line, - ) - if nginxSSL is False: - if ':443:' in line: - line = line.replace(':443:', ':80:') - if ':9200:' in line: - line = line.replace(':9200:', ':9201:') - else: - if ':80:' in line: - line = line.replace(':80:', ':443:') - if ':9201:' in line: - line = line.replace(':9201:', ':9200:') - - elif 'traefik.' in line: - # enable/disable/configure traefik labels if applicable - - # Traefik enabled vs. disabled - if 'traefik.enable' in line: - line = re.sub( - r'(#\s*)?(traefik\.enable\s*:\s*)(\S+)', - fr"\g<2>{TrueOrFalseQuote(behindReverseProxy and traefikLabels)}", + elif re.match(r'^\s*-.+:/zeek/upload(:.+)?\s*$', line): + # zeek's reference to the zeek-logs/upload directory + line = ReplaceBindMountLocation( line, + os.path.join(zeekLogDir, 'upload'), + sectionIndents[currentSection] * 3, ) - else: - line = re.sub( - r'(#\s*)?(traefik\..*)', - fr"{'' if traefikLabels else '# '}\g<2>", + + elif re.match(r'^\s*-.+:/zeek/extract_files(:.+)?\s*$', line): + # zeek's reference to the zeek-logs/extract_files directory + line = ReplaceBindMountLocation( line, + os.path.join(zeekLogDir, 'extract_files'), + sectionIndents[currentSection] * 3, ) - if 'traefik.http.' in line and '.osmalcolm.' in line: - # OpenSearch router enabled/disabled/host value - line = re.sub( - r'(#\s*)?(traefik\..*)', - fr"{'' if behindReverseProxy and traefikLabels and opensearchOpen else '# '}\g<2>", + elif currentService == 'zeek-live': + # stuff specifically in the zeek-live section + if re.match(r'^\s*-.+:/zeek/live(:.+)?\s*$', line): + # zeek-live's reference to the zeek-logs/live directory + line = ReplaceBindMountLocation( line, + os.path.join(zeekLogDir, 'live'), + sectionIndents[currentSection] * 3, ) - if ('.rule') in line: - line = re.sub( - r'(traefik\.http\.routers\.osmalcolm\.rule\s*:\s*)(\S+)', - fr"\g<1>'Host(`{traefikOpenSearchHost}`)'", - line, - ) - if 'traefik.http.routers.malcolm.rule' in line: - # Malcolm interface router host value - line = re.sub( - r'(traefik\.http\.routers\.malcolm\.rule\s*:\s*)(\S+)', - fr"\g<1>'Host(`{traefikHost}`)'", + elif re.match(r'^\s*-.+:/zeek/extract_files(:.+)?\s*$', line): + # zeek-lives's reference to the zeek-logs/extract_files directory + line = ReplaceBindMountLocation( line, + os.path.join(zeekLogDir, 'extract_files'), + sectionIndents[currentSection] * 3, ) - elif 'traefik.http.routers.' in line and '.entrypoints' in line: - # Malcolm routers entrypoints + elif currentService == 'nginx-proxy': + # stuff specifically in the nginx-proxy section + + if re.match(r'^\s*test\s*:', line): + # set nginx-proxy health check based on whether they're using HTTPS or not line = re.sub( - r'(traefik\.[\w\.]+\s*:\s*)(\S+)', - fr"\g<1>'{traefikEntrypoint}'", + r'https?://localhost:\d+', + fr"{'https' if nginxSSL else 'http'}://localhost:443", line, ) - elif 'traefik.http.routers.' in line and '.certresolver' in line: - # Malcolm routers resolvers + elif re.match(r'^[\s#]*-\s*"([\d\.]+:)?\d+:\d+"\s*$', line): + # set bind IPs and ports based on whether it should be externally exposed or not line = re.sub( - r'(traefik\.[\w\.]+\s*:\s*)(\S+)', - fr"\g<1>'{traefikResolver}'", + r'^([\s#]*-\s*")([\d\.]+:)?(\d+:\d+"\s*)$', + fr"\g<1>{'0.0.0.0' if nginxSSL and (((not '9200:9200' in line) and (not '5601:5601' in line)) or opensearchOpen) else '127.0.0.1'}:\g<3>", line, ) + if nginxSSL is False: + if ':443:' in line: + line = line.replace(':443:', ':80:') + if ':9200:' in line: + line = line.replace(':9200:', ':9201:') + else: + if ':80:' in line: + line = line.replace(':80:', ':443:') + if ':9201:' in line: + line = line.replace(':9201:', ':9200:') + + elif 'traefik.' in line: + # enable/disable/configure traefik labels if applicable + + # Traefik enabled vs. disabled + if 'traefik.enable' in line: + line = re.sub( + r'(#\s*)?(traefik\.enable\s*:\s*)(\S+)', + fr"\g<2>{TrueOrFalseQuote(behindReverseProxy and traefikLabels)}", + line, + ) + else: + line = re.sub( + r'(#\s*)?(traefik\..*)', + fr"{'' if traefikLabels else '# '}\g<2>", + line, + ) - elif currentSection == 'networks': - # re-write the network definition from scratch - if not sectionStartLine: - if not networkWritten: - print(f"{sectionIndents[currentSection]}default:") - print( - f"{sectionIndents[currentSection] * 2}external: {'true' if (len(dockerNetworkExternalName) > 0) else 'false'}" - ) - if len(dockerNetworkExternalName) > 0: - print(f"{sectionIndents[currentSection] * 2}name: {dockerNetworkExternalName}") - networkWritten = True - # we already re-wrote the network stuff, anything else is superfluous - skipLine = True + if 'traefik.http.' in line and '.osmalcolm.' in line: + # OpenSearch router enabled/disabled/host value + line = re.sub( + r'(#\s*)?(traefik\..*)', + fr"{'' if behindReverseProxy and traefikLabels and opensearchOpen else '# '}\g<2>", + line, + ) + if ('.rule') in line: + line = re.sub( + r'(traefik\.http\.routers\.osmalcolm\.rule\s*:\s*)(\S+)', + fr"\g<1>'Host(`{traefikOpenSearchHost}`)'", + line, + ) + + if 'traefik.http.routers.malcolm.rule' in line: + # Malcolm interface router host value + line = re.sub( + r'(traefik\.http\.routers\.malcolm\.rule\s*:\s*)(\S+)', + fr"\g<1>'Host(`{traefikHost}`)'", + line, + ) - if not skipLine: - print(line) + elif 'traefik.http.routers.' in line and '.entrypoints' in line: + # Malcolm routers entrypoints + line = re.sub( + r'(traefik\.[\w\.]+\s*:\s*)(\S+)', + fr"\g<1>'{traefikEntrypoint}'", + line, + ) - finally: - composeFileHandle.close() - # restore ownership - os.chown(composeFile, origUid, origGuid) + elif 'traefik.http.routers.' in line and '.certresolver' in line: + # Malcolm routers resolvers + line = re.sub( + r'(traefik\.[\w\.]+\s*:\s*)(\S+)', + fr"\g<1>'{traefikResolver}'", + line, + ) + + elif currentSection == 'networks': + # re-write the network definition from scratch + if not sectionStartLine: + if not networkWritten: + print(f"{sectionIndents[currentSection]}default:") + print( + f"{sectionIndents[currentSection] * 2}external: {'true' if (len(dockerNetworkExternalName) > 0) else 'false'}" + ) + if len(dockerNetworkExternalName) > 0: + print(f"{sectionIndents[currentSection] * 2}name: {dockerNetworkExternalName}") + networkWritten = True + # we already re-wrote the network stuff, anything else is superfluous + skipLine = True + + if not skipLine: + print(line) + + finally: + composeFileHandle.close() + # restore ownership + os.chown(composeFile, origUid, origGuid) try: touch(MalcolmCfgRunOnceFile) @@ -1699,8 +1753,8 @@ def tweak_malcolm_runtime( ################################################################################################### class LinuxInstaller(Installer): # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - def __init__(self, debug=False, configOnly=False): - super().__init__(debug, configOnly) + def __init__(self, orchMode, debug=False, configOnly=False): + super().__init__(orchMode, debug, configOnly) self.distro = None self.codename = None @@ -1843,194 +1897,203 @@ def install_docker(self): result = False - # first see if docker is already installed and runnable - err, out = self.run_process(['docker', 'info'], privileged=True) - - if err == 0: - result = True - - elif InstallerYesOrNo('"docker info" failed, attempt to install Docker?', default=True): - if InstallerYesOrNo('Attempt to install Docker using official repositories?', default=True): - # install required packages for repo-based install - if self.distro == PLATFORM_LINUX_UBUNTU: - requiredRepoPackages = [ - 'apt-transport-https', - 'ca-certificates', - 'curl', - 'gnupg-agent', - 'software-properties-common', - ] - elif self.distro == PLATFORM_LINUX_DEBIAN: - requiredRepoPackages = [ - 'apt-transport-https', - 'ca-certificates', - 'curl', - 'gnupg2', - 'software-properties-common', - ] - elif self.distro == PLATFORM_LINUX_FEDORA: - requiredRepoPackages = ['dnf-plugins-core'] - elif self.distro == PLATFORM_LINUX_CENTOS: - requiredRepoPackages = ['yum-utils', 'device-mapper-persistent-data', 'lvm2'] - else: - requiredRepoPackages = [] + if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: + # first see if docker is already installed and runnable + err, out = self.run_process(['docker', 'info'], privileged=True) - if len(requiredRepoPackages) > 0: - eprint(f"Installing required packages: {requiredRepoPackages}") - self.install_package(requiredRepoPackages) + if err == 0: + result = True - # install docker via repo if possible - dockerPackages = [] - if ((self.distro == PLATFORM_LINUX_UBUNTU) or (self.distro == PLATFORM_LINUX_DEBIAN)) and self.codename: - # for debian/ubuntu, add docker GPG key and check its fingerprint - if self.debug: - eprint("Requesting docker GPG key for package signing") - dockerGpgKey = requests_imported.get( - f'https://download.docker.com/linux/{self.distro}/gpg', allow_redirects=True - ) - err, out = self.run_process( - ['apt-key', 'add'], - stdin=dockerGpgKey.content.decode(sys.getdefaultencoding()), - privileged=True, - stderr=False, - ) - if err == 0: + elif InstallerYesOrNo('"docker info" failed, attempt to install Docker?', default=True): + if InstallerYesOrNo('Attempt to install Docker using official repositories?', default=True): + # install required packages for repo-based install + if self.distro == PLATFORM_LINUX_UBUNTU: + requiredRepoPackages = [ + 'apt-transport-https', + 'ca-certificates', + 'curl', + 'gnupg-agent', + 'software-properties-common', + ] + elif self.distro == PLATFORM_LINUX_DEBIAN: + requiredRepoPackages = [ + 'apt-transport-https', + 'ca-certificates', + 'curl', + 'gnupg2', + 'software-properties-common', + ] + elif self.distro == PLATFORM_LINUX_FEDORA: + requiredRepoPackages = ['dnf-plugins-core'] + elif self.distro == PLATFORM_LINUX_CENTOS: + requiredRepoPackages = ['yum-utils', 'device-mapper-persistent-data', 'lvm2'] + else: + requiredRepoPackages = [] + + if len(requiredRepoPackages) > 0: + eprint(f"Installing required packages: {requiredRepoPackages}") + self.install_package(requiredRepoPackages) + + # install docker via repo if possible + dockerPackages = [] + if ( + (self.distro == PLATFORM_LINUX_UBUNTU) or (self.distro == PLATFORM_LINUX_DEBIAN) + ) and self.codename: + # for debian/ubuntu, add docker GPG key and check its fingerprint + if self.debug: + eprint("Requesting docker GPG key for package signing") + dockerGpgKey = requests_imported.get( + f'https://download.docker.com/linux/{self.distro}/gpg', allow_redirects=True + ) err, out = self.run_process( - ['apt-key', 'fingerprint', DEB_GPG_KEY_FINGERPRINT], privileged=True, stderr=False + ['apt-key', 'add'], + stdin=dockerGpgKey.content.decode(sys.getdefaultencoding()), + privileged=True, + stderr=False, ) + if err == 0: + err, out = self.run_process( + ['apt-key', 'fingerprint', DEB_GPG_KEY_FINGERPRINT], privileged=True, stderr=False + ) - # add docker .deb repository - if err == 0: + # add docker .deb repository + if err == 0: + if self.debug: + eprint("Adding docker repository") + err, out = self.run_process( + [ + 'add-apt-repository', + '-y', + '-r', + f'deb [arch=amd64] https://download.docker.com/linux/{self.distro} {self.codename} stable', + ], + privileged=True, + ) + err, out = self.run_process( + [ + 'add-apt-repository', + '-y', + '-u', + f'deb [arch=amd64] https://download.docker.com/linux/{self.distro} {self.codename} stable', + ], + privileged=True, + ) + + # docker packages to install + if err == 0: + dockerPackages.extend( + ['docker-ce', 'docker-ce-cli', 'docker-compose-plugin', 'containerd.io'] + ) + + elif self.distro == PLATFORM_LINUX_FEDORA: + # add docker fedora repository if self.debug: eprint("Adding docker repository") err, out = self.run_process( [ - 'add-apt-repository', + 'dnf', + 'config-manager', '-y', - '-r', - f'deb [arch=amd64] https://download.docker.com/linux/{self.distro} {self.codename} stable', + '--add-repo', + 'https://download.docker.com/linux/fedora/docker-ce.repo', ], privileged=True, ) + + # docker packages to install + if err == 0: + dockerPackages.extend( + ['docker-ce', 'docker-ce-cli', 'docker-compose-plugin', 'containerd.io'] + ) + + elif self.distro == PLATFORM_LINUX_CENTOS: + # add docker centos repository + if self.debug: + eprint("Adding docker repository") err, out = self.run_process( [ - 'add-apt-repository', + 'yum-config-manager', '-y', - '-u', - f'deb [arch=amd64] https://download.docker.com/linux/{self.distro} {self.codename} stable', + '--add-repo', + 'https://download.docker.com/linux/centos/docker-ce.repo', ], privileged=True, ) - # docker packages to install - if err == 0: - dockerPackages.extend(['docker-ce', 'docker-ce-cli', 'docker-compose-plugin', 'containerd.io']) - - elif self.distro == PLATFORM_LINUX_FEDORA: - # add docker fedora repository - if self.debug: - eprint("Adding docker repository") - err, out = self.run_process( - [ - 'dnf', - 'config-manager', - '-y', - '--add-repo', - 'https://download.docker.com/linux/fedora/docker-ce.repo', - ], - privileged=True, - ) - - # docker packages to install - if err == 0: - dockerPackages.extend(['docker-ce', 'docker-ce-cli', 'docker-compose-plugin', 'containerd.io']) - - elif self.distro == PLATFORM_LINUX_CENTOS: - # add docker centos repository - if self.debug: - eprint("Adding docker repository") - err, out = self.run_process( - [ - 'yum-config-manager', - '-y', - '--add-repo', - 'https://download.docker.com/linux/centos/docker-ce.repo', - ], - privileged=True, - ) + # docker packages to install + if err == 0: + dockerPackages.extend( + ['docker-ce', 'docker-ce-cli', 'docker-compose-plugin', 'containerd.io'] + ) - # docker packages to install - if err == 0: - dockerPackages.extend(['docker-ce', 'docker-ce-cli', 'docker-compose-plugin', 'containerd.io']) + else: + err, out = None, None - else: - err, out = None, None + if len(dockerPackages) > 0: + eprint(f"Installing docker packages: {dockerPackages}") + if self.install_package(dockerPackages): + eprint("Installation of docker packages apparently succeeded") + result = True + else: + eprint("Installation of docker packages failed") - if len(dockerPackages) > 0: - eprint(f"Installing docker packages: {dockerPackages}") - if self.install_package(dockerPackages): - eprint("Installation of docker packages apparently succeeded") - result = True + # the user either chose not to use the official repos, the official repo installation failed, or there are not official repos available + # see if we want to attempt using the convenience script at https://get.docker.com (see https://github.com/docker/docker-install) + if not result and InstallerYesOrNo( + 'Docker not installed via official repositories. Attempt to install Docker via convenience script (please read https://github.com/docker/docker-install)?', + default=False, + ): + tempFileName = os.path.join(self.tempDirName, 'docker-install.sh') + if DownloadToFile("https://get.docker.com/", tempFileName, debug=self.debug): + os.chmod(tempFileName, 493) # 493 = 0o755 + err, out = self.run_process(([tempFileName]), privileged=True) + if err == 0: + eprint("Installation of docker apparently succeeded") + result = True + else: + eprint(f"Installation of docker failed: {out}") else: - eprint("Installation of docker packages failed") + eprint(f"Downloading {dockerComposeUrl} to {tempFileName} failed") - # the user either chose not to use the official repos, the official repo installation failed, or there are not official repos available - # see if we want to attempt using the convenience script at https://get.docker.com (see https://github.com/docker/docker-install) - if not result and InstallerYesOrNo( - 'Docker not installed via official repositories. Attempt to install Docker via convenience script (please read https://github.com/docker/docker-install)?', - default=False, - ): - tempFileName = os.path.join(self.tempDirName, 'docker-install.sh') - if DownloadToFile("https://get.docker.com/", tempFileName, debug=self.debug): - os.chmod(tempFileName, 493) # 493 = 0o755 - err, out = self.run_process(([tempFileName]), privileged=True) - if err == 0: - eprint("Installation of docker apparently succeeded") - result = True - else: - eprint(f"Installation of docker failed: {out}") + if result and ((self.distro == PLATFORM_LINUX_FEDORA) or (self.distro == PLATFORM_LINUX_CENTOS)): + # centos/fedora don't automatically start/enable the daemon, so do so now + err, out = self.run_process(['systemctl', 'start', 'docker'], privileged=True) + if err == 0: + err, out = self.run_process(['systemctl', 'enable', 'docker'], privileged=True) + if err != 0: + eprint(f"Enabling docker service failed: {out}") else: - eprint(f"Downloading {dockerComposeUrl} to {tempFileName} failed") - - if result and ((self.distro == PLATFORM_LINUX_FEDORA) or (self.distro == PLATFORM_LINUX_CENTOS)): - # centos/fedora don't automatically start/enable the daemon, so do so now - err, out = self.run_process(['systemctl', 'start', 'docker'], privileged=True) - if err == 0: - err, out = self.run_process(['systemctl', 'enable', 'docker'], privileged=True) - if err != 0: - eprint(f"Enabling docker service failed: {out}") - else: - eprint(f"Starting docker service failed: {out}") - - # at this point we either have installed docker successfully or we have to give up, as we've tried all we could - err, out = self.run_process(['docker', 'info'], privileged=True, retry=6, retrySleepSec=5) - if result and (err == 0): - if self.debug: - eprint('"docker info" succeeded') + eprint(f"Starting docker service failed: {out}") - # add non-root user to docker group if required - usersToAdd = [] - if self.scriptUser == 'root': - while InstallerYesOrNo( - f"Add {'a' if len(usersToAdd) == 0 else 'another'} non-root user to the \"docker\" group?" - ): - tmpUser = InstallerAskForString('Enter user account') - if len(tmpUser) > 0: - usersToAdd.append(tmpUser) - else: - usersToAdd.append(self.scriptUser) + # at this point we either have installed docker successfully or we have to give up, as we've tried all we could + err, out = self.run_process(['docker', 'info'], privileged=True, retry=6, retrySleepSec=5) + if result and (err == 0): + if self.debug: + eprint('"docker info" succeeded') - for user in usersToAdd: - err, out = self.run_process(['usermod', '-a', '-G', 'docker', user], privileged=True) - if err == 0: - if self.debug: - eprint(f'Adding {user} to "docker" group succeeded') + # add non-root user to docker group if required + usersToAdd = [] + if self.scriptUser == 'root': + while InstallerYesOrNo( + f"Add {'a' if len(usersToAdd) == 0 else 'another'} non-root user to the \"docker\" group?" + ): + tmpUser = InstallerAskForString('Enter user account') + if len(tmpUser) > 0: + usersToAdd.append(tmpUser) else: - eprint(f'Adding {user} to "docker" group failed') + usersToAdd.append(self.scriptUser) + + for user in usersToAdd: + err, out = self.run_process(['usermod', '-a', '-G', 'docker', user], privileged=True) + if err == 0: + if self.debug: + eprint(f'Adding {user} to "docker" group succeeded') + else: + eprint(f'Adding {user} to "docker" group failed') - elif err != 0: - result = False - raise Exception(f'{ScriptName} requires docker, please see {DOCKER_INSTALL_URLS[self.distro]}') + elif err != 0: + result = False + raise Exception(f'{ScriptName} requires docker, please see {DOCKER_INSTALL_URLS[self.distro]}') return result @@ -2038,81 +2101,82 @@ def install_docker(self): def install_docker_compose(self): result = False - dockerComposeCmd = 'docker-compose' - if not which(dockerComposeCmd, debug=self.debug): - if os.path.isfile('/usr/libexec/docker/cli-plugins/docker-compose'): - dockerComposeCmd = '/usr/libexec/docker/cli-plugins/docker-compose' - elif os.path.isfile('/usr/local/bin/docker-compose'): - dockerComposeCmd = '/usr/local/bin/docker-compose' + if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: + dockerComposeCmd = 'docker-compose' + if not which(dockerComposeCmd, debug=self.debug): + if os.path.isfile('/usr/libexec/docker/cli-plugins/docker-compose'): + dockerComposeCmd = '/usr/libexec/docker/cli-plugins/docker-compose' + elif os.path.isfile('/usr/local/bin/docker-compose'): + dockerComposeCmd = '/usr/local/bin/docker-compose' - # first see if docker-compose is already installed and runnable (try non-root and root) - err, out = self.run_process([dockerComposeCmd, 'version'], privileged=False) - if err != 0: - err, out = self.run_process([dockerComposeCmd, 'version'], privileged=True) + # first see if docker-compose is already installed and runnable (try non-root and root) + err, out = self.run_process([dockerComposeCmd, 'version'], privileged=False) + if err != 0: + err, out = self.run_process([dockerComposeCmd, 'version'], privileged=True) + + if (err != 0) and InstallerYesOrNo( + '"docker-compose version" failed, attempt to install docker-compose?', default=True + ): + if InstallerYesOrNo('Install docker-compose directly from docker github?', default=True): + # download docker-compose from github and put it in /usr/local/bin + + # need to know some linux platform info + unames = [] + err, out = self.run_process((['uname', '-s'])) + if (err == 0) and (len(out) > 0): + unames.append(out[0].lower()) + err, out = self.run_process((['uname', '-m'])) + if (err == 0) and (len(out) > 0): + unames.append(out[0].lower()) + if len(unames) == 2: + # download docker-compose from github and save it to a temporary file + tempFileName = os.path.join(self.tempDirName, dockerComposeCmd) + dockerComposeUrl = f"https://github.com/docker/compose/releases/download/v{DOCKER_COMPOSE_INSTALL_VERSION}/docker-compose-{unames[0]}-{unames[1]}" + if DownloadToFile(dockerComposeUrl, tempFileName, debug=self.debug): + os.chmod(tempFileName, 493) # 493 = 0o755, mark as executable + # put docker-compose into /usr/local/bin + err, out = self.run_process( + (['cp', '-f', tempFileName, '/usr/local/bin/docker-compose']), privileged=True + ) + if err == 0: + eprint("Download and installation of docker-compose apparently succeeded") + dockerComposeCmd = '/usr/local/bin/docker-compose' + else: + raise Exception(f'Error copying {tempFileName} to /usr/local/bin: {out}') - if (err != 0) and InstallerYesOrNo( - '"docker-compose version" failed, attempt to install docker-compose?', default=True - ): - if InstallerYesOrNo('Install docker-compose directly from docker github?', default=True): - # download docker-compose from github and put it in /usr/local/bin - - # need to know some linux platform info - unames = [] - err, out = self.run_process((['uname', '-s'])) - if (err == 0) and (len(out) > 0): - unames.append(out[0].lower()) - err, out = self.run_process((['uname', '-m'])) - if (err == 0) and (len(out) > 0): - unames.append(out[0].lower()) - if len(unames) == 2: - # download docker-compose from github and save it to a temporary file - tempFileName = os.path.join(self.tempDirName, dockerComposeCmd) - dockerComposeUrl = f"https://github.com/docker/compose/releases/download/v{DOCKER_COMPOSE_INSTALL_VERSION}/docker-compose-{unames[0]}-{unames[1]}" - if DownloadToFile(dockerComposeUrl, tempFileName, debug=self.debug): - os.chmod(tempFileName, 493) # 493 = 0o755, mark as executable - # put docker-compose into /usr/local/bin - err, out = self.run_process( - (['cp', '-f', tempFileName, '/usr/local/bin/docker-compose']), privileged=True - ) - if err == 0: - eprint("Download and installation of docker-compose apparently succeeded") - dockerComposeCmd = '/usr/local/bin/docker-compose' else: - raise Exception(f'Error copying {tempFileName} to /usr/local/bin: {out}') + eprint(f"Downloading {dockerComposeUrl} to {tempFileName} failed") + elif InstallerYesOrNo('Install docker-compose via pip (privileged)?', default=False): + # install docker-compose via pip (as root) + err, out = self.run_process([self.pipCmd, 'install', dockerComposeCmd], privileged=True) + if err == 0: + eprint("Installation of docker-compose apparently succeeded") else: - eprint(f"Downloading {dockerComposeUrl} to {tempFileName} failed") - - elif InstallerYesOrNo('Install docker-compose via pip (privileged)?', default=False): - # install docker-compose via pip (as root) - err, out = self.run_process([self.pipCmd, 'install', dockerComposeCmd], privileged=True) - if err == 0: - eprint("Installation of docker-compose apparently succeeded") - else: - eprint(f"Install docker-compose via pip failed with {err}, {out}") + eprint(f"Install docker-compose via pip failed with {err}, {out}") - elif InstallerYesOrNo('Install docker-compose via pip (user)?', default=True): - # install docker-compose via pip (regular user) - err, out = self.run_process([self.pipCmd, 'install', dockerComposeCmd], privileged=False) - if err == 0: - eprint("Installation of docker-compose apparently succeeded") - else: - eprint(f"Install docker-compose via pip failed with {err}, {out}") + elif InstallerYesOrNo('Install docker-compose via pip (user)?', default=True): + # install docker-compose via pip (regular user) + err, out = self.run_process([self.pipCmd, 'install', dockerComposeCmd], privileged=False) + if err == 0: + eprint("Installation of docker-compose apparently succeeded") + else: + eprint(f"Install docker-compose via pip failed with {err}, {out}") - # see if docker-compose is now installed and runnable (try non-root and root) - err, out = self.run_process([dockerComposeCmd, 'version'], privileged=False) - if err != 0: - err, out = self.run_process([dockerComposeCmd, 'version'], privileged=True) + # see if docker-compose is now installed and runnable (try non-root and root) + err, out = self.run_process([dockerComposeCmd, 'version'], privileged=False) + if err != 0: + err, out = self.run_process([dockerComposeCmd, 'version'], privileged=True) - if err == 0: - result = True - if self.debug: - eprint('"docker-compose version" succeeded') + if err == 0: + result = True + if self.debug: + eprint('"docker-compose version" succeeded') - else: - raise Exception( - f'{ScriptName} requires docker-compose, please see {DOCKER_COMPOSE_INSTALL_URLS[self.platform]}' - ) + else: + raise Exception( + f'{ScriptName} requires docker-compose, please see {DOCKER_COMPOSE_INSTALL_URLS[self.platform]}' + ) return result @@ -2265,8 +2329,8 @@ def tweak_system_files(self): ################################################################################################### class MacInstaller(Installer): # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - def __init__(self, debug=False, configOnly=False): - super().__init__(debug, configOnly) + def __init__(self, orchMode, debug=False, configOnly=False): + super().__init__(orchMode, debug, configOnly) self.sudoCmd = [] @@ -2279,7 +2343,6 @@ def __init__(self, debug=False, configOnly=False): else: self.useBrew = False - eprint('Docker can be installed and maintained with Homebrew, or manually.') if (not brewInstalled) and ( not InstallerYesOrNo('Homebrew is not installed: continue with manual installation?', default=False) ): @@ -2338,145 +2401,150 @@ def __init__(self, debug=False, configOnly=False): def install_docker(self): result = False - # first see if docker is already installed/runnable - err, out = self.run_process(['docker', 'info']) + if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: + # first see if docker is already installed/runnable + err, out = self.run_process(['docker', 'info']) - if (err != 0) and self.useBrew and self.package_is_installed(MAC_BREW_DOCKER_PACKAGE): - # if docker is installed via brew, but not running, prompt them to start it - eprint(f'{MAC_BREW_DOCKER_PACKAGE} appears to be installed via Homebrew, but "docker info" failed') - while True: - response = InstallerAskForString( - 'Starting Docker the first time may require user interaction. Please find and start Docker in the Applications folder, then return here and type YES' - ).lower() - if response == 'yes': - break - err, out = self.run_process(['docker', 'info'], retry=12, retrySleepSec=5) + if (err != 0) and self.useBrew and self.package_is_installed(MAC_BREW_DOCKER_PACKAGE): + # if docker is installed via brew, but not running, prompt them to start it + eprint(f'{MAC_BREW_DOCKER_PACKAGE} appears to be installed via Homebrew, but "docker info" failed') + while True: + response = InstallerAskForString( + 'Starting Docker the first time may require user interaction. Please find and start Docker in the Applications folder, then return here and type YES' + ).lower() + if response == 'yes': + break + err, out = self.run_process(['docker', 'info'], retry=12, retrySleepSec=5) - # did docker info work? - if err == 0: - result = True + # did docker info work? + if err == 0: + result = True - elif InstallerYesOrNo('"docker info" failed, attempt to install Docker?', default=True): - if self.useBrew: - # install docker via brew cask (requires user interaction) - dockerPackages = [MAC_BREW_DOCKER_PACKAGE, "docker-compose"] - eprint(f"Installing docker packages: {dockerPackages}") - if self.install_package(dockerPackages): - eprint("Installation of docker packages apparently succeeded") - while True: - response = InstallerAskForString( - 'Starting Docker the first time may require user interaction. Please find and start Docker in the Applications folder, then return here and type YES' - ).lower() - if response == 'yes': - break - else: - eprint("Installation of docker packages failed") + elif InstallerYesOrNo('"docker info" failed, attempt to install Docker?', default=True): + if self.useBrew: + # install docker via brew cask (requires user interaction) + dockerPackages = [MAC_BREW_DOCKER_PACKAGE, "docker-compose"] + eprint(f"Installing docker packages: {dockerPackages}") + if self.install_package(dockerPackages): + eprint("Installation of docker packages apparently succeeded") + while True: + response = InstallerAskForString( + 'Starting Docker the first time may require user interaction. Please find and start Docker in the Applications folder, then return here and type YES' + ).lower() + if response == 'yes': + break + else: + eprint("Installation of docker packages failed") - else: - # install docker via downloaded dmg file (requires user interaction) - dlDirName = f'/Users/{self.scriptUser}/Downloads' - if os.path.isdir(dlDirName): - tempFileName = os.path.join(dlDirName, 'Docker.dmg') else: - tempFileName = os.path.join(self.tempDirName, 'Docker.dmg') - if DownloadToFile('https://download.docker.com/mac/edge/Docker.dmg', tempFileName, debug=self.debug): - while True: - response = InstallerAskForString( - f'Installing and starting Docker the first time may require user interaction. Please open Finder and install {tempFileName}, start Docker from the Applications folder, then return here and type YES' - ).lower() - if response == 'yes': - break + # install docker via downloaded dmg file (requires user interaction) + dlDirName = f'/Users/{self.scriptUser}/Downloads' + if os.path.isdir(dlDirName): + tempFileName = os.path.join(dlDirName, 'Docker.dmg') + else: + tempFileName = os.path.join(self.tempDirName, 'Docker.dmg') + if DownloadToFile( + 'https://download.docker.com/mac/edge/Docker.dmg', tempFileName, debug=self.debug + ): + while True: + response = InstallerAskForString( + f'Installing and starting Docker the first time may require user interaction. Please open Finder and install {tempFileName}, start Docker from the Applications folder, then return here and type YES' + ).lower() + if response == 'yes': + break + + # at this point we either have installed docker successfully or we have to give up, as we've tried all we could + err, out = self.run_process(['docker', 'info'], retry=12, retrySleepSec=5) + if err == 0: + result = True + if self.debug: + eprint('"docker info" succeeded') - # at this point we either have installed docker successfully or we have to give up, as we've tried all we could - err, out = self.run_process(['docker', 'info'], retry=12, retrySleepSec=5) - if err == 0: - result = True - if self.debug: - eprint('"docker info" succeeded') + elif err != 0: + raise Exception( + f'{ScriptName} requires docker edge, please see {DOCKER_INSTALL_URLS[self.platform]}' + ) elif err != 0: raise Exception(f'{ScriptName} requires docker edge, please see {DOCKER_INSTALL_URLS[self.platform]}') - elif err != 0: - raise Exception(f'{ScriptName} requires docker edge, please see {DOCKER_INSTALL_URLS[self.platform]}') - - # tweak CPU/RAM usage for Docker in Mac - settingsFile = MAC_BREW_DOCKER_SETTINGS.format(self.scriptUser) - if ( - result - and os.path.isfile(settingsFile) - and InstallerYesOrNo(f'Configure Docker resource usage in {settingsFile}?', default=True) - ): - # adjust CPU and RAM based on system resources - if self.totalCores >= 16: - newCpus = 12 - elif self.totalCores >= 12: - newCpus = 8 - elif self.totalCores >= 8: - newCpus = 6 - elif self.totalCores >= 4: - newCpus = 4 - else: - newCpus = 2 - - if self.totalMemoryGigs >= 64.0: - newMemoryGiB = 32 - elif self.totalMemoryGigs >= 32.0: - newMemoryGiB = 24 - elif self.totalMemoryGigs >= 24.0: - newMemoryGiB = 16 - elif self.totalMemoryGigs >= 16.0: - newMemoryGiB = 12 - elif self.totalMemoryGigs >= 8.0: - newMemoryGiB = 8 - elif self.totalMemoryGigs >= 4.0: - newMemoryGiB = 4 - else: - newMemoryGiB = 2 - - while not InstallerYesOrNo( - f"Setting {newCpus if newCpus else '(unchanged)'} for CPU cores and {newMemoryGiB if newMemoryGiB else '(unchanged)'} GiB for RAM. Is this OK?", - default=True, + # tweak CPU/RAM usage for Docker in Mac + settingsFile = MAC_BREW_DOCKER_SETTINGS.format(self.scriptUser) + if ( + result + and os.path.isfile(settingsFile) + and InstallerYesOrNo(f'Configure Docker resource usage in {settingsFile}?', default=True) ): - newCpus = InstallerAskForString('Enter Docker CPU cores (e.g., 4, 8, 16)') - newMemoryGiB = InstallerAskForString('Enter Docker RAM MiB (e.g., 8, 16, etc.)') - - if newCpus or newMemoryGiB: - with open(settingsFile, 'r+') as f: - data = json.load(f) - if newCpus: - data['cpus'] = int(newCpus) - if newMemoryGiB: - data['memoryMiB'] = int(newMemoryGiB) * 1024 - f.seek(0) - json.dump(data, f, indent=2) - f.truncate() - - # at this point we need to essentially update our system memory stats because we're running inside docker - # and don't have the whole banana at our disposal - self.totalMemoryGigs = newMemoryGiB - - eprint("Docker resource settings adjusted, attempting restart...") - - err, out = self.run_process(['osascript', '-e', 'quit app "Docker"']) - if err == 0: - time.sleep(5) - err, out = self.run_process(['open', '-a', 'Docker']) + # adjust CPU and RAM based on system resources + if self.totalCores >= 16: + newCpus = 12 + elif self.totalCores >= 12: + newCpus = 8 + elif self.totalCores >= 8: + newCpus = 6 + elif self.totalCores >= 4: + newCpus = 4 + else: + newCpus = 2 + + if self.totalMemoryGigs >= 64.0: + newMemoryGiB = 32 + elif self.totalMemoryGigs >= 32.0: + newMemoryGiB = 24 + elif self.totalMemoryGigs >= 24.0: + newMemoryGiB = 16 + elif self.totalMemoryGigs >= 16.0: + newMemoryGiB = 12 + elif self.totalMemoryGigs >= 8.0: + newMemoryGiB = 8 + elif self.totalMemoryGigs >= 4.0: + newMemoryGiB = 4 + else: + newMemoryGiB = 2 - if err == 0: - err, out = self.run_process(['docker', 'info'], retry=12, retrySleepSec=5) + while not InstallerYesOrNo( + f"Setting {newCpus if newCpus else '(unchanged)'} for CPU cores and {newMemoryGiB if newMemoryGiB else '(unchanged)'} GiB for RAM. Is this OK?", + default=True, + ): + newCpus = InstallerAskForString('Enter Docker CPU cores (e.g., 4, 8, 16)') + newMemoryGiB = InstallerAskForString('Enter Docker RAM MiB (e.g., 8, 16, etc.)') + + if newCpus or newMemoryGiB: + with open(settingsFile, 'r+') as f: + data = json.load(f) + if newCpus: + data['cpus'] = int(newCpus) + if newMemoryGiB: + data['memoryMiB'] = int(newMemoryGiB) * 1024 + f.seek(0) + json.dump(data, f, indent=2) + f.truncate() + + # at this point we need to essentially update our system memory stats because we're running inside docker + # and don't have the whole banana at our disposal + self.totalMemoryGigs = newMemoryGiB + + eprint("Docker resource settings adjusted, attempting restart...") + + err, out = self.run_process(['osascript', '-e', 'quit app "Docker"']) if err == 0: - if self.debug: - eprint('"docker info" succeeded') + time.sleep(5) + err, out = self.run_process(['open', '-a', 'Docker']) - else: - eprint(f"Restarting Docker automatically failed: {out}") - while True: - response = InstallerAskForString( - 'Please restart Docker via the system taskbar, then return here and type YES' - ).lower() - if response == 'yes': - break + if err == 0: + err, out = self.run_process(['docker', 'info'], retry=12, retrySleepSec=5) + if err == 0: + if self.debug: + eprint('"docker info" succeeded') + + else: + eprint(f"Restarting Docker automatically failed: {out}") + while True: + response = InstallerAskForString( + 'Please restart Docker via the system taskbar, then return here and type YES' + ).lower() + if response == 'yes': + break return result @@ -2486,6 +2554,7 @@ def install_docker(self): def main(): global args global requests_imported + global kube_imported global yaml_imported global dotenv_imported @@ -2535,7 +2604,7 @@ def main(): metavar='', type=str, default='', - help='Single docker-compose YML file to configure', + help='YAML file (docker-compose file to configure or kubeconfig file)', ) parser.add_argument( '-e', @@ -2628,6 +2697,15 @@ def main(): if (not requests_imported) or (not yaml_imported) or (not dotenv_imported): exit(2) + orchMode = OrchestrationFramework.UNKNOWN + if args.configFile and os.path.isfile(args.configFile): + if not ( + (orchMode := DetermineYamlFileFormat(args.configFile)) and (orchMode in OrchestrationFrameworksSupported) + ): + raise Exception(f'{args.configFile} must be a docker-compose or kubeconfig YAML file') + else: + orchMode = OrchestrationFramework.DOCKER_COMPOSE + # If Malcolm and images tarballs are provided, we will use them. # If they are not provided, look in the pwd first, then in the script directory, to see if we # can locate the most recent tarballs @@ -2663,12 +2741,12 @@ def main(): installerPlatform = platform.system() if installerPlatform == PLATFORM_LINUX: - installer = LinuxInstaller(debug=args.debug, configOnly=args.configOnly) + installer = LinuxInstaller(orchMode, debug=args.debug, configOnly=args.configOnly) elif installerPlatform == PLATFORM_MAC: - installer = MacInstaller(debug=args.debug, configOnly=args.configOnly) + installer = MacInstaller(orchMode, debug=args.debug, configOnly=args.configOnly) elif installerPlatform == PLATFORM_WINDOWS: raise Exception(f'{ScriptName} is not yet supported on {installerPlatform}') - installer = WindowsInstaller(debug=args.debug, configOnly=args.configOnly) + installer = WindowsInstaller(orchMode, debug=args.debug, configOnly=args.configOnly) success = False installPath = None @@ -2676,13 +2754,13 @@ def main(): if not args.configOnly: if hasattr(installer, 'install_required_packages'): success = installer.install_required_packages() - if hasattr(installer, 'install_docker'): + if (orchMode is OrchestrationFramework.DOCKER_COMPOSE) and hasattr(installer, 'install_docker'): success = installer.install_docker() - if hasattr(installer, 'install_docker_compose'): + if (orchMode is OrchestrationFramework.DOCKER_COMPOSE) and hasattr(installer, 'install_docker_compose'): success = installer.install_docker_compose() if hasattr(installer, 'tweak_system_files'): success = installer.tweak_system_files() - if hasattr(installer, 'install_docker_images'): + if (orchMode is OrchestrationFramework.DOCKER_COMPOSE) and hasattr(installer, 'install_docker_images'): success = installer.install_docker_images(imageFile) # if .env directory is unspecified, use the default ./config directory @@ -2696,6 +2774,17 @@ def main(): else: raise + if orchMode is OrchestrationFramework.KUBERNETES: + kube_imported = KubernetesDynamic(debug=args.debug) + if args.debug: + eprint(f"Imported kubernetes: {kube_imported}") + if kube_imported: + kube_imported.config.load_kube_config(args.configFile) + else: + raise Exception( + f'{ScriptName} requires the official Python client library for kubernetes for {orchMode} mode' + ) + if ( args.configOnly or (args.configFile and os.path.isfile(args.configFile)) From b2aefab2e669e165c84f6ac73726367d16c5c14a Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 17 Apr 2023 10:57:36 -0600 Subject: [PATCH 156/239] work in progress on control.py's logs function for idaholab/Malcolm#172 --- kubernetes/services-dev-plan.md | 53 ----- scripts/control.py | 335 +++++++++----------------------- scripts/malcolm_common.py | 214 +++++++++++++++++++- 3 files changed, 303 insertions(+), 299 deletions(-) delete mode 100644 kubernetes/services-dev-plan.md diff --git a/kubernetes/services-dev-plan.md b/kubernetes/services-dev-plan.md deleted file mode 100644 index f97923067..000000000 --- a/kubernetes/services-dev-plan.md +++ /dev/null @@ -1,53 +0,0 @@ -## Services for which Kubernetes manifests need to be developed - -See **support Malcolm deployment with Kubernetes** [idaholab/Malcolm#149](https://github.com/idaholab/Malcolm/issues/149) - -"Core services" are listed earlier in the list (i.e., we should probably approach the services roughly in this order). - -* nginx-proxy -* opensearch - - This is more complicated if we do OpenSearch in the container vs. [a remote instance](https://idaholab.github.io/Malcolm/docs/opensearch-instances.html#OpenSearchInstance). My recommendation is to do early development of this container with a remote instance (see the corresponding [`docker-compose.yml` section](https://github.com/idaholab/Malcolm/blob/0c14303f242ce1bae7e48b30ca7234c996930008/docker-compose-standalone.yml#L46-L68)) and then come back to it. We can still do the service/container with the `OPENSEARCH_LOCAL` variable set to `false`, which will cause the [`service_check_passthrough.sh`](https://github.com/idaholab/Malcolm/blob/main/shared/bin/service_check_passthrough.sh) script to get run at startup instead of the actual OpenSearch service. Note that [this requires](https://idaholab.github.io/Malcolm/docs/opensearch-instances.html#OpenSearchAuth) [LDAP authentication](https://idaholab.github.io/Malcolm/docs/authsetup.html#AuthLDAP), or to use basic authentication but ensure the accounts and passwords match on Malcolm and the remote OpenSearch instance. -* dashboards -* upload -* pcap-monitor -* arkime -* api -* dashboards-helper -* zeek -* suricata -* file-monitor -* filebeat -* freq - - This one really doesn't have many dependencies on other services, so it could be done whenever. -* logstash -* netbox-redis -* netbox-redis-cache -* netbox-postgres -* netbox -* htadmin - - Leaving this for towards the end since if we're using remote OpenSearch we'd be using LDAP anyway, and even if not we can use auth_setup to set a username/password. At the point this is done we'll no longer be able to use `configmap` for `nginx/htpasswd` (and `htadmin/config.ini`) as they'll need to be read/write. -* pcap-capture - - Not sure what live capture looks like at all in this scenario: what would the capture interface be? So leaving this to the very end. -* zeek-live - - See note for `pcap-capture`. -* suricata-live - - See note for `pcap-capture`. - -## Groupings - -Note: this is all dependent on if we can get things in the same deployment to communicate via hostname. - -* dashboards - - dashboards - - dashboards-helper -* logs/enrichment - - logstash - - freq -* netbox - - netbox - - netbox-redis - - netbox-redis-cache - - netbox-postgres -* proxy/auth - - nginx-proxy - - htadmin diff --git a/scripts/control.py b/scripts/control.py index 1178dd0ba..6f9df7236 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -15,6 +15,7 @@ import re import secrets import shutil +import signal import stat import string import sys @@ -39,6 +40,7 @@ OrchestrationFramework, OrchestrationFrameworksSupported, PLATFORM_WINDOWS, + ProcessLogLine, posInt, ScriptPath, YAMLDynamic, @@ -97,6 +99,7 @@ def __exit__(self, *args): kubeImported = None opensslBin = None orchMode = None +shuttingDown = [False] yamlImported = None @@ -110,6 +113,13 @@ def __exit__(self, *args): coloramaImported = False +################################################################################################### +# handle sigint/sigterm and set a global shutdown variable +def shutdown_handler(signum, frame): + global shuttingDown + shuttingDown[0] = True + + ################################################################################################### def checkEnvFilesExist(): global args @@ -503,86 +513,17 @@ def logs(): global dockerBin global dockerComposeBin global orchMode - - urlUserPassRegEx = re.compile(r'(\w+://[^/]+?:)[^/]+?(@[^/]+)') - - # noisy logs (a lot of it is NGINX logs from health checks) - ignoreRegEx = re.compile( - r""" - .+( - deprecated - | "GET\s+/\s+HTTP/1\.\d+"\s+200\s+- - | \bGET.+\b302\s+30\b - | (async|output)\.go.+(reset\s+by\s+peer|Connecting\s+to\s+backoff|backoff.+established$) - | /(opensearch-dashboards|dashboards|kibana)/(api/ui_metric/report|internal/search/(es|opensearch)) - | (Error\s+during\s+file\s+comparison|File\s+was\s+renamed):\s+/zeek/live/logs/ - | /_ns_/nstest\.html - | /usr/share/logstash/x-pack/lib/filters/geoip/database_manager - | \b(d|es)?stats\.json - | \b1.+GET\s+/\s+.+401.+curl - | _cat/indices - | branding.*config\s+is\s+not\s+found\s+or\s+invalid - | but\s+there\s+are\s+no\s+living\s+connections - | Connecting\s+to\s+backoff - | curl.+localhost.+GET\s+/api/status\s+200 - | DEPRECATION - | descheduling\s+job\s*id - | eshealth - | esindices/list - | executing\s+attempt_(transition|set_replica_count)\s+for - | GET\s+/(netbox/api|_cat/health|api/status|sessions2-|arkime_\w+).+HTTP/[\d\.].+\b200\b - | loaded\s+config\s+'/etc/netbox/config/ - | "netbox"\s+application\s+started - | \[notice\].+app\s+process\s+\d+\s+exited\s+with\s+code\s+0\b - | POST\s+/(arkime_\w+)(/\w+)?/_(d?stat|doc|search).+HTTP/[\d\.].+\b20[01]\b - | POST\s+/_bulk\s+HTTP/[\d\.].+\b20[01]\b - | POST\s+/server/php/\s+HTTP/\d+\.\d+"\s+\d+\s+\d+.*:8443/ - | POST\s+HTTP/[\d\.].+\b200\b - | reaped\s+unknown\s+pid - | redis.*(changes.+seconds.+Saving|Background\s+saving\s+(started|terminated)|DB\s+saved\s+on\s+disk|Fork\s+CoW) - | remov(ed|ing)\s+(old\s+file|dead\s+symlink|empty\s+directory) - | retry\.go.+(send\s+unwait|done$) - | running\s+full\s+sweep - | saved_objects - | scheduling\s+job\s*id.+opendistro-ism - | SSL/TLS\s+verifications\s+disabled - | Successfully\s+handled\s+GET\s+request\s+for\s+'/' - | Test\s+run\s+complete.*:failed=>0,\s*:errored=>0\b - | throttling\s+index - | update_mapping - | updating\s+number_of_replicas - | use_field_mapping - | Using\s+geoip\s+database - ) - """, - re.VERBOSE | re.IGNORECASE, - ) - - # logs we don't want to eliminate, but we don't want to repeat ad-nauseum - # TODO: not implemented yet - # dupeRegEx = re.compile( - # r""" - # .+( - # Maybe the destination pipeline is down or stopping - # ) - # """, - # re.VERBOSE | re.IGNORECASE, - # ) - - serviceRegEx = re.compile(r'^(?P.+?\|)\s*(?P.*)$') - iso8601TimeRegEx = re.compile( - r'^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$' - ) + global shuttingDown finishedStartingRegEx = re.compile(r'.+Pipelines\s+running\s+\{.*:non_running_pipelines=>\[\]\}') - finishedStarting = False + + osEnv = os.environ.copy() + # use local temporary path + osEnv['TMPDIR'] = MalcolmTmpPath if orchMode is OrchestrationFramework.DOCKER_COMPOSE: # increase COMPOSE_HTTP_TIMEOUT to be ridiculously large so docker-compose never times out the TTY doing debug output - osEnv = os.environ.copy() osEnv['COMPOSE_HTTP_TIMEOUT'] = '100000000' - # docker-compose use local temporary path - osEnv['TMPDIR'] = MalcolmTmpPath err, out = run_process( [dockerComposeBin, '-f', args.composeFile, 'ps', args.service][: 5 if args.service is not None else -1], @@ -591,187 +532,86 @@ def logs(): ) print("\n".join(out)) - if args.logLineCount is None: - args.logLineCount = 'all' + cmd = [ + dockerComposeBin, + '-f', + args.composeFile, + 'logs', + '--tail', + str(args.logLineCount) if args.logLineCount else 'all', + '-f', + args.service, + ][: 8 if args.service else -1] - process = Popen( - [ - dockerComposeBin, - '-f', + elif orchMode is OrchestrationFramework.KUBERNETES: + if which("stern"): + cmd = [ + "stern", + "--kubeconfig", args.composeFile, - 'logs', + "--only-log-lines", + "--color", + 'auto' if coloramaImported else 'never', + "--template", + '{{.Namespace}}/{{color .PodColor .PodName}}/{{color .ContainerColor .ContainerName}} | {{.Message}}{{"\\n"}}' + if args.debug + else '{{color .ContainerColor .ContainerName}} | {{.Message}}{{"\\n"}}', '--tail', - str(args.logLineCount), - '-f', - args.service, - ][: 8 if args.service is not None else -1], - env=osEnv, - stdout=PIPE, - stderr=None if args.debug else DEVNULL, - ) - while True: - output = process.stdout.readline() - if (len(output) == 0) and (process.poll() is not None): - break - if output: - outputStr = urlUserPassRegEx.sub(r"\1xxxxxxxx\2", output.decode().strip()) - outputStrEscaped = EscapeAnsi(outputStr) - if ignoreRegEx.match(outputStrEscaped): - # print(f'!!!!!!!: {outputStr}') - pass - elif ( - (args.cmdStart or args.cmdRestart) - and (not args.cmdLogs) - and finishedStartingRegEx.match(outputStrEscaped) - ): - finishedStarting = True - else: - serviceMatch = serviceRegEx.search(outputStrEscaped) - serviceMatchFmt = serviceRegEx.search(outputStr) if coloramaImported else serviceMatch - serviceStr = serviceMatchFmt.group('service') if (serviceMatchFmt is not None) else '' - - messageStr = serviceMatch.group('message') if (serviceMatch is not None) else '' - messageStrSplit = messageStr.split(' ') - messageTimeMatch = iso8601TimeRegEx.match(messageStrSplit[0]) - if (messageTimeMatch is None) or (len(messageStrSplit) <= 1): - messageStrToTestJson = messageStr - else: - messageStrToTestJson = messageStrSplit[1:].join(' ') - - outputJson = LoadStrIfJson(messageStrToTestJson) - if isinstance(outputJson, dict): - # if there's a timestamp, move it outside of the JSON to the beginning of the log string - timeKey = None - if 'time' in outputJson: - timeKey = 'time' - elif 'timestamp' in outputJson: - timeKey = 'timestamp' - elif '@timestamp' in outputJson: - timeKey = '@timestamp' - timeStr = '' - if timeKey is not None: - timeStr = f"{outputJson[timeKey]} " - outputJson.pop(timeKey, None) - elif messageTimeMatch is not None: - timeStr = f"{messageTimeMatch[0]} " - - if ( - ('job.schedule' in outputJson) - and ('job.position' in outputJson) - and ('job.command' in outputJson) - ): - # this is a status output line from supercronic, let's format and clean it up so it fits in better with the rest of the logs - - # remove some clutter for the display - for noisyKey in ['level', 'channel', 'iteration', 'job.position', 'job.schedule']: - outputJson.pop(noisyKey, None) - - # if it's just command and message, format those NOT as JSON - jobCmd = outputJson['job.command'] - jobStatus = outputJson['msg'] - if ( - (len(outputJson.keys()) == 2) - and ('job.command' in outputJson) - and ('msg' in outputJson) - ): - # if it's the most common status (starting or job succeeded) then don't print unless debug mode - if args.debug or ((jobStatus != 'starting') and (jobStatus != 'job succeeded')): - print( - f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr} {jobCmd}: {jobStatus}" - ) - else: - pass - - else: - # standardize and print the JSON output - print( - f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" - ) - - elif 'dashboards' in serviceStr: - # this is an output line from dashboards, let's clean it up a bit: remove some clutter for the display - for noisyKey in ['type', 'tags', 'pid', 'method', 'prevState', 'prevMsg']: - outputJson.pop(noisyKey, None) - - # standardize and print the JSON output - print( - f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" - ) - - elif 'filebeat' in serviceStr: - # this is an output line from filebeat, let's clean it up a bit: remove some clutter for the display - for noisyKey in [ - 'ecs.version', - 'harvester_id', - 'input_id', - 'log.level', - 'log.logger', - 'log.origin', - 'os_id', - 'service.name', - 'state_id', - ]: - outputJson.pop(noisyKey, None) - - # we'll fancify a couple of common things from filebeat - if ( - (len(outputJson.keys()) == 3) - and ('message' in outputJson) - and ('source_file' in outputJson) - and ('finished' in outputJson) - ): - print( - f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{outputJson['message'].rstrip('.')}: {outputJson['source_file']}" - ) - - elif len(outputJson.keys()) == 1: - outputKey = next(iter(outputJson)) - print( - f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{outputKey + ': ' if outputKey != 'message' else ''}{outputJson[outputKey]}" - ) - else: - # standardize and print the JSON output - print( - f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" - ) - - else: - # standardize and print the JSON output - print( - f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" - ) - - else: - # just a regular non-JSON string, print as-is - print(outputStr if coloramaImported else outputStrEscaped) + str(args.logLineCount) if args.logLineCount else '-1', + ] + if args.namespace: + cmd.extend(['--namespace', args.namespace]) else: - time.sleep(0.5) + cmd.append('--all-namespaces') + cmd.append(args.service if args.service else '.*') - if finishedStarting: - process.terminate() - try: - process.wait(timeout=5.0) - except TimeoutExpired: - process.kill() - # # TODO: Replace 'localhost' with an outwards-facing IP since I doubt anybody is - # accessing these from the Malcolm server. - print("\nStarted Malcolm\n\n") - print("Malcolm services can be accessed via the following URLs:") - print("------------------------------------------------------------------------------") - print(" - Arkime: https://localhost/") - print(" - OpenSearch Dashboards: https://localhost/dashboards/") - print(" - PCAP upload (web): https://localhost/upload/") - print(" - PCAP upload (sftp): sftp://username@127.0.0.1:8022/files/") - print(" - NetBox: https://localhost/netbox/\n") - print(" - Account management: https://localhost/auth/\n") - print(" - Documentation: https://localhost/readme/\n") - - process.poll() + else: + raise Exception( + f'{sys._getframe().f_code.co_name} with orchestration mode {orchMode} requires "stern" (https://github.com/stern/stern/releases/latest)' + ) else: raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') + process = Popen( + cmd, + env=osEnv, + stdout=PIPE, + stderr=None if args.debug else DEVNULL, + ) + while not shuttingDown[0]: + output = process.stdout.readline() + if (len(output) == 0) and (process.poll() is not None): + break + + if output := ProcessLogLine(output, debug=args.debug): + print(output) + + else: + time.sleep(0.5) + + if output and (args.cmdStart or args.cmdRestart) and (not args.cmdLogs) and finishedStartingRegEx.match(output): + process.terminate() + try: + process.wait(timeout=5.0) + except TimeoutExpired: + process.kill() + # # TODO: Replace 'localhost' with an outwards-facing IP since I doubt anybody is + # accessing these from the Malcolm server. + print("\nStarted Malcolm\n\n") + print("Malcolm services can be accessed via the following URLs:") + print("------------------------------------------------------------------------------") + print(" - Arkime: https://localhost/") + print(" - OpenSearch Dashboards: https://localhost/dashboards/") + print(" - PCAP upload (web): https://localhost/upload/") + print(" - PCAP upload (sftp): sftp://username@127.0.0.1:8022/files/") + print(" - NetBox: https://localhost/netbox/\n") + print(" - Account management: https://localhost/auth/\n") + print(" - Documentation: https://localhost/readme/\n") + + process.poll() + ################################################################################################### def stop(wipe=False): @@ -1661,6 +1501,7 @@ def main(): global kubeImported global opensslBin global orchMode + global shuttingDown global yamlImported # extract arguments from the command line @@ -1827,6 +1668,10 @@ def main(): else: sys.tracebacklimit = 0 + # handle sigint and sigterm for graceful shutdown + signal.signal(signal.SIGINT, shutdown_handler) + signal.signal(signal.SIGTERM, shutdown_handler) + yamlImported = YAMLDynamic(debug=args.debug) if args.debug: eprint(f"Imported yaml: {yamlImported}") diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index dc741628c..b1afc87b4 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -5,6 +5,7 @@ import getpass import importlib +import json import os import platform import re @@ -12,7 +13,15 @@ import sys import malcolm_utils -from malcolm_utils import eprint, str2bool, run_process, deep_get +from malcolm_utils import ( + deep_get, + eprint, + EscapeAnsi, + LoadStrIfJson, + remove_suffix, + run_process, + str2bool, +) from collections import defaultdict, namedtuple from enum import Flag, IntFlag, auto @@ -30,6 +39,14 @@ Dialog = None MainDialog = None +try: + from colorama import init as ColoramaInit, Fore, Back, Style + + ColoramaInit() + coloramaImported = True +except Exception: + coloramaImported = False + ################################################################################################### ScriptPath = os.path.dirname(os.path.realpath(__file__)) MalcolmPath = os.path.abspath(os.path.join(ScriptPath, os.pardir)) @@ -631,3 +648,198 @@ def DownloadToFile(url, local_filename, debug=False): f"Download of {url} to {local_filename} {'succeeded' if fExists else 'failed'} ({malcolm_utils.sizeof_fmt(fSize)})" ) return fExists and (fSize > 0) + + +################################################################################################### +# process log line from containers' output + +URL_USER_PASS_REGEX = re.compile(r'(\w+://[^/]+?:)[^/]+?(@[^/]+)') + +# noisy logs (a lot of it is NGINX logs from health checks) +LOG_IGNORE_REGEX = re.compile( + r""" +.+( + deprecated + | "GET\s+/\s+HTTP/1\.\d+"\s+200\s+- + | \bGET.+\b302\s+30\b + | (async|output)\.go.+(reset\s+by\s+peer|Connecting\s+to\s+backoff|backoff.+established$) + | /(opensearch-dashboards|dashboards|kibana)/(api/ui_metric/report|internal/search/(es|opensearch)) + | (Error\s+during\s+file\s+comparison|File\s+was\s+renamed):\s+/zeek/live/logs/ + | /_ns_/nstest\.html + | /usr/share/logstash/x-pack/lib/filters/geoip/database_manager + | \b(d|es)?stats\.json + | \b1.+GET\s+/\s+.+401.+curl + | _cat/indices + | branding.*config\s+is\s+not\s+found\s+or\s+invalid + | but\s+there\s+are\s+no\s+living\s+connections + | Connecting\s+to\s+backoff + | curl.+localhost.+GET\s+/api/status\s+200 + | DEPRECATION + | descheduling\s+job\s*id + | eshealth + | esindices/list + | executing\s+attempt_(transition|set_replica_count)\s+for + | GET\s+/(netbox/api|_cat/health|api/status|sessions2-|arkime_\w+).+HTTP/[\d\.].+\b200\b + | loaded\s+config\s+'/etc/netbox/config/ + | "netbox"\s+application\s+started + | \[notice\].+app\s+process\s+\d+\s+exited\s+with\s+code\s+0\b + | POST\s+/(arkime_\w+)(/\w+)?/_(d?stat|doc|search).+HTTP/[\d\.].+\b20[01]\b + | POST\s+/_bulk\s+HTTP/[\d\.].+\b20[01]\b + | POST\s+/server/php/\s+HTTP/\d+\.\d+"\s+\d+\s+\d+.*:8443/ + | POST\s+HTTP/[\d\.].+\b200\b + | reaped\s+unknown\s+pid + | redis.*(changes.+seconds.+Saving|Background\s+saving\s+(started|terminated)|DB\s+saved\s+on\s+disk|Fork\s+CoW) + | remov(ed|ing)\s+(old\s+file|dead\s+symlink|empty\s+directory) + | retry\.go.+(send\s+unwait|done$) + | running\s+full\s+sweep + | saved_objects + | scheduling\s+job\s*id.+opendistro-ism + | SSL/TLS\s+verifications\s+disabled + | Successfully\s+handled\s+GET\s+request\s+for\s+'/' + | Test\s+run\s+complete.*:failed=>0,\s*:errored=>0\b + | throttling\s+index + | update_mapping + | updating\s+number_of_replicas + | use_field_mapping + | Using\s+geoip\s+database +) +""", + re.VERBOSE | re.IGNORECASE, +) + +# logs we don't want to eliminate, but we don't want to repeat ad-nauseum +# TODO: not implemented yet +# dupeRegEx = re.compile( +# r""" +# .+( +# Maybe the destination pipeline is down or stopping +# ) +# """, +# re.VERBOSE | re.IGNORECASE, +# ) + +SERVICE_REGEX = re.compile(r'^(?P.+?\|)\s*(?P.*)$') + +CONTAINER_REPL_REGEX = re.compile(r'([\w\.-]+)-container(\s*\|)') + +ISO8601_TIME_REGEX = re.compile( + r'^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$' +) + + +def ProcessLogLine(line, debug=False): + global ISO8601_TIME_REGEX + global LOG_IGNORE_REGEX + global SERVICE_REGEX + global URL_USER_PASS_REGEX + + outputStr = CONTAINER_REPL_REGEX.sub(r"\1\2", URL_USER_PASS_REGEX.sub(r"\1xxxxxxxx\2", line.decode().strip())) + outputStrEscaped = EscapeAnsi(outputStr) + if LOG_IGNORE_REGEX.match(outputStrEscaped): + return None + else: + serviceMatch = SERVICE_REGEX.search(outputStrEscaped) + serviceMatchFmt = SERVICE_REGEX.search(outputStr) if coloramaImported else serviceMatch + serviceStr = serviceMatchFmt.group('service').replace('-container', '') if (serviceMatchFmt is not None) else '' + + messageStr = serviceMatch.group('message') if (serviceMatch is not None) else '' + messageStrSplit = messageStr.split(' ') + messageTimeMatch = ISO8601_TIME_REGEX.match(messageStrSplit[0]) + if (messageTimeMatch is None) or (len(messageStrSplit) <= 1): + messageStrToTestJson = messageStr + else: + messageStrToTestJson = messageStrSplit[1:].join(' ') + + outputJson = LoadStrIfJson(messageStrToTestJson) + if isinstance(outputJson, dict): + # if there's a timestamp, move it outside of the JSON to the beginning of the log string + timeKey = None + if 'time' in outputJson: + timeKey = 'time' + elif 'timestamp' in outputJson: + timeKey = 'timestamp' + elif '@timestamp' in outputJson: + timeKey = '@timestamp' + timeStr = '' + if timeKey is not None: + timeStr = f"{outputJson[timeKey]} " + outputJson.pop(timeKey, None) + elif messageTimeMatch is not None: + timeStr = f"{messageTimeMatch[0]} " + + if ('job.schedule' in outputJson) and ('job.position' in outputJson) and ('job.command' in outputJson): + # this is a status line line from supercronic, let's format and clean it up so it fits in better with the rest of the logs + + # remove some clutter for the display + for noisyKey in ['level', 'channel', 'iteration', 'job.position', 'job.schedule']: + outputJson.pop(noisyKey, None) + + # if it's just command and message, format those NOT as JSON + jobCmd = outputJson['job.command'] + jobStatus = outputJson['msg'] + if (len(outputJson.keys()) == 2) and ('job.command' in outputJson) and ('msg' in outputJson): + # if it's the most common status (starting or job succeeded) then don't print unless debug mode + if debug or ((jobStatus != 'starting') and (jobStatus != 'job succeeded')): + return ( + f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr} {jobCmd}: {jobStatus}" + ) + else: + return None + + else: + # standardize and print the JSON line + return ( + f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" + ) + + elif 'dashboards' in serviceStr: + # this is an line line from dashboards, let's clean it up a bit: remove some clutter for the display + for noisyKey in ['type', 'tags', 'pid', 'method', 'prevState', 'prevMsg']: + outputJson.pop(noisyKey, None) + + # standardize and print the JSON line + return f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" + + elif 'filebeat' in serviceStr: + # this is an line line from filebeat, let's clean it up a bit: remove some clutter for the display + for noisyKey in [ + 'ecs.version', + 'harvester_id', + 'input_id', + 'log.level', + 'log.logger', + 'log.origin', + 'os_id', + 'service.name', + 'state_id', + ]: + outputJson.pop(noisyKey, None) + + # we'll fancify a couple of common things from filebeat + if ( + (len(outputJson.keys()) == 3) + and ('message' in outputJson) + and ('source_file' in outputJson) + and ('finished' in outputJson) + ): + return f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{outputJson['message'].rstrip('.')}: {outputJson['source_file']}" + + elif len(outputJson.keys()) == 1: + outputKey = next(iter(outputJson)) + return f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{outputKey + ': ' if outputKey != 'message' else ''}{outputJson[outputKey]}" + + else: + # standardize and print the JSON line + return ( + f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" + ) + + else: + # standardize and print the JSON line + return f"{serviceStr}{Style.RESET_ALL if coloramaImported else ''} {timeStr}{json.dumps(outputJson)}" + + else: + # just a regular non-JSON string, print as-is + return outputStr if coloramaImported else outputStrEscaped + + return None From 0e873f55b45519c714f5ed7b37421a36d88ab0bc Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 17 Apr 2023 12:12:19 -0600 Subject: [PATCH 157/239] work in progress on control.py's function for idaholab/Malcolm#172 --- scripts/control.py | 88 +++++++++++++++++++++-------------- scripts/malcolm_kubernetes.py | 18 +++++++ 2 files changed, 70 insertions(+), 36 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index 6f9df7236..8a8ae0ccd 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -65,6 +65,7 @@ from malcolm_kubernetes import ( PrintPodStatus, PrintNodeStatus, + DeleteNamespace, ) from base64 import b64encode @@ -572,45 +573,51 @@ def logs(): ) else: - raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') + cmd = [] - process = Popen( - cmd, - env=osEnv, - stdout=PIPE, - stderr=None if args.debug else DEVNULL, - ) - while not shuttingDown[0]: - output = process.stdout.readline() - if (len(output) == 0) and (process.poll() is not None): - break + if cmd: + process = Popen( + cmd, + env=osEnv, + stdout=PIPE, + stderr=None if args.debug else DEVNULL, + ) + while not shuttingDown[0]: + output = process.stdout.readline() + if (len(output) == 0) and (process.poll() is not None): + break - if output := ProcessLogLine(output, debug=args.debug): - print(output) + if output := ProcessLogLine(output, debug=args.debug): + print(output) - else: - time.sleep(0.5) - - if output and (args.cmdStart or args.cmdRestart) and (not args.cmdLogs) and finishedStartingRegEx.match(output): - process.terminate() - try: - process.wait(timeout=5.0) - except TimeoutExpired: - process.kill() - # # TODO: Replace 'localhost' with an outwards-facing IP since I doubt anybody is - # accessing these from the Malcolm server. - print("\nStarted Malcolm\n\n") - print("Malcolm services can be accessed via the following URLs:") - print("------------------------------------------------------------------------------") - print(" - Arkime: https://localhost/") - print(" - OpenSearch Dashboards: https://localhost/dashboards/") - print(" - PCAP upload (web): https://localhost/upload/") - print(" - PCAP upload (sftp): sftp://username@127.0.0.1:8022/files/") - print(" - NetBox: https://localhost/netbox/\n") - print(" - Account management: https://localhost/auth/\n") - print(" - Documentation: https://localhost/readme/\n") - - process.poll() + else: + time.sleep(0.5) + + if ( + output + and (args.cmdStart or args.cmdRestart) + and (not args.cmdLogs) + and finishedStartingRegEx.match(output) + ): + process.terminate() + try: + process.wait(timeout=5.0) + except TimeoutExpired: + process.kill() + # # TODO: Replace 'localhost' with an outwards-facing IP since I doubt anybody is + # accessing these from the Malcolm server. + print("\nStarted Malcolm\n\n") + print("Malcolm services can be accessed via the following URLs:") + print("------------------------------------------------------------------------------") + print(" - Arkime: https://localhost/") + print(" - OpenSearch Dashboards: https://localhost/dashboards/") + print(" - PCAP upload (web): https://localhost/upload/") + print(" - PCAP upload (sftp): sftp://username@127.0.0.1:8022/files/") + print(" - NetBox: https://localhost/netbox/\n") + print(" - Account management: https://localhost/auth/\n") + print(" - Documentation: https://localhost/readme/\n") + + process.poll() ################################################################################################### @@ -706,6 +713,15 @@ def stop(wipe=False): eprint("Malcolm has been stopped and its data cleared\n") + elif orchMode is OrchestrationFramework.KUBERNETES: + deleteResults = DeleteNamespace(namespace=args.namespace) + eprint(f"The {args.namespace} namespace and its underlying resources have been deleted\n") + if args.debug: + eprint(deleteResults) + + if wipe: + eprint(f'Data on PersistentVolume storage cannot be deleted by {ScriptName}: it must be deleted manually\n') + else: raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index 617de8ba8..9c4c4a3c2 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -11,6 +11,7 @@ from malcolm_utils import ( eprint, tablify, + LoadStrIfJson, ) @@ -230,3 +231,20 @@ def PrintPodStatus(namespace=None): statusRows.append([str(x) for x in pod_summary[pod]]) tablify(statusRows) + + +def DeleteNamespace(namespace): + results_dict = {} + if namespace: + if kubeImported := KubernetesDynamic(): + try: + results_dict[namespace] = kubeImported.client.CoreV1Api().delete_namespace( + namespace, + propagation_policy='Foreground', + ) + except kubeImported.client.rest.ApiException as x: + results_dict[namespace] = LoadStrIfJson(str(x)) + if not results_dict[namespace]: + results_dict[namespace] = str(x) + + return results_dict From 3e2c208d91210a745e72ea28cb9190b4ffa48611 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 17 Apr 2023 16:12:23 -0600 Subject: [PATCH 158/239] work in progress on control.py's function for idaholab/Malcolm#172 --- scripts/control.py | 44 +++++++- scripts/malcolm_kubernetes.py | 195 +++++++++++++++++++++++++++++++++- scripts/malcolm_utils.py | 10 ++ 3 files changed, 242 insertions(+), 7 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index 8a8ae0ccd..7068039fb 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -30,6 +30,7 @@ DetermineYamlFileFormat, DisplayMessage, DisplayProgramBox, + DotEnvDynamic, GetUidGidFromComposeFile, KubernetesDynamic, LocalPathForContainerBindMount, @@ -48,6 +49,7 @@ ) from malcolm_utils import ( + dictsearch, eprint, EscapeForCurl, EscapeAnsi, @@ -66,6 +68,7 @@ PrintPodStatus, PrintNodeStatus, DeleteNamespace, + StartMalcolm, ) from base64 import b64encode @@ -102,6 +105,7 @@ def __exit__(self, *args): orchMode = None shuttingDown = [False] yamlImported = None +dotenvImported = None ################################################################################################### @@ -715,9 +719,19 @@ def stop(wipe=False): elif orchMode is OrchestrationFramework.KUBERNETES: deleteResults = DeleteNamespace(namespace=args.namespace) - eprint(f"The {args.namespace} namespace and its underlying resources have been deleted\n") - if args.debug: + + if dictsearch(deleteResults, 'error'): + eprint( + f"Deleting {args.namespace} namespace and its underlying resources returned the following error(s):\n" + ) eprint(deleteResults) + eprint() + + else: + eprint(f"The {args.namespace} namespace and its underlying resources have been deleted\n") + if args.debug: + eprint(deleteResults) + eprint() if wipe: eprint(f'Data on PersistentVolume storage cannot be deleted by {ScriptName}: it must be deleted manually\n') @@ -845,6 +859,25 @@ def start(): eprint("\n".join(out)) exit(err) + elif orchMode is OrchestrationFramework.KUBERNETES: + startResults = StartMalcolm( + namespace=args.namespace, + malcolmPath=MalcolmPath, + configPath=args.configDir, + ) + + if dictsearch(startResults, 'error'): + eprint( + f"Starting the {args.namespace} namespace and creating its underlying resources returned the following error(s):\n" + ) + eprint(startResults) + eprint() + + elif args.debug: + eprint() + eprint(startResults) + eprint() + else: raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') @@ -1519,6 +1552,7 @@ def main(): global orchMode global shuttingDown global yamlImported + global dotenvImported # extract arguments from the command line # print (sys.argv[1:]); @@ -1694,6 +1728,12 @@ def main(): if not yamlImported: exit(2) + dotenvImported = DotEnvDynamic(debug=args.debug) + if args.debug: + eprint(f"Imported dotenv: {dotenvImported}") + if not dotenvImported: + exit(2) + if not ((orchMode := DetermineYamlFileFormat(args.composeFile)) and (orchMode in OrchestrationFrameworksSupported)): raise Exception(f'{args.composeFile} must be a docker-compose or kubeconfig YAML file') diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index 9c4c4a3c2..dfbde8d25 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -3,13 +3,21 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. +import glob +import os + from concurrent.futures import ThreadPoolExecutor, as_completed +from collections import defaultdict from malcolm_common import ( KubernetesDynamic, + DotEnvDynamic, + MalcolmPath, ) from malcolm_utils import ( eprint, + file_contents, + remove_suffix, tablify, LoadStrIfJson, ) @@ -18,6 +26,66 @@ ################################################################################################### MALCOLM_IMAGE_PREFIX = 'ghcr.io/idaholab/malcolm/' +MALCOLM_CONFIGMAPS = { + 'etc-nginx': [ + os.path.join(MalcolmPath, os.path.join('nginx', 'nginx_ldap.conf')), + os.path.join(MalcolmPath, os.path.join('nginx', 'nginx.conf')), + ], + 'var-local-catrust': [ + os.path.join(MalcolmPath, os.path.join('nginx', 'ca-trust')), + ], + 'etc-nginx-certs': [ + os.path.join(MalcolmPath, os.path.join('nginx', 'certs')), + ], + 'etc-nginx-certs-pem': [ + os.path.join(MalcolmPath, os.path.join(os.path.join('nginx', 'certs'), 'dhparam.pem')), + ], + 'etc-nginx-auth': [ + os.path.join(MalcolmPath, os.path.join('nginx', 'htpasswd')), + ], + 'opensearch-curlrc': [ + os.path.join(MalcolmPath, '.opensearch.primary.curlrc'), + os.path.join(MalcolmPath, '.opensearch.secondary.curlrc'), + ], + 'opensearch-keystore': [ + os.path.join(MalcolmPath, os.path.join('opensearch', 'opensearch.keystore')), + ], + 'logstash-certs': [ + os.path.join(MalcolmPath, os.path.join('logstash', 'certs')), + ], + 'logstash-maps': [ + os.path.join(MalcolmPath, os.path.join('logstash', 'maps')), + ], + 'logstash-keystore': [ + os.path.join(MalcolmPath, os.path.join('logstash', 'logstash.keystore')), + ], + 'yara-rules': [ + os.path.join(MalcolmPath, os.path.join('yara', 'rules')), + ], + 'suricata-rules': [ + os.path.join(MalcolmPath, os.path.join('suricata', 'rules')), + ], + 'filebeat-certs': [ + os.path.join(MalcolmPath, os.path.join('filebeat', 'certs')), + ], + 'netbox-netmap-json': [ + 'net-map.json', + ], + 'netbox-config': [ + os.path.join(MalcolmPath, os.path.join(os.path.join('netbox', 'config'), 'configuration')), + ], + 'netbox-reports': [ + os.path.join(MalcolmPath, os.path.join(os.path.join('netbox', 'config'), 'reports')), + ], + 'netbox-scripts': [ + os.path.join(MalcolmPath, os.path.join(os.path.join('netbox', 'config'), 'scripts')), + ], + 'htadmin-config': [ + os.path.join(MalcolmPath, os.path.join('htadmin', 'config.ini')), + os.path.join(MalcolmPath, os.path.join('htadmin', 'metadata')), + ], +} + ################################################################################################### def _nanocore_to_millicore(n): @@ -234,17 +302,134 @@ def PrintPodStatus(namespace=None): def DeleteNamespace(namespace): - results_dict = {} + results_dict = defaultdict(dict) if namespace: if kubeImported := KubernetesDynamic(): try: - results_dict[namespace] = kubeImported.client.CoreV1Api().delete_namespace( + results_dict[namespace]['delete_namespace'] = kubeImported.client.CoreV1Api().delete_namespace( namespace, propagation_policy='Foreground', ) except kubeImported.client.rest.ApiException as x: - results_dict[namespace] = LoadStrIfJson(str(x)) - if not results_dict[namespace]: - results_dict[namespace] = str(x) + if x.status != 404: + results_dict[namespace]['error'] = LoadStrIfJson(str(x)) + if not results_dict[namespace]['error']: + results_dict[namespace]['error'] = str(x) + + return results_dict + + +def StartMalcolm(namespace, malcolmPath, configPath): + if not namespace: + namespace = 'malcolm' + + results_dict = defaultdict(dict) + + if ( + os.path.isdir(malcolmPath) + and os.path.isdir(configPath) + and (kubeImported := KubernetesDynamic()) + and (dotenvImported := DotEnvDynamic()) + and (client := kubeImported.client.CoreV1Api()) + and (apiClient := kubeImported.client.ApiClient()) + ): + # create the namespace + try: + results_dict['create_namespace']['result'] = client.create_namespace( + kubeImported.client.V1Namespace(metadata=kubeImported.client.V1ObjectMeta(name=namespace)) + ).metadata + except kubeImported.client.rest.ApiException as x: + if x.status != 409: + results_dict['create_namespace']['error'] = LoadStrIfJson(str(x)) + if not results_dict['create_namespace']['error']: + results_dict['create_namespace']['error'] = str(x) + + # create configmaps from files + results_dict['create_namespaced_config_map']['result'] = dict() + for configMapName, configMapFiles in MALCOLM_CONFIGMAPS.items(): + try: + configMap = kubeImported.client.V1ConfigMap() + configMap.metadata = kubeImported.client.V1ObjectMeta(name=configMapName) + configMap.data = {} + for fname in configMapFiles: + if os.path.isfile(fname): + configMap.data[os.path.basename(fname)] = file_contents(fname) + elif os.path.isdir(fname): + for subfname in glob.iglob(os.path.join(os.path.join(fname, '**'), '*'), recursive=True): + if os.path.isfile(subfname): + configMap.data[os.path.basename(subfname)] = file_contents(subfname) + results_dict['create_namespaced_config_map']['result'][ + configMapName + ] = client.create_namespaced_config_map( + namespace=namespace, + body=configMap, + ).metadata + except kubeImported.client.rest.ApiException as x: + if x.status != 409: + if not results_dict['create_namespaced_config_map']['error']: + results_dict['create_namespaced_config_map']['error'] = dict() + results_dict['create_namespaced_config_map']['error'][ + os.path.basename(configMapName) + ] = LoadStrIfJson(str(x)) + if not results_dict['create_namespaced_config_map']['error'][os.path.basename(configMapName)]: + results_dict['create_namespaced_config_map']['error'][os.path.basename(configMapName)] = str(x) + + # create configmaps from .env files + results_dict['create_namespaced_config_map_from_env_file']['result'] = dict() + for envFileName in glob.iglob(os.path.join(configPath, '*.env'), recursive=False): + if os.path.isfile(envFileName): + try: + configMap = kubeImported.client.V1ConfigMap() + configMap.metadata = kubeImported.client.V1ObjectMeta( + name=remove_suffix(os.path.basename(envFileName), '.env') + '-env' + ) + configMap.data = dotenvImported.dotenv_values(envFileName) + results_dict['create_namespaced_config_map_from_env_file']['result'][ + configMap.metadata.name + ] = client.create_namespaced_config_map( + namespace=namespace, + body=configMap, + ).metadata + except kubeImported.client.rest.ApiException as x: + if x.status != 409: + if not results_dict['create_namespaced_config_map_from_env_file']['error']: + results_dict['create_namespaced_config_map_from_env_file']['error'] = dict() + results_dict['create_namespaced_config_map_from_env_file']['error'][ + os.path.basename(envFileName) + ] = LoadStrIfJson(str(x)) + if not results_dict['create_namespaced_config_map_from_env_file']['error'][ + os.path.basename(envFileName) + ]: + results_dict['create_namespaced_config_map_from_env_file']['error'][ + os.path.basename(envFileName) + ] = str(x) + + # apply manifests + results_dict['create_from_yaml']['result'] = dict() + for yamlName in sorted( + glob.iglob(os.path.join(os.path.join(malcolmPath, 'kubernetes'), '*.yml'), recursive=False) + ): + try: + results_dict['create_from_yaml']['result'][ + os.path.basename(yamlName) + ] = kubeImported.utils.create_from_yaml( + apiClient, + yamlName, + namespace=namespace, + ) + except kubeImported.client.rest.ApiException as x: + if x.status != 409: + if not results_dict['create_from_yaml']['error']: + results_dict['create_from_yaml']['error'] = dict() + results_dict['create_from_yaml']['error'][os.path.basename(yamlName)] = LoadStrIfJson(str(x)) + if not results_dict['create_from_yaml']['error'][os.path.basename(yamlName)]: + results_dict['create_from_yaml']['error'][os.path.basename(yamlName)] = str(x) + except kubeImported.utils.FailToCreateError as fe: + if [exc for exc in fe.api_exceptions if exc.status != 409]: + if not results_dict['create_from_yaml']['error']: + results_dict['create_from_yaml']['error'] = dict() + results_dict['create_from_yaml']['error'][os.path.basename(yamlName)] = LoadStrIfJson(str(fe)) + if not results_dict['create_from_yaml']['error'][os.path.basename(yamlName)]: + results_dict['create_from_yaml']['error'][os.path.basename(yamlName)] = str(fe) return results_dict diff --git a/scripts/malcolm_utils.py b/scripts/malcolm_utils.py index 05ace70a0..a6943edd8 100644 --- a/scripts/malcolm_utils.py +++ b/scripts/malcolm_utils.py @@ -468,6 +468,16 @@ def touch(filename): os.utime(filename, None) +################################################################################################### +# read the contents of a text file +def file_contents(filename, encoding='utf8'): + if os.path.isfile(filename): + with open(filename, encoding=encoding) as f: + return f.read() + else: + return None + + ################################################################################################### def val2bool(v): try: From d2e176072e55b102d1bb18664f9dcef74695c579 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 17 Apr 2023 22:24:28 -0600 Subject: [PATCH 159/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/malcolm_kubernetes.py | 43 +++++++++++++++++++++++++++-------- scripts/malcolm_utils.py | 19 +++++++++++++--- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index dfbde8d25..28f6e2086 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -3,6 +3,7 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. +import base64 import glob import os @@ -348,25 +349,47 @@ def StartMalcolm(namespace, malcolmPath, configPath): results_dict['create_namespaced_config_map']['result'] = dict() for configMapName, configMapFiles in MALCOLM_CONFIGMAPS.items(): try: - configMap = kubeImported.client.V1ConfigMap() - configMap.metadata = kubeImported.client.V1ObjectMeta(name=configMapName) - configMap.data = {} + dataMap = {} + binaryDataMap = {} for fname in configMapFiles: if os.path.isfile(fname): - configMap.data[os.path.basename(fname)] = file_contents(fname) + contents = file_contents( + fname, + binary_fallback=True, + ) + if hasattr(contents, 'decode'): + binaryDataMap[os.path.basename(fname)] = base64.b64encode(contents).decode('utf-8') + else: + dataMap[os.path.basename(fname)] = contents elif os.path.isdir(fname): for subfname in glob.iglob(os.path.join(os.path.join(fname, '**'), '*'), recursive=True): if os.path.isfile(subfname): - configMap.data[os.path.basename(subfname)] = file_contents(subfname) + contents = file_contents( + subfname, + binary_fallback=True, + ) + if hasattr(contents, 'decode'): + binaryDataMap[os.path.basename(subfname)] = base64.b64encode(contents).decode( + 'utf-8' + ) + else: + dataMap[os.path.basename(subfname)] = contents results_dict['create_namespaced_config_map']['result'][ configMapName ] = client.create_namespaced_config_map( namespace=namespace, - body=configMap, + body=kubeImported.client.V1ConfigMap( + metadata=kubeImported.client.V1ObjectMeta( + name=configMapName, + namespace=namespace, + ), + data=dataMap if dataMap else {}, + binary_data=binaryDataMap if binaryDataMap else {}, + ), ).metadata except kubeImported.client.rest.ApiException as x: if x.status != 409: - if not results_dict['create_namespaced_config_map']['error']: + if 'error' not in results_dict['create_namespaced_config_map']: results_dict['create_namespaced_config_map']['error'] = dict() results_dict['create_namespaced_config_map']['error'][ os.path.basename(configMapName) @@ -392,7 +415,7 @@ def StartMalcolm(namespace, malcolmPath, configPath): ).metadata except kubeImported.client.rest.ApiException as x: if x.status != 409: - if not results_dict['create_namespaced_config_map_from_env_file']['error']: + if 'error' not in results_dict['create_namespaced_config_map_from_env_file']: results_dict['create_namespaced_config_map_from_env_file']['error'] = dict() results_dict['create_namespaced_config_map_from_env_file']['error'][ os.path.basename(envFileName) @@ -419,14 +442,14 @@ def StartMalcolm(namespace, malcolmPath, configPath): ) except kubeImported.client.rest.ApiException as x: if x.status != 409: - if not results_dict['create_from_yaml']['error']: + if 'error' not in results_dict['create_from_yaml']: results_dict['create_from_yaml']['error'] = dict() results_dict['create_from_yaml']['error'][os.path.basename(yamlName)] = LoadStrIfJson(str(x)) if not results_dict['create_from_yaml']['error'][os.path.basename(yamlName)]: results_dict['create_from_yaml']['error'][os.path.basename(yamlName)] = str(x) except kubeImported.utils.FailToCreateError as fe: if [exc for exc in fe.api_exceptions if exc.status != 409]: - if not results_dict['create_from_yaml']['error']: + if 'error' not in results_dict['create_from_yaml']: results_dict['create_from_yaml']['error'] = dict() results_dict['create_from_yaml']['error'][os.path.basename(yamlName)] = LoadStrIfJson(str(fe)) if not results_dict['create_from_yaml']['error'][os.path.basename(yamlName)]: diff --git a/scripts/malcolm_utils.py b/scripts/malcolm_utils.py index a6943edd8..0be5f8e18 100644 --- a/scripts/malcolm_utils.py +++ b/scripts/malcolm_utils.py @@ -470,10 +470,23 @@ def touch(filename): ################################################################################################### # read the contents of a text file -def file_contents(filename, encoding='utf8'): +def file_contents(filename, encoding='utf-8', binary_fallback=False): if os.path.isfile(filename): - with open(filename, encoding=encoding) as f: - return f.read() + decodeErr = False + + try: + with open(filename, 'r', encoding=encoding) as f: + return f.read() + except (UnicodeDecodeError, AttributeError): + if binary_fallback: + decodeErr = True + else: + raise + + if decodeErr and binary_fallback: + with open(filename, 'rb') as f: + return f.read() + else: return None From 731bc74bed50e1acbae2d9819b11e4b3a63273af Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 07:52:56 -0600 Subject: [PATCH 160/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/control.py | 18 +++++++++- scripts/malcolm_kubernetes.py | 68 +++++++++++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index 7068039fb..92eca76d2 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -718,7 +718,10 @@ def stop(wipe=False): eprint("Malcolm has been stopped and its data cleared\n") elif orchMode is OrchestrationFramework.KUBERNETES: - deleteResults = DeleteNamespace(namespace=args.namespace) + deleteResults = DeleteNamespace( + namespace=args.namespace, + deleteRetPerVol=args.deleteRetPerVol, + ) if dictsearch(deleteResults, 'error'): eprint( @@ -1684,6 +1687,19 @@ def main(): default=False, help="Stop Malcolm and delete all data", ) + parser.add_argument( + '--reclaim-persistent-volume', + dest='deleteRetPerVol', + action='store_true', + help='Delete PersistentVolumes with Retain reclaim policy (default; only for "wipe" operation with Kubernetes)', + ) + parser.add_argument( + '--no-reclaim-persistent-volume', + dest='deleteRetPerVol', + action='store_false', + help='Do not delete PersistentVolumes with Retain reclaim policy (only for "wipe" operation with Kubernetes)', + ) + parser.set_defaults(deleteRetPerVol=True) parser.add_argument( '--auth', dest='cmdAuthSetup', diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index 28f6e2086..2671f506b 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -9,6 +9,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from collections import defaultdict +from itertools import chain from malcolm_common import ( KubernetesDynamic, @@ -16,8 +17,10 @@ MalcolmPath, ) from malcolm_utils import ( + deep_get, eprint, file_contents, + get_iterable, remove_suffix, tablify, LoadStrIfJson, @@ -302,12 +305,57 @@ def PrintPodStatus(namespace=None): tablify(statusRows) -def DeleteNamespace(namespace): +def DeleteNamespace(namespace, deleteRetPerVol=False): results_dict = defaultdict(dict) + if namespace: if kubeImported := KubernetesDynamic(): + k8s_api = kubeImported.client.CoreV1Api() + + manualDeletePersistentVolumes = [] + if deleteRetPerVol: + # If indicated, manually delete PersistentVolumes with "Retain" reclaim policy + # - https://kubernetes.io/docs/concepts/storage/persistent-volumes/#retain + + # get a list of PersistentVolumes to delete after the delete_namespace + # 1. from list_namespaced_persistent_volume_claim + # 2. from list_persistent_volume with the "namespace=XXXXXXX" label + manualDeletePersistentVolumes = [ + x.spec.volume_name + for x in k8s_api.list_namespaced_persistent_volume_claim( + watch=False, + namespace=namespace, + ).items + ] + manualDeletePersistentVolumes.extend( + [ + x.metadata.name + for x in k8s_api.list_persistent_volume( + label_selector=f'namespace={namespace}', + ).items + if x.spec.persistent_volume_reclaim_policy == 'Retain' + ] + ) + + # filter (ensuring we only ended up with "Retain" PersistentVolumes) and dedupe + manualDeletePersistentVolumes = list( + chain( + *[ + [ + x.metadata.name + for x in k8s_api.list_persistent_volume( + field_selector=f'metadata.name={name}', + ).items + if x.spec.persistent_volume_reclaim_policy == 'Retain' + ] + for name in set(manualDeletePersistentVolumes) + ] + ) + ) + + # delete the namespace, which should delete the resources belonging to it try: - results_dict[namespace]['delete_namespace'] = kubeImported.client.CoreV1Api().delete_namespace( + results_dict[namespace]['delete_namespace'] = k8s_api.delete_namespace( namespace, propagation_policy='Foreground', ) @@ -317,6 +365,22 @@ def DeleteNamespace(namespace): if not results_dict[namespace]['error']: results_dict[namespace]['error'] = str(x) + # If indicated, manually delete each PersistentVolume with "Retain" reclaim policy identified above + if manualDeletePersistentVolumes: + results_dict[namespace]['delete_persistent_volume'] = dict() + for name in manualDeletePersistentVolumes: + try: + results_dict[namespace]['delete_persistent_volume'][name] = k8s_api.delete_persistent_volume( + name=name + ) + except kubeImported.client.rest.ApiException as x: + if x.status != 404: + if 'error' not in results_dict[namespace]['delete_persistent_volume']: + results_dict[namespace]['delete_persistent_volume']['error'] = dict() + results_dict[namespace]['delete_persistent_volume']['error'][name] = LoadStrIfJson(str(x)) + if not results_dict[namespace]['delete_persistent_volume']['error'][name]: + results_dict[namespace]['delete_persistent_volume']['error'][name] = str(x) + return results_dict From 716c91cd7391f62ac04b02718746a3d2a54eea4c Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 07:53:37 -0600 Subject: [PATCH 161/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- kubernetes/01-volumes.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kubernetes/01-volumes.yml b/kubernetes/01-volumes.yml index 07a077c19..0f9ddf71d 100644 --- a/kubernetes/01-volumes.yml +++ b/kubernetes/01-volumes.yml @@ -4,6 +4,8 @@ kind: PersistentVolume metadata: name: pcap-volume namespace: malcolm + labels: + namespace: malcolm spec: capacity: storage: 250Gi @@ -45,6 +47,8 @@ kind: PersistentVolume metadata: name: zeek-volume namespace: malcolm + labels: + namespace: malcolm spec: capacity: storage: 100Gi @@ -86,6 +90,8 @@ kind: PersistentVolume metadata: name: suricata-volume namespace: malcolm + labels: + namespace: malcolm spec: capacity: storage: 100Gi @@ -127,6 +133,8 @@ kind: PersistentVolume metadata: name: config-volume namespace: malcolm + labels: + namespace: malcolm spec: capacity: storage: 50Gi @@ -168,6 +176,8 @@ kind: PersistentVolume metadata: name: opensearch-volume namespace: malcolm + labels: + namespace: malcolm spec: capacity: storage: 250Gi @@ -209,6 +219,8 @@ kind: PersistentVolume metadata: name: opensearch-backup-volume namespace: malcolm + labels: + namespace: malcolm spec: capacity: storage: 250Gi From aa04c8cf8de8d28da4d417128937e3b07f123818 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 07:54:42 -0600 Subject: [PATCH 162/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/control.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index 92eca76d2..fd2fe0057 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -1691,13 +1691,13 @@ def main(): '--reclaim-persistent-volume', dest='deleteRetPerVol', action='store_true', - help='Delete PersistentVolumes with Retain reclaim policy (default; only for "wipe" operation with Kubernetes)', + help='Delete PersistentVolumes with Retain reclaim policy (default; only for "stop" operation with Kubernetes)', ) parser.add_argument( '--no-reclaim-persistent-volume', dest='deleteRetPerVol', action='store_false', - help='Do not delete PersistentVolumes with Retain reclaim policy (only for "wipe" operation with Kubernetes)', + help='Do not delete PersistentVolumes with Retain reclaim policy (only for "stop" operation with Kubernetes)', ) parser.set_defaults(deleteRetPerVol=True) parser.add_argument( From 728b55dcde8d0a07725a488410941e799f8453c7 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 08:06:51 -0600 Subject: [PATCH 163/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/install.py | 6 +++--- scripts/malcolm_kubernetes.py | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/scripts/install.py b/scripts/install.py index 8b333f7f9..895dbb27c 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -791,7 +791,7 @@ def tweak_malcolm_runtime( 'Download updated Suricata signatures periodically?', default=False ) autoZeek = InstallerYesOrNo('Automatically analyze all PCAP files with Zeek?', default=True) - zeekICSBestGuess = (autoZeek or liveZeek) and InstallerYesOrNo( + zeekICSBestGuess = autoZeek and InstallerYesOrNo( 'Should Malcolm use "best guess" to identify potential OT/ICS traffic with Zeek?', default=False ) reverseDns = InstallerYesOrNo( @@ -2053,7 +2053,7 @@ def install_docker(self): else: eprint(f"Installation of docker failed: {out}") else: - eprint(f"Downloading {dockerComposeUrl} to {tempFileName} failed") + eprint(f"Downloading https://get.docker.com/ to {tempFileName} failed") if result and ((self.distro == PLATFORM_LINUX_FEDORA) or (self.distro == PLATFORM_LINUX_CENTOS)): # centos/fedora don't automatically start/enable the daemon, so do so now @@ -2746,7 +2746,7 @@ def main(): installer = MacInstaller(orchMode, debug=args.debug, configOnly=args.configOnly) elif installerPlatform == PLATFORM_WINDOWS: raise Exception(f'{ScriptName} is not yet supported on {installerPlatform}') - installer = WindowsInstaller(orchMode, debug=args.debug, configOnly=args.configOnly) + # installer = WindowsInstaller(orchMode, debug=args.debug, configOnly=args.configOnly) success = False installPath = None diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index 2671f506b..346940e86 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -17,10 +17,7 @@ MalcolmPath, ) from malcolm_utils import ( - deep_get, - eprint, file_contents, - get_iterable, remove_suffix, tablify, LoadStrIfJson, From a79483b79aa04880c5fb074012c1f9f7062c3791 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 08:49:56 -0600 Subject: [PATCH 164/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/control.py | 60 ++++++++++++++++++++++++++++------- scripts/malcolm_kubernetes.py | 43 +++++++++++++++++++++++++ scripts/malcolm_utils.py | 18 ++++++++++- 3 files changed, 108 insertions(+), 13 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index fd2fe0057..1f55f90af 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -54,6 +54,7 @@ EscapeForCurl, EscapeAnsi, get_iterable, + get_primary_ip, LoadStrIfJson, ParseCurlFile, RemoveEmptyFolders, @@ -69,6 +70,7 @@ PrintNodeStatus, DeleteNamespace, StartMalcolm, + get_node_hostnames_and_ips, ) from base64 import b64encode @@ -383,6 +385,36 @@ def status(): raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') +################################################################################################### +def printURLs(): + global orchMode + + if orchMode is OrchestrationFramework.KUBERNETES: + addrs = get_node_hostnames_and_ips(mastersOnly=True) + if not any((addrs['external'], addrs['hostname'])): + addrs = get_node_hostnames_and_ips(mastersOnly=False) + if addrs['external']: + myIp = addrs['external'][0] + elif addrs['hostname']: + myIp = addrs['hostname'][0] + elif addrs['internal']: + myIp = addrs['internal'][0] + else: + myIp = '' + else: + myIp = get_primary_ip() + + print("\nMalcolm services can be accessed via the following URLs:") + print("------------------------------------------------------------------------------") + print(f" - Arkime: https://{myIp}/") + print(f" - OpenSearch Dashboards: https://{myIp}/dashboards/") + print(f" - PCAP upload (web): https://{myIp}/upload/") + print(f" - PCAP upload (sftp): sftp://username@{myIp}:8022/files/") + print(f" - NetBox: https://{myIp}/netbox/") + print(f" - Account management: https://{myIp}/auth/") + print(f" - Documentation: https://{myIp}/readme/") + + ################################################################################################### def netboxBackup(backupFileName=None): global args @@ -608,18 +640,9 @@ def logs(): process.wait(timeout=5.0) except TimeoutExpired: process.kill() - # # TODO: Replace 'localhost' with an outwards-facing IP since I doubt anybody is - # accessing these from the Malcolm server. - print("\nStarted Malcolm\n\n") - print("Malcolm services can be accessed via the following URLs:") - print("------------------------------------------------------------------------------") - print(" - Arkime: https://localhost/") - print(" - OpenSearch Dashboards: https://localhost/dashboards/") - print(" - PCAP upload (web): https://localhost/upload/") - print(" - PCAP upload (sftp): sftp://username@127.0.0.1:8022/files/") - print(" - NetBox: https://localhost/netbox/\n") - print(" - Account management: https://localhost/auth/\n") - print(" - Documentation: https://localhost/readme/\n") + + print("\nStarted Malcolm\n") + printURLs() process.poll() @@ -1718,6 +1741,15 @@ def main(): default=False, help="Display status of Malcolm components", ) + parser.add_argument( + '--urls', + dest='cmdPrintURLs', + type=str2bool, + nargs='?', + const=True, + default=False, + help="Display Malcolm URLs", + ) try: parser.error = parser.exit @@ -1878,6 +1910,10 @@ def main(): if args.cmdStatus: status() + # display Malcolm URLS + if args.cmdPrintURLs: + printURLs() + # backup NetBox files if args.netboxBackupFile is not None: print(f"NetBox configuration database saved to {netboxBackup(args.netboxBackupFile)}") diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index 346940e86..14f45ea62 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -17,10 +17,13 @@ MalcolmPath, ) from malcolm_utils import ( + deep_get, + eprint, file_contents, remove_suffix, tablify, LoadStrIfJson, + val2bool, ) @@ -235,6 +238,46 @@ def pod_stats(node, namespace): return pod_dict +def get_node_hostnames_and_ips(mastersOnly=False): + result = {} + result['hostname'] = list() + result['external'] = list() + result['internal'] = list() + + if ( + (kubeImported := KubernetesDynamic()) + and (k8s_api := kubeImported.client.CoreV1Api()) + and ( + node_stats := kubeImported.client.CustomObjectsApi().list_cluster_custom_object( + "metrics.k8s.io", "v1beta1", "nodes" + ) + ) + ): + for stat in node_stats['items']: + if (not mastersOnly) or any( + [ + val2bool(deep_get(stat, ['metadata', 'labels', l], default=False)) + for l in ('node-role.kubernetes.io/control-plane', 'node-role.kubernetes.io/master') + ] + ): + api_response = k8s_api.read_node_status(stat['metadata']['name']) + result['hostname'].extend( + (list(set([x.address for x in api_response.status.addresses if not x.type.endswith('IP')]))) + ) + result['external'].extend( + (list(set([x.address for x in api_response.status.addresses if x.type.endswith('ExternalIP')]))) + ) + result['internal'].extend( + (list(set([x.address for x in api_response.status.addresses if x.type.endswith('InternalIP')]))) + ) + + result['hostname'] = list(set(result['hostname'])) + result['external'] = list(set(result['external'])) + result['internal'] = list(set(result['internal'])) + + return result + + def PrintNodeStatus(): node_list = load_node_list() with ThreadPoolExecutor() as executor: diff --git a/scripts/malcolm_utils.py b/scripts/malcolm_utils.py index 0be5f8e18..c5d6fee49 100644 --- a/scripts/malcolm_utils.py +++ b/scripts/malcolm_utils.py @@ -282,6 +282,22 @@ def isipaddress(value): return result +################################################################################################### +# return the primary IP (the one with a default route) on the local box +def get_primary_ip(): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.settimeout(0) + try: + # this IP doesn't have to be reachable + s.connect(('10.254.254.254', 1)) + ip = s.getsockname()[0] + except Exception: + ip = '127.0.0.1' + finally: + s.close() + return ip + + ################################################################################################### # attempt to decode a string as JSON, returning the object if it decodes and None otherwise def LoadStrIfJson(jsonStr): @@ -469,7 +485,7 @@ def touch(filename): ################################################################################################### -# read the contents of a text file +# read the contents of a file, first assuming text (with encoding), optionally falling back to binary def file_contents(filename, encoding='utf-8', binary_fallback=False): if os.path.isfile(filename): decodeErr = False From 4a315afb2d66219eb31dc96bc76729b4bae0654d Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 09:09:21 -0600 Subject: [PATCH 165/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- .../config/hooks/normal/0169-pip-installs.hook.chroot | 5 ++++- malcolm-iso/config/package-lists/python.list.chroot | 2 -- sensor-iso/config/hooks/normal/0169-pip-installs.hook.chroot | 2 ++ sensor-iso/config/package-lists/python.list.chroot | 1 - sensor-iso/interface/requirements.txt | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/malcolm-iso/config/hooks/normal/0169-pip-installs.hook.chroot b/malcolm-iso/config/hooks/normal/0169-pip-installs.hook.chroot index 48608cb18..605e90427 100755 --- a/malcolm-iso/config/hooks/normal/0169-pip-installs.hook.chroot +++ b/malcolm-iso/config/hooks/normal/0169-pip-installs.hook.chroot @@ -5,4 +5,7 @@ export LANG=C.UTF-8 # python 3 pip3 install --no-compile --no-cache-dir --force-reinstall --upgrade \ - debinterface \ No newline at end of file + debinterface \ + kubernetes \ + python-dotenv \ + ruamel.yaml \ No newline at end of file diff --git a/malcolm-iso/config/package-lists/python.list.chroot b/malcolm-iso/config/package-lists/python.list.chroot index 253fdc03f..02ceb6d7a 100644 --- a/malcolm-iso/config/package-lists/python.list.chroot +++ b/malcolm-iso/config/package-lists/python.list.chroot @@ -2,11 +2,9 @@ python3 python3-pip python3-bs4 python3-colorama -python3-dotenv python3-netifaces python3-psutil python3-pycryptodome python3-dialog python3-requests -python3-ruamel.yaml python3-yaml \ No newline at end of file diff --git a/sensor-iso/config/hooks/normal/0169-pip-installs.hook.chroot b/sensor-iso/config/hooks/normal/0169-pip-installs.hook.chroot index a02ef3d16..952ce3fc4 100755 --- a/sensor-iso/config/hooks/normal/0169-pip-installs.hook.chroot +++ b/sensor-iso/config/hooks/normal/0169-pip-installs.hook.chroot @@ -11,6 +11,8 @@ pip3 install --no-compile --no-cache-dir --force-reinstall --upgrade \ dateparser \ debinterface \ pymisp \ + python-dotenv \ + ruamel.yaml \ stix2 \ taxii2-client \ watchdog diff --git a/sensor-iso/config/package-lists/python.list.chroot b/sensor-iso/config/package-lists/python.list.chroot index 88abf3f76..898f3c9dc 100644 --- a/sensor-iso/config/package-lists/python.list.chroot +++ b/sensor-iso/config/package-lists/python.list.chroot @@ -10,7 +10,6 @@ python3-pip python3-psutil python3-pycryptodome python3-requests -python3-ruamel.yaml python3-semantic-version python3-setuptools python3-tz diff --git a/sensor-iso/interface/requirements.txt b/sensor-iso/interface/requirements.txt index ab47c4010..fd639a82d 100644 --- a/sensor-iso/interface/requirements.txt +++ b/sensor-iso/interface/requirements.txt @@ -9,7 +9,7 @@ itsdangerous==2.1.2 Jinja2==3.1.2 MarkupSafe==2.1.2 psutil==5.9.4 -python-dotenv==0.21.1 +python-dotenv==1.0.0 requests==2.28.2 six==1.16.0 urllib3==1.26.14 From 1a5af9e48b6b93bb2c40977b71437fea1348e3d3 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 11:01:01 -0600 Subject: [PATCH 166/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/control.py | 38 ++++++++---- scripts/malcolm_common.py | 29 +++++---- scripts/malcolm_kubernetes.py | 109 +++++++++++++++++++++++++++++++++- 3 files changed, 150 insertions(+), 26 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index 1f55f90af..865f92447 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -31,7 +31,7 @@ DisplayMessage, DisplayProgramBox, DotEnvDynamic, - GetUidGidFromComposeFile, + GetUidGidFromEnv, KubernetesDynamic, LocalPathForContainerBindMount, MainDialog, @@ -71,6 +71,8 @@ DeleteNamespace, StartMalcolm, get_node_hostnames_and_ips, + PodExec, + GetPodNamesForService, ) from base64 import b64encode @@ -169,10 +171,11 @@ def keystore_op(service, dropPriv=False, *keystore_args, **run_process_kwargs): err = -1 results = [] - if orchMode is OrchestrationFramework.DOCKER_COMPOSE: - # the opensearch containers all follow the same naming pattern for these executables - keystoreBinProc = f"/usr/share/{service}/bin/{service}-keystore" + # the opensearch containers all follow the same naming pattern for these executables + keystoreBinProc = f"/usr/share/{service}/bin/{service}-keystore" + uidGidDict = GetUidGidFromEnv(args.configDir) + if orchMode is OrchestrationFramework.DOCKER_COMPOSE: # if we're using docker-uid-gid-setup.sh to drop privileges as we spin up a container dockerUidGuidSetup = "/usr/local/bin/docker-uid-gid-setup.sh" @@ -190,11 +193,8 @@ def keystore_op(service, dropPriv=False, *keystore_args, **run_process_kwargs): localKeystorePreExists = False volumeKeystore = None volumeKeystoreDir = None - uidGidDict = None try: - uidGidDict = GetUidGidFromComposeFile(args.composeFile) - composeFileLines = list() with open(args.composeFile, 'r') as f: allLines = f.readlines() @@ -341,10 +341,28 @@ def keystore_op(service, dropPriv=False, *keystore_args, **run_process_kwargs): else: eprint(e) + elif orchMode is OrchestrationFramework.KUBERNETES: + cmd = [keystoreBinProc] + if keystore_args: + cmd.extend(list(keystore_args)) + cmd = [x for x in cmd if x] + + err, results = PodExec( + service, + args.namespace, + [x for x in cmd if x], + stdin=run_process_kwargs['stdin'] + if ('stdin' in run_process_kwargs and run_process_kwargs['stdin']) + else None, + ) + + if args.debug: + dbgStr = f"{cmd}({run_process_kwargs['stdin'][:80] + bool(run_process_kwargs['stdin'][80:]) * '...' if 'stdin' in run_process_kwargs and run_process_kwargs['stdin'] else ''}) returned {err}: {results}" + eprint(dbgStr) + else: raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') - # success = (error == 0) return (err == 0), results @@ -428,7 +446,7 @@ def netboxBackup(backupFileName=None): osEnv = os.environ.copy() osEnv['TMPDIR'] = MalcolmTmpPath - uidGidDict = GetUidGidFromComposeFile(args.composeFile) + uidGidDict = GetUidGidFromEnv(args.configDir) dockerCmd = [ dockerComposeBin, @@ -481,7 +499,7 @@ def netboxRestore(backupFileName=None): osEnv = os.environ.copy() osEnv['TMPDIR'] = MalcolmTmpPath - uidGidDict = GetUidGidFromComposeFile(args.composeFile) + uidGidDict = GetUidGidFromEnv(args.configDir) dockerCmdBase = [ dockerComposeBin, diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index b1afc87b4..cc1913016 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -136,22 +136,21 @@ def LocalPathForContainerBindMount(service, dockerComposeContents, containerPath ################################################################################################## -def GetUidGidFromComposeFile(composeFile): +def GetUidGidFromEnv(configDir=None): + configDirToCheck = configDir if configDir and os.path.isdir(configDir) else os.path.join(MalcolmPath, 'config') uidGidDict = defaultdict(str) - pyPlatform = platform.system() - uidGidDict['PUID'] = f'{os.getuid()}' if (pyPlatform != PLATFORM_WINDOWS) else '1000' - uidGidDict['PGID'] = f'{os.getgid()}' if (pyPlatform != PLATFORM_WINDOWS) else '1000' - if os.path.isfile(composeFile): - with open(composeFile, 'r') as f: - composeFileLines = f.readlines() - uidGidDict.update( - dict( - x.split(':') - for x in [ - ''.join(x.split()) for x in composeFileLines if re.search(r'^\s*P[UG]ID\s*:\s*\d+\s*$', x) - ] - ) - ) + if dotEnvImported := DotEnvDynamic(): + pyPlatform = platform.system() + uidGidDict['PUID'] = f'{os.getuid()}' if (pyPlatform != PLATFORM_WINDOWS) else '1000' + uidGidDict['PGID'] = f'{os.getgid()}' if (pyPlatform != PLATFORM_WINDOWS) else '1000' + envFileName = os.path.join(configDirToCheck, 'process.env') + if os.path.isfile(envFileName): + envValues = dotEnvImported.dotenv_values(envFileName) + if 'PUID' in envValues: + uidGidDict['PUID'] = envValues['PUID'] + if 'PGID' in envValues: + uidGidDict['PGID'] = envValues['PGID'] + return uidGidDict diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index 14f45ea62..fd520ffd4 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -10,15 +10,19 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from collections import defaultdict from itertools import chain +from io import StringIO from malcolm_common import ( - KubernetesDynamic, DotEnvDynamic, + KubernetesDynamic, MalcolmPath, + YAMLDynamic, ) from malcolm_utils import ( deep_get, + dictsearch, eprint, + get_iterable, file_contents, remove_suffix, tablify, @@ -278,6 +282,109 @@ def get_node_hostnames_and_ips(mastersOnly=False): return result +def GetPodNamesForService(service, namespace): + podsNames = [] + + if namespace and (kubeImported := KubernetesDynamic()) and (client := kubeImported.client.CoreV1Api()): + podsNames = [ + x.metadata.name + for x in client.list_namespaced_pod( + namespace, + watch=False, + label_selector=f'name={service}-deployment', + ).items + ] + + return podsNames + + +def PodExec( + service, + namespace, + command, + stdout=True, + stderr=True, + stdin=None, + timeout=60, +): + retcode = -1 + output = [] + + if namespace and (kubeImported := KubernetesDynamic()) and (client := kubeImported.client.CoreV1Api()): + podsNames = GetPodNamesForService(service, namespace) + + for podName in podsNames: + resp = None + try: + while True: + resp = client.read_namespaced_pod( + name=podName, + namespace=namespace, + ) + if resp.status.phase != 'Pending': + break + + resp = kubeImported.stream.stream( + client.connect_get_namespaced_pod_exec, + podName, + namespace, + command=get_iterable(command), + stdout=stdout, + stderr=stderr, + stdin=stdin is not None, + tty=False, + _preload_content=False, + ) + rawOutput = StringIO('') + rawErrput = StringIO('') + stdinRemaining = list(get_iterable(stdin)) if (stdin is not None) else [] + counter = 0 + while resp.is_open() and (counter <= timeout): + resp.update(timeout=1) + counter += 1 + if stdout and resp.peek_stdout(): + rawOutput.write(resp.read_stdout()) + if stderr and resp.peek_stderr(): + rawErrput.write(resp.read_stderr()) + if stdinRemaining: + resp.write_stdin(stdinRemaining.pop(0) + "\n") + if stdout and resp.peek_stdout(): + rawOutput.write(resp.read_stdout()) + if stderr and resp.peek_stderr(): + rawErrput.write(resp.read_stderr()) + output.extend(rawOutput.getvalue().split('\n')) + output.extend(rawErrput.getvalue().split('\n')) + + err = None + if yamlImported := YAMLDynamic(): + err = yamlImported.safe_load(resp.read_channel(kubeImported.stream.ws_client.ERROR_CHANNEL)) + + if not err: + err = {} + err['status'] = 'Success' + + if deep_get(err, ['status'], None) == 'Success': + retcode = 0 + elif deep_get(err, ['reason'], None) == 'NonZeroExitCode': + retcodes = [ + int(deep_get(x, ['message'], 1)) + for x in deep_get(err, ['details', 'causes'], [{'reason': 'ExitCode', 'message': '1'}]) + if (deep_get(x, ['reason'], None) == 'ExitCode') + ] + retcode = retcodes[0] if len(retcodes) > 0 else 1 + else: + # can't parse, but it's a failure + retcode = 1 + + resp.close() + + except kubeImported.client.rest.ApiException as x: + if x.status != 404: + raise + + return retcode, output + + def PrintNodeStatus(): node_list = load_node_list() with ThreadPoolExecutor() as executor: From 6dc8da9e85fa46c275df021544bb0b3f872b37b5 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 11:17:20 -0600 Subject: [PATCH 167/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/control.py | 12 ++++++++++-- scripts/malcolm_kubernetes.py | 16 +++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index 865f92447..4b12c9c10 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -49,6 +49,7 @@ ) from malcolm_utils import ( + deep_get, dictsearch, eprint, EscapeForCurl, @@ -79,6 +80,7 @@ from collections import defaultdict, namedtuple from subprocess import PIPE, STDOUT, DEVNULL, Popen, TimeoutExpired from urllib.parse import urlparse +from itertools import chain try: from contextlib import nullcontext @@ -347,7 +349,7 @@ def keystore_op(service, dropPriv=False, *keystore_args, **run_process_kwargs): cmd.extend(list(keystore_args)) cmd = [x for x in cmd if x] - err, results = PodExec( + podsResults = PodExec( service, args.namespace, [x for x in cmd if x], @@ -356,9 +358,15 @@ def keystore_op(service, dropPriv=False, *keystore_args, **run_process_kwargs): else None, ) + err = 0 if all([deep_get(v, ['err'], 1) == 0 for k, v in podsResults.items()]) else 1 + results = list(chain(*[deep_get(v, ['output'], '') for k, v in podsResults.items()])) + if args.debug: - dbgStr = f"{cmd}({run_process_kwargs['stdin'][:80] + bool(run_process_kwargs['stdin'][80:]) * '...' if 'stdin' in run_process_kwargs and run_process_kwargs['stdin'] else ''}) returned {err}: {results}" + dbgStr = f"{len(podsResults)} pods: {cmd}({run_process_kwargs['stdin'][:80] + bool(run_process_kwargs['stdin'][80:]) * '...' if 'stdin' in run_process_kwargs and run_process_kwargs['stdin'] else ''}) returned {err}: {results}" eprint(dbgStr) + for podname, podResults in podsResults.items(): + dbgStr = f"{podname}: {cmd}({run_process_kwargs['stdin'][:80] + bool(run_process_kwargs['stdin'][80:]) * '...' if 'stdin' in run_process_kwargs and run_process_kwargs['stdin'] else ''}) returned {deep_get(podResults, ['err'], 1)}: {deep_get(podResults, ['output'], 'unknown')}" + eprint(dbgStr) else: raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index fd520ffd4..ab3fbb4d4 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -307,14 +307,14 @@ def PodExec( stdin=None, timeout=60, ): - retcode = -1 - output = [] + results = {} if namespace and (kubeImported := KubernetesDynamic()) and (client := kubeImported.client.CoreV1Api()): podsNames = GetPodNamesForService(service, namespace) for podName in podsNames: - resp = None + retcode = -1 + output = [] try: while True: resp = client.read_namespaced_pod( @@ -380,9 +380,15 @@ def PodExec( except kubeImported.client.rest.ApiException as x: if x.status != 404: - raise + if retcode == 0: + retcode = 1 + output.extend(str(x)) - return retcode, output + results[podName] = {} + results[podName]['err'] = retcode + results[podName]['output'] = output + + return results def PrintNodeStatus(): From d62fbaa68ab182042b422855b2393a50da4baa88 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 12:00:54 -0600 Subject: [PATCH 168/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/control.py | 117 ++++++++++++++++++++++++++++++++-- scripts/malcolm_kubernetes.py | 3 +- 2 files changed, 115 insertions(+), 5 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index 4b12c9c10..fde3fcae0 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -449,13 +449,13 @@ def netboxBackup(backupFileName=None): backupFileName, backupMediaFileName = None, None + uidGidDict = GetUidGidFromEnv(args.configDir) + if orchMode is OrchestrationFramework.DOCKER_COMPOSE: # docker-compose use local temporary path osEnv = os.environ.copy() osEnv['TMPDIR'] = MalcolmTmpPath - uidGidDict = GetUidGidFromEnv(args.configDir) - dockerCmd = [ dockerComposeBin, '-f', @@ -489,6 +489,38 @@ def netboxBackup(backupFileName=None): with tarfile.open(backupMediaFileName, 'w:gz') as t: t.add(os.path.join(os.path.join(MalcolmPath, 'netbox'), 'media'), arcname='.') + elif orchMode is OrchestrationFramework.KUBERNETES: + if podsResults := PodExec( + service='netbox-postgres', + namespace=args.namespace, + command=[ + 'pg_dump', + '-U', + 'netbox', + '-d', + 'netbox', + ], + maxPodsToExec=1, + ): + podName = next(iter(podsResults)) + err = podsResults[podName]['err'] + results = podsResults[podName]['output'] + else: + err = 1 + results = [] + + if (err != 0) or (len(results) == 0): + raise Exception('Error creating NetBox configuration database backup') + + if (backupFileName is None) or (len(backupFileName) == 0): + backupFileName = f"malcolm_netbox_backup_{time.strftime('%Y%m%d-%H%M%S')}.gz" + + with gzip.GzipFile(backupFileName, "wb") as f: + f.write(bytes('\n'.join(results), 'utf-8')) + + # TODO: can't backup netbox/media directory via kubernetes at the moment + backupMediaFileName = None + else: raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') @@ -502,13 +534,13 @@ def netboxRestore(backupFileName=None): global orchMode if backupFileName and os.path.isfile(backupFileName): + uidGidDict = GetUidGidFromEnv(args.configDir) + if orchMode is OrchestrationFramework.DOCKER_COMPOSE: # docker-compose use local temporary path osEnv = os.environ.copy() osEnv['TMPDIR'] = MalcolmTmpPath - uidGidDict = GetUidGidFromEnv(args.configDir) - dockerCmdBase = [ dockerComposeBin, '-f', @@ -566,6 +598,82 @@ def netboxRestore(backupFileName=None): with tarfile.open(backupMediaFileName) as t: t.extractall(mediaPath) + elif orchMode is OrchestrationFramework.KUBERNETES: + # if the netbox_init.py process is happening, interrupt it + if podsResults := PodExec( + service='netbox', + namespace=args.namespace, + command=['bash', '-c', 'pgrep -f /usr/local/bin/netbox_init.py | xargs -r kill'], + ): + err = 0 if all([deep_get(v, ['err'], 1) == 0 for k, v in podsResults.items()]) else 1 + results = list(chain(*[deep_get(v, ['output'], '') for k, v in podsResults.items()])) + else: + err = 1 + results = [] + if (err != 0) and args.debug: + eprint(f'Error ({err}) interrupting netbox_init.py: {results}') + + # drop the existing netbox database + if podsResults := PodExec( + service='netbox-postgres', + namespace=args.namespace, + command=['dropdb', '-U', 'netbox', 'netbox', '--force'], + ): + err = 0 if all([deep_get(v, ['err'], 1) == 0 for k, v in podsResults.items()]) else 1 + results = list(chain(*[deep_get(v, ['output'], '') for k, v in podsResults.items()])) + else: + err = 1 + results = [] + if ((err != 0) or (len(results) == 0)) and args.debug: + eprint(f'Error dropping NetBox database: {results}') + + # create a new netbox database + if podsResults := PodExec( + service='netbox-postgres', + namespace=args.namespace, + command=['createdb', '-U', 'netbox', 'netbox'], + ): + err = 0 if all([deep_get(v, ['err'], 1) == 0 for k, v in podsResults.items()]) else 1 + results = list(chain(*[deep_get(v, ['output'], '') for k, v in podsResults.items()])) + else: + err = 1 + results = [] + if err != 0: + raise Exception('Error creating new NetBox database') + + # load the backed-up psql dump + with gzip.open(backupFileName, 'rt') as f: + if podsResults := PodExec( + service='netbox-postgres', + namespace=args.namespace, + command=['psql', '-U', 'netbox'], + stdin=f.read(), + ): + err = 0 if all([deep_get(v, ['err'], 1) == 0 for k, v in podsResults.items()]) else 1 + results = list(chain(*[deep_get(v, ['output'], '') for k, v in podsResults.items()])) + else: + err = 1 + results = [] + if (err != 0) or (len(results) == 0): + raise Exception('Error loading NetBox database') + + # migrations if needed + if podsResults := PodExec( + service='netbox', + namespace=args.namespace, + command=['/opt/netbox/netbox/manage.py', 'migrate'], + ): + eprint(podsResults) + err = 0 if all([deep_get(v, ['err'], 1) == 0 for k, v in podsResults.items()]) else 1 + results = list(chain(*[deep_get(v, ['output'], '') for k, v in podsResults.items()])) + else: + err = 1 + results = [] + if (err != 0) or (len(results) == 0): + raise Exception('Error performing NetBox migration') + + # TODO: can't restore netbox/media directory via kubernetes at the moment + else: raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') @@ -636,6 +744,7 @@ def logs(): else: cmd = [] + raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') if cmd: process = Popen( diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index ab3fbb4d4..b11b03637 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -306,13 +306,14 @@ def PodExec( stderr=True, stdin=None, timeout=60, + maxPodsToExec=1, ): results = {} if namespace and (kubeImported := KubernetesDynamic()) and (client := kubeImported.client.CoreV1Api()): podsNames = GetPodNamesForService(service, namespace) - for podName in podsNames: + for podName in podsNames[:maxPodsToExec]: retcode = -1 output = [] try: From c633cd5c2329521f44d7dd3c33a8db2d5c628f51 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 12:16:38 -0600 Subject: [PATCH 169/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/control.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index fde3fcae0..e31b12da3 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -755,15 +755,15 @@ def logs(): ) while not shuttingDown[0]: output = process.stdout.readline() - if (len(output) == 0) and (process.poll() is not None): - break + if not output: + if process.poll() is not None: + break + else: + time.sleep(0.5) - if output := ProcessLogLine(output, debug=args.debug): + elif output := ProcessLogLine(output, debug=args.debug): print(output) - else: - time.sleep(0.5) - if ( output and (args.cmdStart or args.cmdRestart) From 2a935d410d418c2ba6e7669f24ab6f14921c2f8f Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 12:18:13 -0600 Subject: [PATCH 170/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/malcolm_common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index cc1913016..69dd8cdf7 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -682,6 +682,7 @@ def DownloadToFile(url, local_filename, debug=False): | loaded\s+config\s+'/etc/netbox/config/ | "netbox"\s+application\s+started | \[notice\].+app\s+process\s+\d+\s+exited\s+with\s+code\s+0\b + | kube-probe/ | POST\s+/(arkime_\w+)(/\w+)?/_(d?stat|doc|search).+HTTP/[\d\.].+\b20[01]\b | POST\s+/_bulk\s+HTTP/[\d\.].+\b20[01]\b | POST\s+/server/php/\s+HTTP/\d+\.\d+"\s+\d+\s+\d+.*:8443/ From d964143b9ae49749ae3fa8ae03ab404ebee6aaa3 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 12:50:33 -0600 Subject: [PATCH 171/239] work in progress on script consolidation for kubernetes, idaholab/Malcolm#172 --- scripts/control.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/control.py b/scripts/control.py index e31b12da3..bf75974f7 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -435,7 +435,8 @@ def printURLs(): print(f" - Arkime: https://{myIp}/") print(f" - OpenSearch Dashboards: https://{myIp}/dashboards/") print(f" - PCAP upload (web): https://{myIp}/upload/") - print(f" - PCAP upload (sftp): sftp://username@{myIp}:8022/files/") + if orchMode is not OrchestrationFramework.KUBERNETES: + print(f" - PCAP upload (sftp): sftp://username@{myIp}:8022/files/") print(f" - NetBox: https://{myIp}/netbox/") print(f" - Account management: https://{myIp}/auth/") print(f" - Documentation: https://{myIp}/readme/") From a56e3c9712653a3e7931ceab3eb413b383b2db05 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 13:30:52 -0600 Subject: [PATCH 172/239] added Vagrantfile for testing Kubernetes in Malcolm --- .dockerignore | 1 + kubernetes/vagrant/.gitignore | 2 + kubernetes/vagrant/Vagrantfile | 198 +++++++++++++++++++++++++++ kubernetes/vagrant/shared/.gitignore | 3 + kubernetes/vagrant/ssh_config | 3 + 5 files changed, 207 insertions(+) create mode 100644 kubernetes/vagrant/.gitignore create mode 100644 kubernetes/vagrant/Vagrantfile create mode 100644 kubernetes/vagrant/shared/.gitignore create mode 100644 kubernetes/vagrant/ssh_config diff --git a/.dockerignore b/.dockerignore index d485d44e3..cb9ee6224 100644 --- a/.dockerignore +++ b/.dockerignore @@ -25,6 +25,7 @@ opensearch opensearch-backup arkime-logs arkime-raw +kubernetes malcolm-iso sensor-iso nginx/nginx_ldap*.conf diff --git a/kubernetes/vagrant/.gitignore b/kubernetes/vagrant/.gitignore new file mode 100644 index 000000000..86a37fb65 --- /dev/null +++ b/kubernetes/vagrant/.gitignore @@ -0,0 +1,2 @@ +.vagrant + diff --git a/kubernetes/vagrant/Vagrantfile b/kubernetes/vagrant/Vagrantfile new file mode 100644 index 000000000..822075113 --- /dev/null +++ b/kubernetes/vagrant/Vagrantfile @@ -0,0 +1,198 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# +# This Vagrantfile facilitates testing Malcolm's kubernetes deployment +# by providing a simple virtualized 3 node k3s cluster. +# Not for production use. +# + +unless Vagrant.has_plugin?("vagrant-reload") + raise 'vagrant-reload plugin is not installed!' +end + +server_ip = "192.168.56.10" +server_hostname = "server.k3s.internal" + +agents = { "agent1" => "192.168.56.11", + "agent2" => "192.168.56.12" } + +common_script_0 = <<-SHELL + sudo -i + apt-get -qqy update + apt-get -y install --no-install-recommends \ + apt-transport-https \ + bat \ + ca-certificates \ + curl \ + fd-find \ + git \ + gnupg2 \ + iptables \ + jq \ + moreutils \ + ripgrep \ + software-properties-common + cat >> /etc/security/limits.d/limits.conf <> /etc/sysctl.conf <> /etc/hosts + export INSTALL_K3S_EXEC="server --disable traefik --bind-address=#{server_ip} --node-external-ip=#{server_ip} --flannel-iface=eth1" + curl -sfL https://get.k3s.io | sh - + echo "Waiting for k3s to start..." + sleep 30 + curl -sSL -o /tmp/deploy_nginx.yaml https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.6.4/deploy/static/provider/cloud/deploy.yaml + sed -i "/\\/nginx-ingress-controller$/a \\ \\ \\ \\ \\ \\ \\ \\ - --enable-ssl-passthrough" /tmp/deploy_nginx.yaml + kubectl --kubeconfig /etc/rancher/k3s/k3s.yaml apply -f /tmp/deploy_nginx.yaml + until [ -f /var/lib/rancher/k3s/server/token ] && [ -f /etc/rancher/k3s/k3s.yaml ]; do sleep 5; done + cp -v /var/lib/rancher/k3s/server/token /vagrant_shared + cp -v /etc/rancher/k3s/k3s.yaml /vagrant_shared + SHELL + +agent_script_1 = <<-SHELL + sudo -i + echo "#{server_ip} #{server_hostname}" >> /etc/hosts + export K3S_TOKEN_FILE=/vagrant_shared/token + export K3S_URL=https://#{server_ip}:6443 + export INSTALL_K3S_EXEC="--flannel-iface=eth1" + echo "Waiting for k3s server..." + until [ -f /vagrant_shared/token ]; do sleep 5; done + curl -sfL https://get.k3s.io | sh - + SHELL + +Vagrant.configure("2") do |config| + config.vm.box = "bento/debian-11" + config.ssh.config = "ssh_config" + + config.vm.define "server", primary: true do |server| + server.vm.network "private_network", ip: server_ip + server.vm.network :forwarded_port, guest: 6443, host: 6443 + for p in 30000..30100 + server.vm.network :forwarded_port, guest: p, host: p, protocol: "tcp" + end + server.vm.synced_folder '.', '/vagrant', disabled: true + server.vm.synced_folder "./shared", "/vagrant_shared", type: "sshfs", disabled: false + server.vm.hostname = "server" + + server.vm.provider :libvirt do |vm| + vm.cpus = 2 + vm.memory = 4096 + end + server.vm.provider :virtualbox do |vm| + vm.cpus = 2 + vm.memory = 4096 + end + server.vm.provider :vmware_desktop do |vm| + vm.cpus = 2 + vm.memory = 4096 + end + server.vm.provider :vmware_fusion do |vm| + vm.cpus = 2 + vm.memory = 4096 + end + + server.vm.provision "shell", inline: server_script_0 + server.vm.provision "shell", inline: common_script_0 + server.vm.provision :reload + server.vm.provision "shell", inline: server_script_1 + end + + agents.each do |agent_name, agent_ip| + config.vm.define agent_name do |agent| + agent.vm.network "private_network", ip: agent_ip + agent.vm.synced_folder '.', '/vagrant', disabled: true + agent.vm.synced_folder "./shared", "/vagrant_shared", type: "sshfs", disabled: false + agent.vm.hostname = agent_name + + agent.vm.provider :libvirt do |vm| + vm.cpus = 4 + vm.memory = 12288 + end + agent.vm.provider :virtualbox do |vm| + vm.cpus = 4 + vm.memory = 12288 + end + agent.vm.provider :vmware_desktop do |vm| + vm.cpus = 4 + vm.memory = 12288 + end + agent.vm.provider :vmware_fusion do |vm| + vm.cpus = 4 + vm.memory = 12288 + end + + agent.vm.provision "shell", inline: common_script_0 + agent.vm.provision :reload + agent.vm.provision "shell", inline: agent_script_1 + end + end +end + diff --git a/kubernetes/vagrant/shared/.gitignore b/kubernetes/vagrant/shared/.gitignore new file mode 100644 index 000000000..a5baada18 --- /dev/null +++ b/kubernetes/vagrant/shared/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore + diff --git a/kubernetes/vagrant/ssh_config b/kubernetes/vagrant/ssh_config new file mode 100644 index 000000000..10ff23648 --- /dev/null +++ b/kubernetes/vagrant/ssh_config @@ -0,0 +1,3 @@ +Host * + IdentitiesOnly yes + GSSAPIAuthentication no From 5ed77f9dddcefad009fe4663a77a2b0b63e7bf58 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 16:25:38 -0600 Subject: [PATCH 173/239] expose other ports for malcolm --- docker-compose-standalone.yml | 1 - docker-compose.yml | 1 - kubernetes/00-ingress.yml | 14 ++++++++++++-- kubernetes/vagrant/Vagrantfile | 12 ++++++++---- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 17f8cd9ad..4d4763d26 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -632,7 +632,6 @@ services: - upload ports: - "0.0.0.0:443:443" - - "127.0.0.1:5601:5601" - "127.0.0.1:9200:9200" volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro diff --git a/docker-compose.yml b/docker-compose.yml index 67db5a300..6135775f9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -708,7 +708,6 @@ services: - upload ports: - "0.0.0.0:443:443" - - "127.0.0.1:5601:5601" - "127.0.0.1:9200:9200" volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro diff --git a/kubernetes/00-ingress.yml b/kubernetes/00-ingress.yml index 4fca03a62..e6578924a 100644 --- a/kubernetes/00-ingress.yml +++ b/kubernetes/00-ingress.yml @@ -1,4 +1,3 @@ ---- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -25,4 +24,15 @@ spec: service: name: nginx-proxy port: - number: 443 \ No newline at end of file + number: 443 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: tcp-services + namespace: malcolm +data: + 5044: "malcolm/logstash:5044" + 5045: "malcolm/filebeat:5045" + 8022: "malcolm/upload:22" + 9200: "malcolm/nginx-proxy:9200" \ No newline at end of file diff --git a/kubernetes/vagrant/Vagrantfile b/kubernetes/vagrant/Vagrantfile index 822075113..1e8216570 100644 --- a/kubernetes/vagrant/Vagrantfile +++ b/kubernetes/vagrant/Vagrantfile @@ -13,6 +13,7 @@ end server_ip = "192.168.56.10" server_hostname = "server.k3s.internal" +load_balancer_additional_ports = "{\\\"name\\\": \\\"proxied-tcp-5044\\\", \\\"port\\\": 5044, \\\"targetPort\\\": 5044, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"name\\\": \\\"proxied-tcp-5045\\\", \\\"port\\\": 5045, \\\"targetPort\\\": 5045, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"name\\\": \\\"proxied-tcp-8022\\\", \\\"port\\\": 8022, \\\"targetPort\\\": 8022, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"name\\\": \\\"proxied-tcp-9200\\\", \\\"port\\\": 9200, \\\"targetPort\\\": 9200, \\\"protocol\\\": \\\"TCP\\\"}" agents = { "agent1" => "192.168.56.11", "agent2" => "192.168.56.12" } @@ -33,7 +34,9 @@ common_script_0 = <<-SHELL moreutils \ ripgrep \ software-properties-common - cat >> /etc/security/limits.d/limits.conf <> /etc/security/limits.d/limits.conf <> /etc/sysctl.conf <> /etc/sysctl.conf < Date: Tue, 18 Apr 2023 19:54:58 -0600 Subject: [PATCH 174/239] expose other ports for malcolm --- kubernetes/vagrant/Vagrantfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kubernetes/vagrant/Vagrantfile b/kubernetes/vagrant/Vagrantfile index 1e8216570..aa6bf5e86 100644 --- a/kubernetes/vagrant/Vagrantfile +++ b/kubernetes/vagrant/Vagrantfile @@ -13,7 +13,8 @@ end server_ip = "192.168.56.10" server_hostname = "server.k3s.internal" -load_balancer_additional_ports = "{\\\"name\\\": \\\"proxied-tcp-5044\\\", \\\"port\\\": 5044, \\\"targetPort\\\": 5044, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"name\\\": \\\"proxied-tcp-5045\\\", \\\"port\\\": 5045, \\\"targetPort\\\": 5045, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"name\\\": \\\"proxied-tcp-8022\\\", \\\"port\\\": 8022, \\\"targetPort\\\": 8022, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"name\\\": \\\"proxied-tcp-9200\\\", \\\"port\\\": 9200, \\\"targetPort\\\": 9200, \\\"protocol\\\": \\\"TCP\\\"}" +load_balancer_additional_ports = "{\\\"appProtocol\\\": \\\"tcp\\\", \\\"name\\\": \\\"proxied-tcp-5044\\\", \\\"port\\\": 5044, \\\"targetPort\\\": 5044, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"appProtocol\\\": \\\"tcp\\\", \\\"name\\\": \\\"proxied-tcp-5045\\\", \\\"port\\\": 5045, \\\"targetPort\\\": 5045, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"appProtocol\\\": \\\"tcp\\\", \\\"name\\\": \\\"proxied-tcp-8022\\\", \\\"port\\\": 8022, \\\"targetPort\\\": 8022, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"appProtocol\\\": \\\"tcp\\\", \\\"name\\\": \\\"proxied-tcp-9200\\\", \\\"port\\\": 9200, \\\"targetPort\\\": 9200, \\\"protocol\\\": \\\"TCP\\\"}" +deployment_additional_ports = "{\\\"name\\\": \\\"lumberjack\\\", \\\"containerPort\\\": 5044, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"name\\\": \\\"tcpjson\\\", \\\"containerPort\\\": 5045, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"name\\\": \\\"sftp\\\", \\\"containerPort\\\": 8022, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"name\\\": \\\"opensearch\\\", \\\"containerPort\\\": 9200, \\\"protocol\\\": \\\"TCP\\\"}" agents = { "agent1" => "192.168.56.11", "agent2" => "192.168.56.12" } @@ -114,6 +115,7 @@ server_script_1 = <<-SHELL sleep 30 curl -sSL -o /tmp/deploy_nginx.yaml https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.0/deploy/static/provider/cloud/deploy.yaml yq -i '( select(.kind == "Deployment").spec.template.spec.containers[].args[] | select(contains("/nginx-ingress-controller")) | parent ) += ["--enable-ssl-passthrough", "--tcp-services-configmap=\$(POD_NAMESPACE)/tcp-services"]' /tmp/deploy_nginx.yaml + yq -i "( select(.kind == \\"Deployment\\").spec.template.spec.containers[].args[] | select(contains(\\"/nginx-ingress-controller\\")) | parent | parent | .ports ) += [#{deployment_additional_ports}]" /tmp/deploy_nginx.yaml yq -i "( select(.kind == \\"Service\\" and .spec.type == \\"LoadBalancer\\").spec.ports ) += [#{load_balancer_additional_ports}]" /tmp/deploy_nginx.yaml kubectl --kubeconfig /etc/rancher/k3s/k3s.yaml apply -f /tmp/deploy_nginx.yaml until [ -f /var/lib/rancher/k3s/server/token ] && [ -f /etc/rancher/k3s/k3s.yaml ]; do sleep 5; done From ac1a502569826bacaf2313c3b55219ec11be89b4 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 20:01:22 -0600 Subject: [PATCH 175/239] expose other ports for malcolm --- kubernetes/vagrant/Vagrantfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kubernetes/vagrant/Vagrantfile b/kubernetes/vagrant/Vagrantfile index aa6bf5e86..0f6069dfb 100644 --- a/kubernetes/vagrant/Vagrantfile +++ b/kubernetes/vagrant/Vagrantfile @@ -13,7 +13,7 @@ end server_ip = "192.168.56.10" server_hostname = "server.k3s.internal" -load_balancer_additional_ports = "{\\\"appProtocol\\\": \\\"tcp\\\", \\\"name\\\": \\\"proxied-tcp-5044\\\", \\\"port\\\": 5044, \\\"targetPort\\\": 5044, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"appProtocol\\\": \\\"tcp\\\", \\\"name\\\": \\\"proxied-tcp-5045\\\", \\\"port\\\": 5045, \\\"targetPort\\\": 5045, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"appProtocol\\\": \\\"tcp\\\", \\\"name\\\": \\\"proxied-tcp-8022\\\", \\\"port\\\": 8022, \\\"targetPort\\\": 8022, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"appProtocol\\\": \\\"tcp\\\", \\\"name\\\": \\\"proxied-tcp-9200\\\", \\\"port\\\": 9200, \\\"targetPort\\\": 9200, \\\"protocol\\\": \\\"TCP\\\"}" +load_balancer_additional_ports = "{\\\"appProtocol\\\": \\\"tcp\\\", \\\"name\\\": \\\"lumberjack\\\", \\\"port\\\": 5044, \\\"targetPort\\\": 5044, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"appProtocol\\\": \\\"tcp\\\", \\\"name\\\": \\\"tcpjson\\\", \\\"port\\\": 5045, \\\"targetPort\\\": 5045, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"appProtocol\\\": \\\"tcp\\\", \\\"name\\\": \\\"sftp\\\", \\\"port\\\": 8022, \\\"targetPort\\\": 8022, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"appProtocol\\\": \\\"tcp\\\", \\\"name\\\": \\\"opensearch\\\", \\\"port\\\": 9200, \\\"targetPort\\\": 9200, \\\"protocol\\\": \\\"TCP\\\"}" deployment_additional_ports = "{\\\"name\\\": \\\"lumberjack\\\", \\\"containerPort\\\": 5044, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"name\\\": \\\"tcpjson\\\", \\\"containerPort\\\": 5045, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"name\\\": \\\"sftp\\\", \\\"containerPort\\\": 8022, \\\"protocol\\\": \\\"TCP\\\"}, {\\\"name\\\": \\\"opensearch\\\", \\\"containerPort\\\": 9200, \\\"protocol\\\": \\\"TCP\\\"}" agents = { "agent1" => "192.168.56.11", From cfed2a34e089864c44637e9db558dcbb223ea59d Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 21:35:21 -0600 Subject: [PATCH 176/239] expose other ports for malcolm --- kubernetes/00-ingress.yml | 11 ----------- kubernetes/00-tcp-services.yml | 10 ++++++++++ 2 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 kubernetes/00-tcp-services.yml diff --git a/kubernetes/00-ingress.yml b/kubernetes/00-ingress.yml index e6578924a..f3a205db2 100644 --- a/kubernetes/00-ingress.yml +++ b/kubernetes/00-ingress.yml @@ -25,14 +25,3 @@ spec: name: nginx-proxy port: number: 443 ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: tcp-services - namespace: malcolm -data: - 5044: "malcolm/logstash:5044" - 5045: "malcolm/filebeat:5045" - 8022: "malcolm/upload:22" - 9200: "malcolm/nginx-proxy:9200" \ No newline at end of file diff --git a/kubernetes/00-tcp-services.yml b/kubernetes/00-tcp-services.yml new file mode 100644 index 000000000..167be1b6c --- /dev/null +++ b/kubernetes/00-tcp-services.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: tcp-services + namespace: malcolm +data: + 5044: "malcolm/logstash:5044" + 5045: "malcolm/filebeat:5045" + 8022: "malcolm/upload:22" + 9200: "malcolm/nginx-proxy:9200" From f2d07cb9641be3e8a191e99e773372dca73b612a Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 21:55:24 -0600 Subject: [PATCH 177/239] expose other ports for malcolm --- kubernetes/00-tcp-services.yml | 2 +- kubernetes/02-opensearch.yml | 1 + kubernetes/04-upload.yml | 1 + kubernetes/12-filebeat.yml | 1 + kubernetes/13-logstash.yml | 1 + kubernetes/vagrant/Vagrantfile | 2 +- 6 files changed, 6 insertions(+), 2 deletions(-) diff --git a/kubernetes/00-tcp-services.yml b/kubernetes/00-tcp-services.yml index 167be1b6c..aba46759a 100644 --- a/kubernetes/00-tcp-services.yml +++ b/kubernetes/00-tcp-services.yml @@ -2,7 +2,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: tcp-services - namespace: malcolm + namespace: ingress-nginx data: 5044: "malcolm/logstash:5044" 5045: "malcolm/filebeat:5045" diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index 42f867bd3..4da085972 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -5,6 +5,7 @@ metadata: name: opensearch namespace: malcolm spec: + type: ClusterIP ports: - port: 9200 protocol: TCP diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index be7673b05..5fb963afa 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -5,6 +5,7 @@ metadata: name: upload namespace: malcolm spec: + type: ClusterIP ports: - port: 22 protocol: TCP diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml index 42d61ee55..446a5698e 100644 --- a/kubernetes/12-filebeat.yml +++ b/kubernetes/12-filebeat.yml @@ -5,6 +5,7 @@ metadata: name: filebeat namespace: malcolm spec: + type: ClusterIP ports: - port: 5045 protocol: TCP diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index ef8d2544a..bf04f6ce4 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -5,6 +5,7 @@ metadata: name: logstash namespace: malcolm spec: + type: ClusterIP ports: - port: 5044 protocol: TCP diff --git a/kubernetes/vagrant/Vagrantfile b/kubernetes/vagrant/Vagrantfile index 0f6069dfb..77e2e2b61 100644 --- a/kubernetes/vagrant/Vagrantfile +++ b/kubernetes/vagrant/Vagrantfile @@ -114,7 +114,7 @@ server_script_1 = <<-SHELL echo "Waiting for k3s to start..." sleep 30 curl -sSL -o /tmp/deploy_nginx.yaml https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.0/deploy/static/provider/cloud/deploy.yaml - yq -i '( select(.kind == "Deployment").spec.template.spec.containers[].args[] | select(contains("/nginx-ingress-controller")) | parent ) += ["--enable-ssl-passthrough", "--tcp-services-configmap=\$(POD_NAMESPACE)/tcp-services"]' /tmp/deploy_nginx.yaml + yq -i '( select(.kind == "Deployment").spec.template.spec.containers[].args[] | select(contains("/nginx-ingress-controller")) | parent ) += ["--enable-ssl-passthrough", "--tcp-services-configmap=ingress-nginx/tcp-services"]' /tmp/deploy_nginx.yaml yq -i "( select(.kind == \\"Deployment\\").spec.template.spec.containers[].args[] | select(contains(\\"/nginx-ingress-controller\\")) | parent | parent | .ports ) += [#{deployment_additional_ports}]" /tmp/deploy_nginx.yaml yq -i "( select(.kind == \\"Service\\" and .spec.type == \\"LoadBalancer\\").spec.ports ) += [#{load_balancer_additional_ports}]" /tmp/deploy_nginx.yaml kubectl --kubeconfig /etc/rancher/k3s/k3s.yaml apply -f /tmp/deploy_nginx.yaml From 017fa0ae91564e2dce3b3c28aabf88dfed84192e Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 22:07:47 -0600 Subject: [PATCH 178/239] expose other ports for malcolm --- kubernetes/00-ingress.yml | 12 ++++++++++++ kubernetes/00-tcp-services.yml | 10 ---------- kubernetes/99-nginx-proxy.yml | 5 +++++ 3 files changed, 17 insertions(+), 10 deletions(-) delete mode 100644 kubernetes/00-tcp-services.yml diff --git a/kubernetes/00-ingress.yml b/kubernetes/00-ingress.yml index f3a205db2..187f44946 100644 --- a/kubernetes/00-ingress.yml +++ b/kubernetes/00-ingress.yml @@ -1,3 +1,4 @@ +--- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -25,3 +26,14 @@ spec: name: nginx-proxy port: number: 443 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: tcp-services + namespace: ingress-nginx +data: + 5044: "malcolm/logstash:5044" + 5045: "malcolm/filebeat:5045" + 8022: "malcolm/upload:22" + 9200: "malcolm/nginx-proxy:9200" \ No newline at end of file diff --git a/kubernetes/00-tcp-services.yml b/kubernetes/00-tcp-services.yml deleted file mode 100644 index aba46759a..000000000 --- a/kubernetes/00-tcp-services.yml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: tcp-services - namespace: ingress-nginx -data: - 5044: "malcolm/logstash:5044" - 5045: "malcolm/filebeat:5045" - 8022: "malcolm/upload:22" - 9200: "malcolm/nginx-proxy:9200" diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index ae7e10d51..374c007f0 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -8,6 +8,10 @@ spec: ports: - port: 443 protocol: TCP + - port: 8443 + protocol: TCP + - port: 9200 + protocol: TCP selector: name: nginx-proxy-deployment @@ -36,6 +40,7 @@ spec: ports: - containerPort: 443 - containerPort: 8443 + - containerPort: 9200 envFrom: - configMapRef: name: process-env From 7f7db0e3a19576af52ac68d76a1ae962882485a1 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 18 Apr 2023 22:47:47 -0600 Subject: [PATCH 179/239] give all ports names --- kubernetes/02-opensearch.yml | 6 ++++-- kubernetes/03-dashboards.yml | 5 ++++- kubernetes/04-upload.yml | 10 +++++++--- kubernetes/05-pcap-monitor.yml | 5 ++++- kubernetes/06-arkime.yml | 5 ++++- kubernetes/07-api.yml | 5 ++++- kubernetes/08-dashboards-helper.yml | 5 ++++- kubernetes/11-file-monitor.yml | 11 ++++++++++- kubernetes/12-filebeat.yml | 5 ++++- kubernetes/13-logstash.yml | 16 +++++++++++++--- kubernetes/15-netbox-redis.yml | 5 ++++- kubernetes/16-netbox-redis-cache.yml | 5 ++++- kubernetes/17-netbox-postgres.yml | 5 ++++- kubernetes/18-netbox.yml | 18 ++++++++++++------ kubernetes/19-htadmin.yml | 5 ++++- kubernetes/23-freq.yml | 5 ++++- kubernetes/99-nginx-proxy.yml | 16 +++++++++++++--- 17 files changed, 103 insertions(+), 29 deletions(-) diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index 4da085972..85d7db527 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -5,10 +5,10 @@ metadata: name: opensearch namespace: malcolm spec: - type: ClusterIP ports: - port: 9200 protocol: TCP + name: opensearch selector: name: opensearch-deployment @@ -40,7 +40,9 @@ spec: - IPC_LOCK - SYS_RESOURCE ports: - - containerPort: 9200 + - name: opensearch + protocol: TCP + containerPort: 9200 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/03-dashboards.yml b/kubernetes/03-dashboards.yml index b6b55e27c..bc513a9ee 100644 --- a/kubernetes/03-dashboards.yml +++ b/kubernetes/03-dashboards.yml @@ -8,6 +8,7 @@ spec: ports: - port: 5601 protocol: TCP + name: http selector: name: dashboards-deployment @@ -34,7 +35,9 @@ spec: stdin: false tty: true ports: - - containerPort: 5601 + - name: http + protocol: TCP + containerPort: 5601 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 5fb963afa..a1297cb04 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -9,7 +9,7 @@ spec: ports: - port: 22 protocol: TCP - name: ssh + name: sftp - port: 80 protocol: TCP name: http @@ -39,8 +39,12 @@ spec: stdin: false tty: true ports: - - containerPort: 22 - - containerPort: 80 + - name: sftp + protocol: TCP + containerPort: 22 + - name: http + protocol: TCP + containerPort: 80 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml index b3838dd8a..4ff27b76d 100644 --- a/kubernetes/05-pcap-monitor.yml +++ b/kubernetes/05-pcap-monitor.yml @@ -8,6 +8,7 @@ spec: ports: - port: 30441 protocol: TCP + name: zmq selector: name: pcap-monitor-deployment @@ -34,7 +35,9 @@ spec: stdin: false tty: true ports: - - containerPort: 30441 + - name: zmq + protocol: TCP + containerPort: 30441 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index 546b604d1..a2c1d2899 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -8,6 +8,7 @@ spec: ports: - port: 8005 protocol: TCP + name: http selector: name: arkime-deployment @@ -39,7 +40,9 @@ spec: - IPC_LOCK - SYS_RESOURCE ports: - - containerPort: 8005 + - name: http + protocol: TCP + containerPort: 8005 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/07-api.yml b/kubernetes/07-api.yml index d23a7a1af..d29939989 100644 --- a/kubernetes/07-api.yml +++ b/kubernetes/07-api.yml @@ -8,6 +8,7 @@ spec: ports: - port: 5000 protocol: TCP + name: http selector: name: api-deployment @@ -36,7 +37,9 @@ spec: command: ["gunicorn"] args: ["--bind", "0:5000", "manage:app"] ports: - - containerPort: 5000 + - name: http + protocol: TCP + containerPort: 5000 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/08-dashboards-helper.yml b/kubernetes/08-dashboards-helper.yml index 6e1a5981b..44832b52d 100644 --- a/kubernetes/08-dashboards-helper.yml +++ b/kubernetes/08-dashboards-helper.yml @@ -8,6 +8,7 @@ spec: ports: - port: 28991 protocol: TCP + name: http selector: name: dashboards-helper-deployment @@ -34,7 +35,9 @@ spec: stdin: false tty: true ports: - - containerPort: 28991 + - name: http + protocol: TCP + containerPort: 28991 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/11-file-monitor.yml b/kubernetes/11-file-monitor.yml index 3b6064b4f..d6d225241 100644 --- a/kubernetes/11-file-monitor.yml +++ b/kubernetes/11-file-monitor.yml @@ -8,6 +8,10 @@ spec: ports: - port: 3310 protocol: TCP + name: clamav + - port: 8440 + protocol: TCP + name: http selector: name: file-monitor-deployment @@ -34,7 +38,12 @@ spec: stdin: false tty: true ports: - - containerPort: 3310 + - name: clamav + containerPort: 3310 + protocol: TCP + - name: http + protocol: TCP + containerPort: 8440 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml index 446a5698e..febbf27b0 100644 --- a/kubernetes/12-filebeat.yml +++ b/kubernetes/12-filebeat.yml @@ -9,6 +9,7 @@ spec: ports: - port: 5045 protocol: TCP + name: tcpjson selector: name: filebeat-deployment @@ -35,7 +36,9 @@ spec: stdin: false tty: true ports: - - containerPort: 5045 + - name: tcpjson + protocol: TCP + containerPort: 5045 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index bf04f6ce4..36636094e 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -10,9 +10,12 @@ spec: - port: 5044 protocol: TCP name: lumberjack + - port: 9001 + protocol: TCP + name: supervisord - port: 9600 protocol: TCP - name: api + name: http selector: name: logstash-deployment @@ -54,8 +57,15 @@ spec: - IPC_LOCK - SYS_RESOURCE ports: - - containerPort: 5044 - - containerPort: 9600 + - name: lumberjack + protocol: TCP + containerPort: 5044 + - name: supervisord + protocol: TCP + containerPort: 9001 + - name: http + protocol: TCP + containerPort: 9600 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index 1be2acd21..afc71ade3 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -8,6 +8,7 @@ spec: ports: - port: 6379 protocol: TCP + name: redis selector: name: netbox-redis-deployment @@ -36,7 +37,9 @@ spec: command: ["sh"] args: ["-c", "redis-server --appendonly yes --requirepass $(REDIS_PASSWORD)"] ports: - - containerPort: 6379 + - name: redis + protocol: TCP + containerPort: 6379 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index 2fa333cb9..48dabb21e 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -8,6 +8,7 @@ spec: ports: - port: 6379 protocol: TCP + name: redis selector: name: netbox-redis-cache-deployment @@ -36,7 +37,9 @@ spec: command: ["sh"] args: ["-c", "redis-server --requirepass $(REDIS_PASSWORD)"] ports: - - containerPort: 6379 + - name: redis + protocol: TCP + containerPort: 6379 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index b81177855..1eb499c81 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -8,6 +8,7 @@ spec: ports: - port: 5432 protocol: TCP + name: postgresql selector: name: netbox-postgres-deployment @@ -34,7 +35,9 @@ spec: stdin: false tty: true ports: - - containerPort: 5432 + - name: postgresql + protocol: TCP + containerPort: 5432 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index ad5b5b6f4..908617354 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -8,13 +8,13 @@ spec: ports: - port: 8080 protocol: TCP - name: eightyeighty + name: http-main - port: 8081 protocol: TCP - name: eightyeightyone + name: http-status - port: 9001 protocol: TCP - name: ninethousandone + name: supervisord selector: name: netbox-deployment @@ -41,9 +41,15 @@ spec: stdin: false tty: true ports: - - containerPort: 8080 - - containerPort: 8081 - - containerPort: 9001 + - name: http-main + protocol: TCP + containerPort: 8080 + - name: http-status + protocol: TCP + containerPort: 8081 + - name: supervisord + protocol: TCP + containerPort: 9001 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml index d4f30e45c..b877ad463 100644 --- a/kubernetes/19-htadmin.yml +++ b/kubernetes/19-htadmin.yml @@ -8,6 +8,7 @@ spec: ports: - port: 80 protocol: TCP + name: http selector: name: htadmin-deployment @@ -34,7 +35,9 @@ spec: stdin: false tty: true ports: - - containerPort: 80 + - name: http + protocol: TCP + containerPort: 80 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/23-freq.yml b/kubernetes/23-freq.yml index b0c0130eb..4c71155da 100644 --- a/kubernetes/23-freq.yml +++ b/kubernetes/23-freq.yml @@ -8,6 +8,7 @@ spec: ports: - port: 10004 protocol: TCP + name: http selector: name: freq-deployment @@ -34,7 +35,9 @@ spec: stdin: false tty: true ports: - - containerPort: 10004 + - name: http + protocol: TCP + containerPort: 10004 envFrom: - configMapRef: name: process-env diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index 374c007f0..e4696de71 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -5,13 +5,17 @@ metadata: name: nginx-proxy namespace: malcolm spec: + type: ClusterIP ports: - port: 443 protocol: TCP + name: https - port: 8443 protocol: TCP + name: http - port: 9200 protocol: TCP + name: opensearch selector: name: nginx-proxy-deployment @@ -38,9 +42,15 @@ spec: stdin: false tty: true ports: - - containerPort: 443 - - containerPort: 8443 - - containerPort: 9200 + - name: https + protocol: TCP + containerPort: 443 + - name: http + protocol: TCP + containerPort: 8443 + - name: opensearch + protocol: TCP + containerPort: 9200 envFrom: - configMapRef: name: process-env From a10e261401c71518542616c186bb378d07eb27c0 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 19 Apr 2023 09:40:25 -0600 Subject: [PATCH 180/239] for idaholab/Malcolm#186, set up runtime-logs volume --- Dockerfiles/nginx.Dockerfile | 2 +- kubernetes/01-volumes.yml | 43 ++++++++++++++++++++++++++++++ kubernetes/12-filebeat.yml | 8 +++++- kubernetes/99-nginx-proxy.yml | 8 +++++- nginx/scripts/docker_entrypoint.sh | 2 ++ 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/Dockerfiles/nginx.Dockerfile b/Dockerfiles/nginx.Dockerfile index 31ca37f55..761ef04b9 100644 --- a/Dockerfiles/nginx.Dockerfile +++ b/Dockerfiles/nginx.Dockerfile @@ -169,7 +169,7 @@ RUN set -x ; \ zlib-dev \ ; \ \ - mkdir -p /usr/src/nginx-auth-ldap /usr/src/ngx_http_substitutions_filter_module /www /www/logs/nginx ; \ + mkdir -p /usr/src/nginx-auth-ldap /usr/src/ngx_http_substitutions_filter_module /www /www/logs/nginx /var/log/nginx ; \ tar -zxC /usr/src -f /nginx.tar.gz ; \ tar -zxC /usr/src/nginx-auth-ldap --strip=1 -f /nginx-auth-ldap.tar.gz ; \ tar -zxC /usr/src/ngx_http_substitutions_filter_module --strip=1 -f /ngx_http_substitutions_filter_module-master.tar.gz ; \ diff --git a/kubernetes/01-volumes.yml b/kubernetes/01-volumes.yml index 0f9ddf71d..a1256f0a8 100644 --- a/kubernetes/01-volumes.yml +++ b/kubernetes/01-volumes.yml @@ -170,6 +170,49 @@ spec: storage: 50Gi volumeName: config-volume +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: runtime-logs-volume + namespace: malcolm + labels: + namespace: malcolm +spec: + capacity: + storage: 50Gi + volumeMode: Filesystem + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: nfs + mountOptions: + - nfsvers=4.1 + - soft + - noac + - timeo=600 + - retrans=2 + nfs: + path: /malcolm/runtime-logs + server: 10.9.0.226 + readOnly: false + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: runtime-logs-claim + namespace: malcolm +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + volumeMode: Filesystem + resources: + requests: + storage: 50Gi + volumeName: runtime-logs-volume + --- apiVersion: v1 kind: PersistentVolume diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml index febbf27b0..31c6c9e9c 100644 --- a/kubernetes/12-filebeat.yml +++ b/kubernetes/12-filebeat.yml @@ -76,7 +76,9 @@ spec: name: filebeat-zeek-volume - mountPath: "/suricata" name: filebeat-suricata-volume - # TODO: live nginx-logs from nginx-proxy container + - name: filebeat-nginx-runtime-logs-volume + mountPath: /nginx + subPath: "nginx" volumes: - name: filebeat-var-local-catrust-volume configMap: @@ -93,3 +95,7 @@ spec: - name: filebeat-suricata-volume persistentVolumeClaim: claimName: suricata-claim + - name: filebeat-nginx-runtime-logs-volume + persistentVolumeClaim: + readOnly: true + claimName: runtime-logs-claim \ No newline at end of file diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index e4696de71..e193bc757 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -86,6 +86,9 @@ spec: subPath: "auth" - name: nginx-etc-auth-default-volume mountPath: /tmp/auth/default/configmap + - name: nginx-runtime-logs-volume + mountPath: /var/log/nginx + subPath: "nginx" volumes: - name: nginx-etc-nginx-volume configMap: @@ -107,4 +110,7 @@ spec: claimName: config-claim - name: nginx-etc-auth-default-volume configMap: - name: etc-nginx-auth \ No newline at end of file + name: etc-nginx-auth + - name: nginx-runtime-logs-volume + persistentVolumeClaim: + claimName: runtime-logs-claim \ No newline at end of file diff --git a/nginx/scripts/docker_entrypoint.sh b/nginx/scripts/docker_entrypoint.sh index 95a7dd83a..4d96735d7 100755 --- a/nginx/scripts/docker_entrypoint.sh +++ b/nginx/scripts/docker_entrypoint.sh @@ -244,5 +244,7 @@ if [[ ! -f /etc/nginx/auth/htpasswd ]] && [[ -f /tmp/auth/default/htpasswd ]]; t rm -rf /tmp/auth/* || true fi +rm -rf /var/log/nginx/* || true + # start supervisor (which will spawn nginx, stunnel, etc.) or whatever the default command is exec "$@" From da1b155430baae4a30da684febdacf321d131f72 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 19 Apr 2023 09:44:06 -0600 Subject: [PATCH 181/239] use /opt/arkime/logs in persistentvolumeclaim --- arkime/scripts/initarkime.sh | 2 +- kubernetes/06-arkime.yml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/arkime/scripts/initarkime.sh b/arkime/scripts/initarkime.sh index 86089a386..c094fe8c1 100755 --- a/arkime/scripts/initarkime.sh +++ b/arkime/scripts/initarkime.sh @@ -17,7 +17,7 @@ fi [[ "$OPENSEARCH_SSL_CERTIFICATE_VERIFICATION" != "true" ]] && DB_SSL_FLAG="--insecure" || DB_SSL_FLAG="" OPENSEARCH_URL_FULL="$(grep -Pi '^elasticsearch\s*=' $ARKIME_DIR/etc/config.ini | cut -d'=' -f2-)" -rm -f /var/run/arkime/initialized /var/run/arkime/runwise +rm -rf /var/run/arkime/initialized /var/run/arkime/runwise $ARKIME_DIR/logs/* # make sure TLS certificates exist prior to starting up CERT_FILE=$ARKIME_DIR/etc/viewer.crt diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index a2c1d2899..d5e7ac59c 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -76,6 +76,9 @@ spec: name: arkime-opensearch-curlrc-volume - mountPath: "/data/pcap" name: arkime-pcap-volume + - name: arkime-runtime-logs-volume + mountPath: /opt/arkime/logs + subPath: "arkime" volumes: - name: arkime-var-local-catrust-volume configMap: @@ -86,3 +89,6 @@ spec: - name: arkime-pcap-volume persistentVolumeClaim: claimName: pcap-claim + - name: arkime-runtime-logs-volume + persistentVolumeClaim: + claimName: runtime-logs-claim \ No newline at end of file From 1c1d7d55ef606136e0bcbd2e710569189d7c3e1c Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 19 Apr 2023 10:09:31 -0600 Subject: [PATCH 182/239] for idaholab/Malcolm#186, use runtime-logs share for Arkime logs --- arkime/scripts/initarkime.sh | 2 +- arkime/scripts/viewer_service.sh | 1 + arkime/scripts/wise_service.sh | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arkime/scripts/initarkime.sh b/arkime/scripts/initarkime.sh index c094fe8c1..86089a386 100755 --- a/arkime/scripts/initarkime.sh +++ b/arkime/scripts/initarkime.sh @@ -17,7 +17,7 @@ fi [[ "$OPENSEARCH_SSL_CERTIFICATE_VERIFICATION" != "true" ]] && DB_SSL_FLAG="--insecure" || DB_SSL_FLAG="" OPENSEARCH_URL_FULL="$(grep -Pi '^elasticsearch\s*=' $ARKIME_DIR/etc/config.ini | cut -d'=' -f2-)" -rm -rf /var/run/arkime/initialized /var/run/arkime/runwise $ARKIME_DIR/logs/* +rm -f /var/run/arkime/initialized /var/run/arkime/runwise # make sure TLS certificates exist prior to starting up CERT_FILE=$ARKIME_DIR/etc/viewer.crt diff --git a/arkime/scripts/viewer_service.sh b/arkime/scripts/viewer_service.sh index d9ce81977..0b589ceea 100755 --- a/arkime/scripts/viewer_service.sh +++ b/arkime/scripts/viewer_service.sh @@ -5,6 +5,7 @@ while true; do if [[ -f /var/run/arkime/initialized && "$VIEWER" == "on" ]]; then echo "Launch viewer..." + rm -f $ARKIME_DIR/logs/viewer* cd $ARKIME_DIR/viewer $ARKIME_DIR/bin/node viewer.js --insecure -c $ARKIME_DIR/etc/config.ini | tee -a $ARKIME_DIR/logs/viewer.log 2>&1 fi diff --git a/arkime/scripts/wise_service.sh b/arkime/scripts/wise_service.sh index 74d3deb7d..7f95f84c4 100755 --- a/arkime/scripts/wise_service.sh +++ b/arkime/scripts/wise_service.sh @@ -6,6 +6,7 @@ while true; do if [[ ("$WISE" == "on") && (-f /var/run/arkime/runwise) && (-f $ARKIME_DIR/etc/wise.ini) ]]; then echo "Launch wise..." + rm -f $ARKIME_DIR/logs/wise* pushd $ARKIME_DIR/wiseService >/dev/null 2>&1 $ARKIME_DIR/bin/node wiseService.js --insecure -c $ARKIME_DIR/etc/wise.ini popd >/dev/null 2>&1 From 7c49ae22371a17b3f95779b94eaef02c022eed21 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 19 Apr 2023 10:26:49 -0600 Subject: [PATCH 183/239] for idaholab/Malcolm#186, use runtime-logs share for Arkime logs --- arkime/scripts/viewer_service.sh | 3 ++- arkime/scripts/wise_service.sh | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arkime/scripts/viewer_service.sh b/arkime/scripts/viewer_service.sh index 0b589ceea..6c16aa932 100755 --- a/arkime/scripts/viewer_service.sh +++ b/arkime/scripts/viewer_service.sh @@ -6,8 +6,9 @@ while true; do if [[ -f /var/run/arkime/initialized && "$VIEWER" == "on" ]]; then echo "Launch viewer..." rm -f $ARKIME_DIR/logs/viewer* - cd $ARKIME_DIR/viewer + pushd $ARKIME_DIR/viewer >/dev/null 2>&1 $ARKIME_DIR/bin/node viewer.js --insecure -c $ARKIME_DIR/etc/config.ini | tee -a $ARKIME_DIR/logs/viewer.log 2>&1 + popd >/dev/null 2>&1 fi sleep 5 done diff --git a/arkime/scripts/wise_service.sh b/arkime/scripts/wise_service.sh index 7f95f84c4..ff9e26b34 100755 --- a/arkime/scripts/wise_service.sh +++ b/arkime/scripts/wise_service.sh @@ -2,13 +2,12 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. - while true; do if [[ ("$WISE" == "on") && (-f /var/run/arkime/runwise) && (-f $ARKIME_DIR/etc/wise.ini) ]]; then echo "Launch wise..." rm -f $ARKIME_DIR/logs/wise* pushd $ARKIME_DIR/wiseService >/dev/null 2>&1 - $ARKIME_DIR/bin/node wiseService.js --insecure -c $ARKIME_DIR/etc/wise.ini + $ARKIME_DIR/bin/node wiseService.js --insecure -c $ARKIME_DIR/etc/wise.ini | tee -a $ARKIME_DIR/logs/wise.log 2>&1 popd >/dev/null 2>&1 fi sleep 5 From 61c6960a9d3efec9838d5655a2fad28f279bf4ea Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 19 Apr 2023 14:28:53 -0600 Subject: [PATCH 184/239] allow verify_ssl=False with imported kubernetes in python --- scripts/malcolm_common.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index 69dd8cdf7..c3eda62c7 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -581,8 +581,14 @@ def YAMLDynamic(debug=False, forceInteraction=False): return DoDynamicImport("yaml", "pyyaml", interactive=forceInteraction, debug=debug) -def KubernetesDynamic(debug=False, forceInteraction=False): - return DoDynamicImport("kubernetes", "kubernetes", interactive=forceInteraction, debug=debug) +def KubernetesDynamic(verifySsl=False, debug=False, forceInteraction=False): + kubes = DoDynamicImport("kubernetes", "kubernetes", interactive=forceInteraction, debug=debug) + if kubes and not verifySsl: + configuration = kubes.client.Configuration() + configuration.verify_ssl = False + configuration.debug = False + kubes.client.Configuration.set_default(configuration) + return kubes def DotEnvDynamic(debug=False, forceInteraction=False): From 4e0e6f354d07c987590595f3a7fc2fa2592cf3d1 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 19 Apr 2023 16:05:47 -0600 Subject: [PATCH 185/239] revert 61c6960a9d3efec9838d5655a2fad28f279bf4ea, allow verify_ssl=False with imported kubernetes in python --- scripts/malcolm_common.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index c3eda62c7..94e3c9c55 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -582,13 +582,7 @@ def YAMLDynamic(debug=False, forceInteraction=False): def KubernetesDynamic(verifySsl=False, debug=False, forceInteraction=False): - kubes = DoDynamicImport("kubernetes", "kubernetes", interactive=forceInteraction, debug=debug) - if kubes and not verifySsl: - configuration = kubes.client.Configuration() - configuration.verify_ssl = False - configuration.debug = False - kubes.client.Configuration.set_default(configuration) - return kubes + return DoDynamicImport("kubernetes", "kubernetes", interactive=forceInteraction, debug=debug) def DotEnvDynamic(debug=False, forceInteraction=False): From b252b0a646a696b0eaa5fd7aa60dccbd473bfcd1 Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 20 Apr 2023 09:23:31 -0600 Subject: [PATCH 186/239] tmux commands --- malcolm-iso/config/includes.chroot/etc/skel/.tmux.conf | 2 ++ sensor-iso/config/includes.chroot/etc/skel/.tmux.conf | 2 ++ 2 files changed, 4 insertions(+) diff --git a/malcolm-iso/config/includes.chroot/etc/skel/.tmux.conf b/malcolm-iso/config/includes.chroot/etc/skel/.tmux.conf index 0ba442291..30e2eebb7 100644 --- a/malcolm-iso/config/includes.chroot/etc/skel/.tmux.conf +++ b/malcolm-iso/config/includes.chroot/etc/skel/.tmux.conf @@ -41,3 +41,5 @@ bind r source-file ~/.tmux.conf\; display "Reloaded conf." # Use vim keybindings in copy mode setw -g mode-keys vi +# don't increase the user count for every pane +set -g default-command "${SHELL}" diff --git a/sensor-iso/config/includes.chroot/etc/skel/.tmux.conf b/sensor-iso/config/includes.chroot/etc/skel/.tmux.conf index 0ba442291..30e2eebb7 100644 --- a/sensor-iso/config/includes.chroot/etc/skel/.tmux.conf +++ b/sensor-iso/config/includes.chroot/etc/skel/.tmux.conf @@ -41,3 +41,5 @@ bind r source-file ~/.tmux.conf\; display "Reloaded conf." # Use vim keybindings in copy mode setw -g mode-keys vi +# don't increase the user count for every pane +set -g default-command "${SHELL}" From 0093dea055ccd87fd58f9489a9d71a7c6edc270e Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 20 Apr 2023 13:35:59 -0600 Subject: [PATCH 187/239] idaholab/Malcolm#171, turn off proxy buffering so requests go straight to Malcolm's internal NGINX. from the docs (https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size), 'when buffering is disabled, the response is passed to a client synchronously, immediately as it is received' --- kubernetes/00-ingress.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/kubernetes/00-ingress.yml b/kubernetes/00-ingress.yml index 187f44946..60d423032 100644 --- a/kubernetes/00-ingress.yml +++ b/kubernetes/00-ingress.yml @@ -9,12 +9,16 @@ metadata: nginx.ingress.kubernetes.io/backend-protocol: "https" nginx.ingress.kubernetes.io/preserve-trailing-slash: "false" nginx.ingress.kubernetes.io/ssl-passthrough: "false" + nginx.ingress.kubernetes.io/client-body-buffer-size: "512k" nginx.ingress.kubernetes.io/proxy-body-size: "0" + nginx.ingress.kubernetes.io/proxy-max-temp-file-size: "0" + nginx.ingress.kubernetes.io/proxy-buffer-size: "512k" + nginx.ingress.kubernetes.io/proxy-buffering: "off" + nginx.ingress.kubernetes.io/proxy-request-buffering: "off" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "600" nginx.ingress.kubernetes.io/proxy-read-timeout: "600" nginx.ingress.kubernetes.io/proxy-send-timeout: "600" - nginx.ingress.kubernetes.io/proxy-buffering: "on" - nginx.ingress.kubernetes.io/proxy-buffer-size: "512k" - nginx.ingress.kubernetes.io/proxy-max-temp-file-size: "8192m" + spec: rules: - http: From b0388df11840113ba8f1ab5d843022f4a2768501 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 20 Apr 2023 13:44:28 -0600 Subject: [PATCH 188/239] use PersistentVolume for PCAP upload (idaholab/Malcolm#171) --- file-upload/docker-entrypoint.sh | 4 ++++ file-upload/php/php.ini | 1 + kubernetes/04-upload.yml | 3 +++ 3 files changed, 8 insertions(+) diff --git a/file-upload/docker-entrypoint.sh b/file-upload/docker-entrypoint.sh index 95685445f..d0e1d0ce0 100755 --- a/file-upload/docker-entrypoint.sh +++ b/file-upload/docker-entrypoint.sh @@ -12,6 +12,10 @@ then exit 1 fi +mkdir -p /tmp/upload_tmp_dir +chown :$PGROUP /tmp/upload_tmp_dir +chmod 775 /tmp/upload_tmp_dir + if ! getent passwd "$MALCOLM_USERNAME" >/dev/null then # Make sure every container gets its own SSH host keys the first time around diff --git a/file-upload/php/php.ini b/file-upload/php/php.ini index ce4b17d78..28c0ee979 100644 --- a/file-upload/php/php.ini +++ b/file-upload/php/php.ini @@ -44,6 +44,7 @@ user_dir = enable_dl = Off file_uploads = On upload_max_filesize = 50G +upload_tmp_dir = "/tmp/upload_tmp_dir" max_file_uploads = 8 allow_url_fopen = On allow_url_include = Off diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index a1297cb04..2d70d1ea5 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -73,6 +73,9 @@ spec: - mountPath: "/var/www/upload/server/php/chroot/files" name: upload-pcap-volume subPath: "upload" + - mountPath: "/tmp/upload_tmp_dir" + name: upload-pcap-volume + subPath: "spool" volumes: - name: upload-var-local-catrust-volume configMap: From cfa334392df44d1a7fe4bda8a0a7bd68a47147fd Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 20 Apr 2023 13:56:53 -0600 Subject: [PATCH 189/239] set php's upload_tmp_dir to a directory on the same storage as the /pcap/upload directory (idaholab/Malcolm#171) --- docker-compose-standalone.yml | 1 + docker-compose.yml | 1 + malcolm-iso/build.sh | 1 + scripts/control.py | 4 ++-- scripts/malcolm_appliance_packager.sh | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 4d4763d26..97f503bd1 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -421,6 +421,7 @@ services: volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/var/www/upload/server/php/chroot/files + - ./pcap/spool:/tmp/upload_tmp_dir healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost"] interval: 30s diff --git a/docker-compose.yml b/docker-compose.yml index 6135775f9..31f2833a3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -472,6 +472,7 @@ services: volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/var/www/upload/server/php/chroot/files + - ./pcap/spool:/tmp/upload_tmp_dir healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost"] interval: 30s diff --git a/malcolm-iso/build.sh b/malcolm-iso/build.sh index 1d2e0445b..1e94dfcd3 100755 --- a/malcolm-iso/build.sh +++ b/malcolm-iso/build.sh @@ -111,6 +111,7 @@ if [ -d "$WORKDIR" ]; then mkdir -p "$MALCOLM_DEST_DIR/opensearch-backup/" mkdir -p "$MALCOLM_DEST_DIR/opensearch/nodes/" mkdir -p "$MALCOLM_DEST_DIR/pcap/processed/" + mkdir -p "$MALCOLM_DEST_DIR/pcap/spool/" mkdir -p "$MALCOLM_DEST_DIR/pcap/upload/" mkdir -p "$MALCOLM_DEST_DIR/scripts/" mkdir -p "$MALCOLM_DEST_DIR/suricata-logs/live" diff --git a/scripts/control.py b/scripts/control.py index bf75974f7..fbc9e0823 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -821,7 +821,7 @@ def stop(wipe=False): BoundPath("netbox-postgres", "/var/lib/postgresql/data", True, None, ["."]), BoundPath("netbox-redis", "/data", True, None, ["."]), BoundPath("opensearch", "/usr/share/opensearch/data", True, ["nodes"], None), - BoundPath("pcap-monitor", "/pcap", True, ["processed", "upload"], None), + BoundPath("pcap-monitor", "/pcap", True, ["processed", "upload", "spool"], None), BoundPath("suricata", "/var/log/suricata", True, None, ["."]), BoundPath("upload", "/var/www/upload/server/php/chroot/files", True, None, None), BoundPath("zeek", "/zeek/extract_files", True, None, None), @@ -967,7 +967,7 @@ def start(): BoundPath("netbox-redis", "/data", False, None, None), BoundPath("opensearch", "/usr/share/opensearch/data", False, ["nodes"], None), BoundPath("opensearch", "/opt/opensearch/backup", False, None, None), - BoundPath("pcap-monitor", "/pcap", False, ["processed", "upload"], None), + BoundPath("pcap-monitor", "/pcap", False, ["processed", "upload", "spool"], None), BoundPath("suricata", "/var/log/suricata", False, ["live"], None), BoundPath("upload", "/var/www/upload/server/php/chroot/files", False, None, None), BoundPath("zeek", "/zeek/extract_files", False, None, None), diff --git a/scripts/malcolm_appliance_packager.sh b/scripts/malcolm_appliance_packager.sh index 8855e03ef..4c8bd21b0 100755 --- a/scripts/malcolm_appliance_packager.sh +++ b/scripts/malcolm_appliance_packager.sh @@ -76,6 +76,7 @@ if mkdir "$DESTDIR"; then mkdir $VERBOSE -p "$DESTDIR/opensearch-backup/" mkdir $VERBOSE -p "$DESTDIR/opensearch/nodes/" mkdir $VERBOSE -p "$DESTDIR/pcap/processed/" + mkdir $VERBOSE -p "$DESTDIR/pcap/spool/" mkdir $VERBOSE -p "$DESTDIR/pcap/upload/" mkdir $VERBOSE -p "$DESTDIR/config/" mkdir $VERBOSE -p "$DESTDIR/scripts/" From cbe56f88901efd867d74337949f45dd74d7e9b38 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 20 Apr 2023 14:22:01 -0600 Subject: [PATCH 190/239] Revert "set php's upload_tmp_dir to a directory on the same storage as the /pcap/upload directory (idaholab/Malcolm#171)" This reverts commit cfa334392df44d1a7fe4bda8a0a7bd68a47147fd. --- docker-compose-standalone.yml | 1 - docker-compose.yml | 1 - malcolm-iso/build.sh | 1 - scripts/control.py | 4 ++-- scripts/malcolm_appliance_packager.sh | 1 - 5 files changed, 2 insertions(+), 6 deletions(-) diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 97f503bd1..4d4763d26 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -421,7 +421,6 @@ services: volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/var/www/upload/server/php/chroot/files - - ./pcap/spool:/tmp/upload_tmp_dir healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost"] interval: 30s diff --git a/docker-compose.yml b/docker-compose.yml index 31f2833a3..6135775f9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -472,7 +472,6 @@ services: volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/var/www/upload/server/php/chroot/files - - ./pcap/spool:/tmp/upload_tmp_dir healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost"] interval: 30s diff --git a/malcolm-iso/build.sh b/malcolm-iso/build.sh index 1e94dfcd3..1d2e0445b 100755 --- a/malcolm-iso/build.sh +++ b/malcolm-iso/build.sh @@ -111,7 +111,6 @@ if [ -d "$WORKDIR" ]; then mkdir -p "$MALCOLM_DEST_DIR/opensearch-backup/" mkdir -p "$MALCOLM_DEST_DIR/opensearch/nodes/" mkdir -p "$MALCOLM_DEST_DIR/pcap/processed/" - mkdir -p "$MALCOLM_DEST_DIR/pcap/spool/" mkdir -p "$MALCOLM_DEST_DIR/pcap/upload/" mkdir -p "$MALCOLM_DEST_DIR/scripts/" mkdir -p "$MALCOLM_DEST_DIR/suricata-logs/live" diff --git a/scripts/control.py b/scripts/control.py index fbc9e0823..bf75974f7 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -821,7 +821,7 @@ def stop(wipe=False): BoundPath("netbox-postgres", "/var/lib/postgresql/data", True, None, ["."]), BoundPath("netbox-redis", "/data", True, None, ["."]), BoundPath("opensearch", "/usr/share/opensearch/data", True, ["nodes"], None), - BoundPath("pcap-monitor", "/pcap", True, ["processed", "upload", "spool"], None), + BoundPath("pcap-monitor", "/pcap", True, ["processed", "upload"], None), BoundPath("suricata", "/var/log/suricata", True, None, ["."]), BoundPath("upload", "/var/www/upload/server/php/chroot/files", True, None, None), BoundPath("zeek", "/zeek/extract_files", True, None, None), @@ -967,7 +967,7 @@ def start(): BoundPath("netbox-redis", "/data", False, None, None), BoundPath("opensearch", "/usr/share/opensearch/data", False, ["nodes"], None), BoundPath("opensearch", "/opt/opensearch/backup", False, None, None), - BoundPath("pcap-monitor", "/pcap", False, ["processed", "upload", "spool"], None), + BoundPath("pcap-monitor", "/pcap", False, ["processed", "upload"], None), BoundPath("suricata", "/var/log/suricata", False, ["live"], None), BoundPath("upload", "/var/www/upload/server/php/chroot/files", False, None, None), BoundPath("zeek", "/zeek/extract_files", False, None, None), diff --git a/scripts/malcolm_appliance_packager.sh b/scripts/malcolm_appliance_packager.sh index 4c8bd21b0..8855e03ef 100755 --- a/scripts/malcolm_appliance_packager.sh +++ b/scripts/malcolm_appliance_packager.sh @@ -76,7 +76,6 @@ if mkdir "$DESTDIR"; then mkdir $VERBOSE -p "$DESTDIR/opensearch-backup/" mkdir $VERBOSE -p "$DESTDIR/opensearch/nodes/" mkdir $VERBOSE -p "$DESTDIR/pcap/processed/" - mkdir $VERBOSE -p "$DESTDIR/pcap/spool/" mkdir $VERBOSE -p "$DESTDIR/pcap/upload/" mkdir $VERBOSE -p "$DESTDIR/config/" mkdir $VERBOSE -p "$DESTDIR/scripts/" From 4f83397f9e566d205569cce8f6644294055288bf Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 20 Apr 2023 14:22:09 -0600 Subject: [PATCH 191/239] Revert "use PersistentVolume for PCAP upload (idaholab/Malcolm#171)" This reverts commit b0388df11840113ba8f1ab5d843022f4a2768501. --- file-upload/docker-entrypoint.sh | 4 ---- file-upload/php/php.ini | 1 - kubernetes/04-upload.yml | 3 --- 3 files changed, 8 deletions(-) diff --git a/file-upload/docker-entrypoint.sh b/file-upload/docker-entrypoint.sh index d0e1d0ce0..95685445f 100755 --- a/file-upload/docker-entrypoint.sh +++ b/file-upload/docker-entrypoint.sh @@ -12,10 +12,6 @@ then exit 1 fi -mkdir -p /tmp/upload_tmp_dir -chown :$PGROUP /tmp/upload_tmp_dir -chmod 775 /tmp/upload_tmp_dir - if ! getent passwd "$MALCOLM_USERNAME" >/dev/null then # Make sure every container gets its own SSH host keys the first time around diff --git a/file-upload/php/php.ini b/file-upload/php/php.ini index 28c0ee979..ce4b17d78 100644 --- a/file-upload/php/php.ini +++ b/file-upload/php/php.ini @@ -44,7 +44,6 @@ user_dir = enable_dl = Off file_uploads = On upload_max_filesize = 50G -upload_tmp_dir = "/tmp/upload_tmp_dir" max_file_uploads = 8 allow_url_fopen = On allow_url_include = Off diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 2d70d1ea5..a1297cb04 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -73,9 +73,6 @@ spec: - mountPath: "/var/www/upload/server/php/chroot/files" name: upload-pcap-volume subPath: "upload" - - mountPath: "/tmp/upload_tmp_dir" - name: upload-pcap-volume - subPath: "spool" volumes: - name: upload-var-local-catrust-volume configMap: From 66a9d40e29663fd2727d63ef974ce9038ceb765e Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 20 Apr 2023 14:28:46 -0600 Subject: [PATCH 192/239] reinclude osd_transform dashboard plugin, update supercronic and fluent-bit --- Dockerfiles/dashboards-helper.Dockerfile | 4 ++-- Dockerfiles/dashboards.Dockerfile | 5 ++--- Dockerfiles/file-monitor.Dockerfile | 4 ++-- Dockerfiles/filebeat.Dockerfile | 4 ++-- Dockerfiles/netbox.Dockerfile | 4 ++-- Dockerfiles/suricata.Dockerfile | 4 ++-- Dockerfiles/zeek.Dockerfile | 4 ++-- scripts/third-party-logs/fluent-bit-setup.ps1 | 4 ++-- 8 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Dockerfiles/dashboards-helper.Dockerfile b/Dockerfiles/dashboards-helper.Dockerfile index 2b070b928..d4f5f322e 100644 --- a/Dockerfiles/dashboards-helper.Dockerfile +++ b/Dockerfiles/dashboards-helper.Dockerfile @@ -47,10 +47,10 @@ ENV DASHBOARDS_URL $DASHBOARDS_URL ENV DASHBOARDS_DARKMODE $DASHBOARDS_DARKMODE ENV PATH="/data:${PATH}" -ENV SUPERCRONIC_VERSION "0.2.2" +ENV SUPERCRONIC_VERSION "0.2.23" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" ENV SUPERCRONIC "supercronic-linux-amd64" -ENV SUPERCRONIC_SHA1SUM "2319da694833c7a147976b8e5f337cd83397d6be" +ENV SUPERCRONIC_SHA1SUM "dbed853c06aba2c611bbb1955b2c1667b51dcb0a" ENV SUPERCRONIC_CRONTAB "/etc/crontab" ENV ECS_RELEASES_URL "https://api.github.com/repos/elastic/ecs/releases/latest" diff --git a/Dockerfiles/dashboards.Dockerfile b/Dockerfiles/dashboards.Dockerfile index 31777e95e..e5293fbce 100644 --- a/Dockerfiles/dashboards.Dockerfile +++ b/Dockerfiles/dashboards.Dockerfile @@ -90,7 +90,7 @@ ENV PUSER_PRIV_DROP true ENV TERM xterm ENV TINI_VERSION v0.19.0 -ENV OSD_TRANSFORM_VIS_VERSION 2.5.0 +ENV OSD_TRANSFORM_VIS_VERSION 2.6.0 ARG OPENSEARCH_URL="http://opensearch:9200" ARG OPENSEARCH_LOCAL="true" @@ -122,8 +122,7 @@ RUN yum upgrade -y && \ /usr/share/opensearch-dashboards/bin/opensearch-dashboards-plugin remove securityDashboards --allow-root && \ cd /usr/share/opensearch-dashboards/plugins && \ /usr/share/opensearch-dashboards/bin/opensearch-dashboards-plugin install file:///tmp/kbnSankeyVis.zip --allow-root && \ - # TODO: when 2.6.0 is released /usr/share/opensearch-dashboards/bin/opensearch-dashboards-plugin install https://github.com/lguillaud/osd_transform_vis/releases/download/$OSD_TRANSFORM_VIS_VERSION/transformVis-$OSD_TRANSFORM_VIS_VERSION.zip --allow-root && \ - # trying to see if things still work if these are owned by root (to avoid a costly chown on container startup) + /usr/share/opensearch-dashboards/bin/opensearch-dashboards-plugin install https://github.com/lguillaud/osd_transform_vis/releases/download/$OSD_TRANSFORM_VIS_VERSION/transformVis-$OSD_TRANSFORM_VIS_VERSION.zip --allow-root && \ chown --silent -R root:root /usr/share/opensearch-dashboards/plugins/* \ /usr/share/opensearch-dashboards/node_modules/* \ /usr/share/opensearch-dashboards/src/* && \ diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index 53a104eb1..4b3f5fab1 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -89,10 +89,10 @@ ENV EXTRACTED_FILE_HTTP_SERVER_ENCRYPT $EXTRACTED_FILE_HTTP_SERVER_ENCRYPT ENV EXTRACTED_FILE_HTTP_SERVER_KEY $EXTRACTED_FILE_HTTP_SERVER_KEY ENV EXTRACTED_FILE_HTTP_SERVER_PORT $EXTRACTED_FILE_HTTP_SERVER_PORT -ENV SUPERCRONIC_VERSION "0.2.2" +ENV SUPERCRONIC_VERSION "0.2.23" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" ENV SUPERCRONIC "supercronic-linux-amd64" -ENV SUPERCRONIC_SHA1SUM "2319da694833c7a147976b8e5f337cd83397d6be" +ENV SUPERCRONIC_SHA1SUM "dbed853c06aba2c611bbb1955b2c1667b51dcb0a" ENV SUPERCRONIC_CRONTAB "/etc/crontab" COPY --chmod=755 shared/bin/yara_rules_setup.sh /usr/local/bin/ diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index 3090e047e..a051b3b55 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -57,10 +57,10 @@ ARG FILEBEAT_TCP_PARSE_TARGET_FIELD="" ARG FILEBEAT_TCP_PARSE_DROP_FIELD="" ARG FILEBEAT_TCP_TAG="_malcolm_beats" -ENV SUPERCRONIC_VERSION "0.2.2" +ENV SUPERCRONIC_VERSION "0.2.23" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" ENV SUPERCRONIC "supercronic-linux-amd64" -ENV SUPERCRONIC_SHA1SUM "2319da694833c7a147976b8e5f337cd83397d6be" +ENV SUPERCRONIC_SHA1SUM "dbed853c06aba2c611bbb1955b2c1667b51dcb0a" ENV SUPERCRONIC_CRONTAB "/etc/crontab" ENV TINI_VERSION v0.19.0 diff --git a/Dockerfiles/netbox.Dockerfile b/Dockerfiles/netbox.Dockerfile index 8e1a369a9..c4719028e 100644 --- a/Dockerfiles/netbox.Dockerfile +++ b/Dockerfiles/netbox.Dockerfile @@ -22,10 +22,10 @@ ENV PUSER "boxer" ENV PGROUP "boxer" ENV PUSER_PRIV_DROP true -ENV SUPERCRONIC_VERSION "0.2.2" +ENV SUPERCRONIC_VERSION "0.2.23" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" ENV SUPERCRONIC "supercronic-linux-amd64" -ENV SUPERCRONIC_SHA1SUM "2319da694833c7a147976b8e5f337cd83397d6be" +ENV SUPERCRONIC_SHA1SUM "dbed853c06aba2c611bbb1955b2c1667b51dcb0a" ENV SUPERCRONIC_CRONTAB "/etc/crontab" ENV NETBOX_DEVICETYPE_LIBRARY_URL "https://codeload.github.com/netbox-community/devicetype-library/tar.gz/master" diff --git a/Dockerfiles/suricata.Dockerfile b/Dockerfiles/suricata.Dockerfile index 48f48c379..2030ed4f3 100644 --- a/Dockerfiles/suricata.Dockerfile +++ b/Dockerfiles/suricata.Dockerfile @@ -27,10 +27,10 @@ ENV PGROUP "suricata" # a final check in docker_entrypoint.sh before startup ENV PUSER_PRIV_DROP false -ENV SUPERCRONIC_VERSION "0.2.2" +ENV SUPERCRONIC_VERSION "0.2.23" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" ENV SUPERCRONIC "supercronic-linux-amd64" -ENV SUPERCRONIC_SHA1SUM "2319da694833c7a147976b8e5f337cd83397d6be" +ENV SUPERCRONIC_SHA1SUM "dbed853c06aba2c611bbb1955b2c1667b51dcb0a" ENV SUPERCRONIC_CRONTAB "/etc/crontab" ENV YQ_VERSION "4.24.2" diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index a2a753b00..555a3ccf6 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -36,10 +36,10 @@ ARG ZEEK_VERSION=5.2.1-0 ENV ZEEK_LTS $ZEEK_LTS ENV ZEEK_VERSION $ZEEK_VERSION -ENV SUPERCRONIC_VERSION "0.2.2" +ENV SUPERCRONIC_VERSION "0.2.23" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" ENV SUPERCRONIC "supercronic-linux-amd64" -ENV SUPERCRONIC_SHA1SUM "2319da694833c7a147976b8e5f337cd83397d6be" +ENV SUPERCRONIC_SHA1SUM "dbed853c06aba2c611bbb1955b2c1667b51dcb0a" ENV SUPERCRONIC_CRONTAB "/etc/crontab" # for build diff --git a/scripts/third-party-logs/fluent-bit-setup.ps1 b/scripts/third-party-logs/fluent-bit-setup.ps1 index 5916189dd..e5ba98b7e 100644 --- a/scripts/third-party-logs/fluent-bit-setup.ps1 +++ b/scripts/third-party-logs/fluent-bit-setup.ps1 @@ -8,8 +8,8 @@ # Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. ############################################################################### -$fluent_bit_version = '2.0' -$fluent_bit_full_version = '2.0.10' +$fluent_bit_version = '2.1' +$fluent_bit_full_version = '2.1.1' ############################################################################### # select an item from a menu provided in an array From 4e88d6a19d97990911ff85f0d330a2920fee9002 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Thu, 20 Apr 2023 14:47:56 -0600 Subject: [PATCH 193/239] stub out places for documentation --- docs/README.md | 1 + docs/kubernetes.md | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 docs/kubernetes.md diff --git a/docs/README.md b/docs/README.md index 59f38d16e..07468ed13 100644 --- a/docs/README.md +++ b/docs/README.md @@ -97,6 +97,7 @@ For smaller networks, use at home by network security enthusiasts, or in the fie - [Generating the ISO](malcolm-iso.md#ISOBuild) - [Setup](malcolm-iso.md#ISOSetup) - [Time synchronization](time-sync.md#ConfigTime) +* [Deploying Malcolm with Kubernetes](kubernetes.md#Kubernetes) * [Hardening](hardening.md#Hardening) - [Compliance Exceptions](hardening.md#ComplianceExceptions) * [Installation example using Ubuntu 22.04 LTS](ubuntu-install-example.md#InstallationExample) diff --git a/docs/kubernetes.md b/docs/kubernetes.md new file mode 100644 index 000000000..e3c2c49d3 --- /dev/null +++ b/docs/kubernetes.md @@ -0,0 +1,5 @@ +# Deploying Malcolm with Kubernetes + +* [TODO](#Todo) + +## TODO From f893f9a699f8c342acf9e666eb6a37276844051b Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 08:17:37 -0600 Subject: [PATCH 194/239] work in progress for idaholab/Malcolm#173, documentation changes for kubernetes deployment. this commit takes care of the changes for moving stuff from docker-compose.yml into .env configuration files --- docs/README.md | 2 +- docs/asset-interaction-analysis.md | 10 +- docs/authsetup.md | 12 +- docs/contributing-dashboards.md | 2 +- docs/contributing-local-modifications.md | 288 ++++++++++++++++------- docs/contributing-logstash.md | 2 +- docs/contributing-zeek.md | 2 +- docs/file-scanning.md | 14 +- docs/host-config-windows.md | 2 +- docs/ics-best-guess.md | 2 +- docs/index-management.md | 4 +- docs/live-analysis.md | 10 +- docs/malcolm-config.md | 158 +++++++------ docs/malcolm-hedgehog-e2e-iso-install.md | 5 +- docs/malcolm-iso.md | 2 +- docs/malcolm-preparation.md | 2 +- docs/malcolm-upgrade.md | 6 +- docs/opensearch-instances.md | 6 +- docs/quickstart.md | 4 +- docs/running.md | 2 +- docs/severity.md | 10 +- docs/third-party-logs.md | 8 +- docs/ubuntu-install-example.md | 8 +- docs/upload.md | 4 +- malcolm-iso/build.sh | 1 + scripts/configure | 1 + scripts/install.py | 3 + scripts/malcolm_appliance_packager.sh | 1 + 28 files changed, 352 insertions(+), 219 deletions(-) create mode 120000 scripts/configure diff --git a/docs/README.md b/docs/README.md index 07468ed13..cdcc3f161 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,7 +24,7 @@ For smaller networks, use at home by network security enthusiasts, or in the fie * [Configuration](malcolm-preparation.md#Configuration) - [Recommended system requirements](system-requirements.md#SystemRequirements) - [Malcolm Configuration](malcolm-config.md#ConfigAndTuning) - + [`docker-compose.yml` parameters](malcolm-config.md#DockerComposeYml) + + [Environment Variable Files](malcolm-config.md#MalcolmConfigEnvVars) - [Configure authentication](authsetup.md#AuthSetup) + [Local account management](authsetup.md#AuthBasicAccountManagement) + [Lightweight Directory Access Protocol (LDAP) authentication](authsetup.md#AuthLDAP) diff --git a/docs/asset-interaction-analysis.md b/docs/asset-interaction-analysis.md index 2c2ce86a4..a72884e26 100644 --- a/docs/asset-interaction-analysis.md +++ b/docs/asset-interaction-analysis.md @@ -16,7 +16,7 @@ Please see the [NetBox page on GitHub](https://github.com/netbox-community/netbo ## Enriching network traffic metadata via NetBox lookups -As Zeek logs and Suricata alerts are parsed and enriched (if the `LOGSTASH_NETBOX_ENRICHMENT` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) is set to `true`) the NetBox API will be queried for the associated hosts' information. If found, the information retrieved by NetBox will be used to enrich these logs through the creation of the following new fields. See [the NetBox API](https://demo.netbox.dev/api/docs/) documentation and [the NetBox documentation](https://demo.netbox.dev/static/docs/introduction/). +As Zeek logs and Suricata alerts are parsed and enriched (if the `LOGSTASH_NETBOX_ENRICHMENT` [environment variable in `./config/logstash.env`](malcolm-config.md#MalcolmConfigEnvVars) is set to `true`) the NetBox API will be queried for the associated hosts' information. If found, the information retrieved by NetBox will be used to enrich these logs through the creation of the following new fields. See [the NetBox API](https://demo.netbox.dev/api/docs/) documentation and [the NetBox documentation](https://demo.netbox.dev/static/docs/introduction/). * `destination.…` - `destination.device.cluster` (`/virtualization/clusters/`) (for [Virtual Machine](https://demo.netbox.dev/static/docs/coe-functionality/virtualization/) device types) @@ -28,13 +28,13 @@ As Zeek logs and Suricata alerts are parsed and enriched (if the `LOGSTASH_NETBO - [`destination.device.service`](https://demo.netbox.dev/static/docs/core-functionality/services/#service-templates) (`/ipam/services/`) - `destination.device.site` (`/dcim/sites/`) - `destination.device.url` (`/dcim/devices/`) - - `destination.device.details` (full JSON object, [only with `LOGSTASH_NETBOX_ENRICHMENT_VERBOSE: 'true'`](malcolm-config.md#DockerComposeYml)) + - `destination.device.details` (full JSON object, [only with `LOGSTASH_NETBOX_ENRICHMENT_VERBOSE: 'true'`](malcolm-config.md#MalcolmConfigEnvVars)) - `destination.segment.id` (`/ipam/vrfs/{id}`) - `destination.segment.name` (`/ipam/vrfs/`) - `destination.segment.site` (`/dcim/sites/`) - `destination.segment.tenant` (`/tenancy/tenants/`) - `destination.segment.url` (`/ipam/vrfs/`) - - `destination.segment.details` (full JSON object, [only with `LOGSTASH_NETBOX_ENRICHMENT_VERBOSE: 'true'`](malcolm-config.md#DockerComposeYml)) + - `destination.segment.details` (full JSON object, [only with `LOGSTASH_NETBOX_ENRICHMENT_VERBOSE: 'true'`](malcolm-config.md#MalcolmConfigEnvVars)) * `source.…` same as `destination.…` * collected as `related` fields (the [same approach](https://www.elastic.co/guide/en/ecs/current/ecs-related.html) used in ECS) - `related.device_type` @@ -47,7 +47,7 @@ As Zeek logs and Suricata alerts are parsed and enriched (if the `LOGSTASH_NETBO For Malcolm's purposes, both physical devices and virtualized hosts will be stored as described above: the `device_type` field can be used to distinguish between them. -NetBox has the concept of [sites](https://demo.netbox.dev/static/docs/core-functionality/sites-and-racks/). Sites can have overlapping IP address ranges, of course. The value of the `NETBOX_DEFAULT_SITE` variable in [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) will be used as a query parameter for these enrichment lookups. +NetBox has the concept of [sites](https://demo.netbox.dev/static/docs/core-functionality/sites-and-racks/). Sites can have overlapping IP address ranges, of course. The value of the `NETBOX_DEFAULT_SITE` variable in [environment variable in `netbox-common.env`](malcolm-config.md#MalcolmConfigEnvVars) will be used as a query parameter for these enrichment lookups. This feature was implemented as described in [idaholab/Malcolm#132](https://github.com/idaholab/Malcolm/issues/132). @@ -73,7 +73,7 @@ See [idaholab/Malcolm#134](https://github.com/idaholab/Malcolm/issues/134). ## Populate NetBox inventory via passively-gathered network traffic metadata -The purpose of an asset management system is to document the intended state of a network: were Malcolm to actively and agressively populate NetBox with the live network state, a network configuration fault could result in an incorrect documented configuration. The Malcolm development team is investigating what data, if any, should automatically flow to NetBox based on traffic observed (enabled via the `NETBOX_CRON` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml)), and what NetBox inventory data could be used, if any, to enrich Malcolm's network traffic metadata. Well-considered suggestions in this area are welcome. +The purpose of an asset management system is to document the intended state of a network: were Malcolm to actively and agressively populate NetBox with the live network state, a network configuration fault could result in an incorrect documented configuration. The Malcolm development team is investigating what data, if any, should automatically flow to NetBox based on traffic observed (enabled via the `NETBOX_CRON` [environment variable in `netbox-common.env`](malcolm-config.md#MalcolmConfigEnvVars)), and what NetBox inventory data could be used, if any, to enrich Malcolm's network traffic metadata. Well-considered suggestions in this area are welcome. See [idaholab/Malcolm#135](https://github.com/idaholab/Malcolm/issues/135). diff --git a/docs/authsetup.md b/docs/authsetup.md index e1d051edd..96b0a1ac1 100644 --- a/docs/authsetup.md +++ b/docs/authsetup.md @@ -12,7 +12,7 @@ With the local basic authentication method, user accounts are managed by Malcolm LDAP authentication are managed on a remote directory service, such as a [Microsoft Active Directory Domain Services](https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/get-started/virtual-dc/active-directory-domain-services-overview) or [OpenLDAP](https://www.openldap.org/). -Malcolm's authentication method is defined in the `x-auth-variables` section near the top of the [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) file with the `NGINX_BASIC_AUTH` environment variable: `true` for local TLS-encrypted HTTP basic authentication, `false` for LDAP authentication. +Malcolm's authentication method is defined in the [`auth-common.env` configuration file](malcolm-config.md#MalcolmConfigEnvVars) file with the `NGINX_BASIC_AUTH` environment variable: `true` for local TLS-encrypted HTTP basic authentication, `false` for LDAP authentication and `no_authentication` to disable authentication completely. In either case, you **must** run `./scripts/auth_setup` before starting Malcolm for the first time in order to: @@ -82,16 +82,16 @@ Authentication over LDAP can be done using one of three ways, [two of which](htt * **LDAPS** - a commonly used (though unofficial and considered deprecated) method in which SSL negotiation takes place before any commands are sent from the client to the server * **Unencrypted** (cleartext) (***not recommended***) -In addition to the `NGINX_BASIC_AUTH` environment variable being set to `false` in the `x-auth-variables` section near the top of the [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) file, the `NGINX_LDAP_TLS_STUNNEL` and `NGINX_LDAP_TLS_STUNNEL` environment variables are used in conjunction with the values in `nginx/nginx_ldap.conf` to define the LDAP connection security level. Use the following combinations of values to achieve the connection security methods above, respectively: +In addition to the `NGINX_BASIC_AUTH` environment variable being set to `false` in the [`auth-common.env` configuration file](malcolm-config.md#MalcolmConfigEnvVars) file, the `NGINX_LDAP_TLS_STUNNEL` and `NGINX_LDAP_TLS_STUNNEL` environment variables are used in conjunction with the values in `nginx/nginx_ldap.conf` to define the LDAP connection security level. Use the following combinations of values to achieve the connection security methods above, respectively: * **StartTLS** - - `NGINX_LDAP_TLS_STUNNEL` set to `true` in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) + - `NGINX_LDAP_TLS_STUNNEL` set to `true` in [`auth-common.env`](malcolm-config.md#MalcolmConfigEnvVars) - `url` should begin with `ldap://` and its port should be either the default LDAP port (389) or the default Global Catalog port (3268) in `nginx/nginx_ldap.conf` * **LDAPS** - - `NGINX_LDAP_TLS_STUNNEL` set to `false` in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) + - `NGINX_LDAP_TLS_STUNNEL` set to `false` in [`auth-common.env`](malcolm-config.md#MalcolmConfigEnvVars) - `url` should begin with `ldaps://` and its port should be either the default LDAPS port (636) or the default LDAPS Global Catalog port (3269) in `nginx/nginx_ldap.conf` * **Unencrypted** (clear text) (***not recommended***) - - `NGINX_LDAP_TLS_STUNNEL` set to `false` in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) + - `NGINX_LDAP_TLS_STUNNEL` set to `false` in [`auth-common.env`](malcolm-config.md#MalcolmConfigEnvVars) - `url` should begin with `ldap://` and its port should be either the default LDAP port (389) or the default Global Catalog port (3268) in `nginx/nginx_ldap.conf` For encrypted connections (whether using **StartTLS** or **LDAPS**), Malcolm will require and verify certificates when one or more trusted CA certificate files are placed in the `nginx/ca-trust/` directory. Otherwise, any certificate presented by the domain server will be accepted. @@ -102,4 +102,4 @@ When you [set up authentication](#AuthSetup) for Malcolm a set of unique [self-s Another option is to generate your own certificates (or have them issued to you) and have them placed in the `nginx/certs/` directory. The certificate and key file should be named `cert.pem` and `key.pem`, respectively. -A third possibility is to use a third-party reverse proxy (e.g., [Traefik](https://doc.traefik.io/traefik/) or [Caddy](https://caddyserver.com/docs/quick-starts/reverse-proxy)) to handle the issuance of the certificates for you and to broker the connections between clients and Malcolm. Reverse proxies such as these often implement the [ACME](https://datatracker.ietf.org/doc/html/rfc8555) protocol for domain name authentication and can be used to request certificates from certificate authorities like [Let's Encrypt](https://letsencrypt.org/how-it-works/). In this configuration, the reverse proxy will be encrypting the connections instead of Malcolm, so you'll need to set the `NGINX_SSL` environment variable to `false` in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) (or answer `no` to the "Require encrypted HTTPS connections?" question posed by `install.py`). If you are setting `NGINX_SSL` to `false`, **make sure** you understand what you are doing and ensure that external connections cannot reach ports over which Malcolm will be communicating without encryption, including verifying your local firewall configuration. \ No newline at end of file +A third possibility is to use a third-party reverse proxy (e.g., [Traefik](https://doc.traefik.io/traefik/) or [Caddy](https://caddyserver.com/docs/quick-starts/reverse-proxy)) to handle the issuance of the certificates for you and to broker the connections between clients and Malcolm. Reverse proxies such as these often implement the [ACME](https://datatracker.ietf.org/doc/html/rfc8555) protocol for domain name authentication and can be used to request certificates from certificate authorities like [Let's Encrypt](https://letsencrypt.org/how-it-works/). In this configuration, the reverse proxy will be encrypting the connections instead of Malcolm, so you'll need to set the `NGINX_SSL` environment variable to `false` in [`nginx.env`](malcolm-config.md#MalcolmConfigEnvVars) (or answer `no` to the "Require encrypted HTTPS connections?" question posed by `./scripts/configure`). If you are setting `NGINX_SSL` to `false`, **make sure** you understand what you are doing and ensure that external connections cannot reach ports over which Malcolm will be communicating without encryption, including verifying your local firewall configuration. \ No newline at end of file diff --git a/docs/contributing-dashboards.md b/docs/contributing-dashboards.md index 6f53fb810..f819e7251 100644 --- a/docs/contributing-dashboards.md +++ b/docs/contributing-dashboards.md @@ -32,7 +32,7 @@ Visualizations and dashboards can be [easily created](dashboards.md#BuildDashboa } } ``` -1. Include the new dashboard either by using a [bind mount](contributing-local-modifications.md#Bind) for the `./dashboards./dashboards/` directory or by [rebuilding](development.md#Build) the `dashboards-helper` Docker image. Dashboards are imported the first time Malcolm starts up. +1. Include the new dashboard either by using a [bind mount](contributing-local-modifications.md#Bind) for the `./dashboards/dashboards/` directory or by [rebuilding](development.md#Build) the `dashboards-helper` Docker image. Dashboards are imported the first time Malcolm starts up. ## OpenSearch Dashboards plugins diff --git a/docs/contributing-local-modifications.md b/docs/contributing-local-modifications.md index e8cf2b563..afec0a841 100644 --- a/docs/contributing-local-modifications.md +++ b/docs/contributing-local-modifications.md @@ -1,6 +1,6 @@ # Local modifications -There are several ways to customize Malcolm's runtime behavior via local changes to configuration files. Many commonly-tweaked settings are discussed in the project [README](README.md) (see [`docker-compose.yml` parameters](malcolm-config.md#DockerComposeYml) and [Customizing event severity scoring](severity.md#SeverityConfig) for some examples). +There are several ways to customize Malcolm's runtime behavior via local changes to configuration files. Many commonly-tweaked settings are discussed in the project [README](README.md) (see [Environment Variable Files](malcolm-config.md#MalcolmConfigEnvVars) and [Customizing event severity scoring](severity.md#SeverityConfig) for some examples). ## Docker bind mounts @@ -8,93 +8,205 @@ Some configuration changes can be put in place by modifying local copies of conf ``` $ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml - opensearch: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro - - ./opensearch/opensearch.keystore:/usr/share/opensearch/config/opensearch.keystore:rw - - ./opensearch:/usr/share/opensearch/data:delegated - - ./opensearch-backup:/opt/opensearch/backup:delegated - dashboards-helper: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - dashboards: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - logstash: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro - - ./logstash/maps/malcolm_severity.yaml:/etc/malcolm_severity.yaml:ro - - ./logstash/certs/ca.crt:/certs/ca.crt:ro - - ./logstash/certs/server.crt:/certs/server.crt:ro - - ./logstash/certs/server.key:/certs/server.key:ro - filebeat: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - - ./zeek-logs:/zeek - - ./suricata-logs:/suricata - - ./filebeat/certs/ca.crt:/certs/ca.crt:ro - - ./filebeat/certs/client.crt:/certs/client.crt:ro - - ./filebeat/certs/client.key:/certs/client.key:ro - arkime: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - - ./pcap:/data/pcap - - ./arkime-logs:/opt/arkime/logs - - ./arkime-raw:/opt/arkime/raw - zeek: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./pcap:/pcap - - ./zeek-logs/upload:/zeek/upload - - ./zeek-logs/extract_files:/zeek/extract_files - - ./zeek/intel:/opt/zeek/share/zeek/site/intel - zeek-live: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./zeek-logs/live:/zeek/live - - ./zeek-logs/extract_files:/zeek/extract_files - - ./zeek/intel:/opt/zeek/share/zeek/site/intel - suricata: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./suricata-logs:/var/log/suricata - - ./pcap:/data/pcap - - ./suricata/rules:/opt/suricata/rules:ro - suricata-live: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./suricata-logs:/var/log/suricata - - ./suricata/rules:/opt/suricata/rules:ro - file-monitor: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./zeek-logs/extract_files:/zeek/extract_files - - ./zeek-logs/current:/zeek/logs - - ./yara/rules:/yara-rules/custom:ro - pcap-capture: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./pcap/upload:/pcap - pcap-monitor: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - - ./zeek-logs:/zeek - - ./pcap:/pcap - upload: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./pcap/upload:/var/www/upload/server/php/chroot/files - htadmin: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./htadmin/config.ini:/var/www/htadmin/config/config.ini:rw - - ./htadmin/metadata:/var/www/htadmin/config/metadata:rw - - ./nginx/htpasswd:/var/www/htadmin/auth/htpasswd:rw - freq: - - ./nginx/ca-trust:/var/local/ca-trust:ro - api: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - nginx-proxy: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./nginx/nginx_ldap.conf:/etc/nginx/nginx_ldap.conf:ro - - ./nginx/htpasswd:/etc/nginx/htpasswd:ro - - ./nginx/certs:/etc/nginx/certs:ro - - ./nginx/certs/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro +opensearch: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro + - ./opensearch:/usr/share/opensearch/data:delegated + - ./opensearch-backup:/opt/opensearch/backup:delegated + - ./opensearch/opensearch.keystore:/usr/share/opensearch/config/persist/opensearch.keystore:rw +dashboards-helper: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/dashboards-helper.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro +dashboards: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro +logstash: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/netbox-common.env + - ./config/netbox.env + - ./config/beats-common.env + - ./config/lookup-common.env + - ./config/logstash.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro + - ./logstash/maps/malcolm_severity.yaml:/etc/malcolm_severity.yaml:ro + - ./logstash/certs/ca.crt:/certs/ca.crt:ro + - ./logstash/certs/server.crt:/certs/server.crt:ro + - ./logstash/certs/server.key:/certs/server.key:ro +filebeat: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/upload-common.env + - ./config/nginx.env + - ./config/beats-common.env + - ./config/filebeat.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./zeek-logs:/zeek + - ./suricata-logs:/suricata + - ./filebeat/certs/ca.crt:/certs/ca.crt:ro + - ./filebeat/certs/client.crt:/certs/client.crt:ro + - ./filebeat/certs/client.key:/certs/client.key:ro +arkime: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/upload-common.env + - ./config/auth.env + - ./config/arkime.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./pcap:/data/pcap + - ./arkime-logs:/opt/arkime/logs + - ./arkime-raw:/opt/arkime/raw +zeek: + - ./config/process.env + - ./config/ssl.env + - ./config/upload-common.env + - ./config/zeek.env + - ./config/zeek-offline.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./pcap:/pcap + - ./zeek-logs/upload:/zeek/upload + - ./zeek-logs/extract_files:/zeek/extract_files + - ./zeek/intel:/opt/zeek/share/zeek/site/intel +zeek-live: + - ./config/process.env + - ./config/ssl.env + - ./config/upload-common.env + - ./config/pcap-capture.env + - ./config/zeek.env + - ./config/zeek-live.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./zeek-logs/live:/zeek/live + - ./zeek-logs/extract_files:/zeek/extract_files + - ./zeek/intel:/opt/zeek/share/zeek/site/intel +suricata: + - ./config/process.env + - ./config/ssl.env + - ./config/upload-common.env + - ./config/suricata.env + - ./config/suricata-offline.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./suricata-logs:/var/log/suricata + - ./pcap:/data/pcap + - ./suricata/rules:/opt/suricata/rules:ro +suricata-live: + - ./config/process.env + - ./config/ssl.env + - ./config/upload-common.env + - ./config/pcap-capture.env + - ./config/suricata.env + - ./config/suricata-live.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./suricata-logs:/var/log/suricata + - ./suricata/rules:/opt/suricata/rules:ro +file-monitor: + - ./config/process.env + - ./config/ssl.env + - ./config/zeek.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./zeek-logs/extract_files:/zeek/extract_files + - ./zeek-logs/current:/zeek/logs + - ./yara/rules:/yara-rules/custom:ro +pcap-capture: + - ./config/process.env + - ./config/ssl.env + - ./config/pcap-capture.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./pcap/upload:/pcap +pcap-monitor: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./config/upload-common.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro + - ./zeek-logs:/zeek + - ./pcap:/pcap +upload: + - ./config/process.env + - ./config/ssl.env + - ./config/auth.env + - ./config/upload.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./pcap/upload:/var/www/upload/server/php/chroot/files +htadmin: + - ./config/process.env + - ./config/ssl.env + - ./config/auth-common.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./htadmin/config.ini:/var/www/htadmin/config/config.ini:rw + - ./htadmin/metadata:/var/www/htadmin/config/metadata:rw + - ./nginx/htpasswd:/var/www/htadmin/auth/htpasswd:rw +freq: + - ./config/process.env + - ./config/ssl.env + - ./config/lookup-common.env + - ./nginx/ca-trust:/var/local/ca-trust:ro +netbox: + - ./config/process.env + - ./config/ssl.env + - ./config/netbox-common.env + - ./config/netbox.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./netbox/config/configuration:/etc/netbox/config:ro + - ./netbox/config/reports:/etc/netbox/reports:ro + - ./netbox/config/scripts:/etc/netbox/scripts:ro + - ./netbox/media:/opt/netbox/netbox/media:rw + - ./net-map.json:/usr/local/share/net-map.json:ro +netbox-postgres: + - ./config/process.env + - ./config/ssl.env + - ./config/netbox-common.env + - ./config/netbox-postgres.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./netbox/postgres:/var/lib/postgresql/data:rw +netbox-redis: + - ./config/process.env + - ./config/ssl.env + - ./config/netbox-common.env + - ./config/netbox-redis.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./netbox/redis:/data +netbox-redis-cache: + - ./config/process.env + - ./config/ssl.env + - ./config/netbox-common.env + - ./config/netbox-redis-cache.env + - ./nginx/ca-trust:/var/local/ca-trust:ro +api: + - ./config/process.env + - ./config/ssl.env + - ./config/opensearch.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro +nginx-proxy: + - ./config/process.env + - ./config/ssl.env + - ./config/auth-common.env + - ./config/nginx.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./nginx/nginx_ldap.conf:/etc/nginx/nginx_ldap.conf:ro + - ./nginx/htpasswd:/etc/nginx/auth/htpasswd:ro + - ./nginx/certs:/etc/nginx/certs:ro + - ./nginx/certs/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro ``` So, for example, if you wanted to make a change to the `nginx-proxy` container's `nginx.conf` file, you could add the following line to the `volumes:` section of the `nginx-proxy` service in your `docker-compose.yml` file: diff --git a/docs/contributing-logstash.md b/docs/contributing-logstash.md index d13c56b34..a628210ac 100644 --- a/docs/contributing-logstash.md +++ b/docs/contributing-logstash.md @@ -28,7 +28,7 @@ Logstash can then be easily extended to add more [`logstash/pipelines`]({{ site. So, in order to add a new **parse pipeline** for `cooltool` after tweaking [`filebeat.yml`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/filebeat/filebeat.yml) as described above, create a `cooltool` directory under [`logstash/pipelines`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/logstash/pipelines) which follows the same pattern as the `zeek` parse pipeline. This directory will have an input file (tiny), a filter file (possibly large), and an output file (tiny). In your filter file, be sure to set the field [`event.hash`](https://www.elastic.co/guide/en/ecs/master/ecs-event.html#field-event-hash) to a unique value to identify indexed documents in OpenSearch; the [fingerprint filter](https://www.elastic.co/guide/en/logstash/current/plugins-filters-fingerprint.html) may be useful for this. -Finally, in your `docker-compose` files, set a new `LOGSTASH_PARSE_PIPELINE_ADDRESSES` environment variable under `logstash-variables` to `cooltool-parse,zeek-parse,suricata-parse,beats-parse` (assuming you named the pipeline address from the previous step `cooltool-parse`) so that logs sent from `filebeat` to `logstash` are forwarded to all parse pipelines. +Finally, in the [`./config/logstash.env` file](malcolm-config.md#MalcolmConfigEnvVars), set a new `LOGSTASH_PARSE_PIPELINE_ADDRESSES` environment variable to `cooltool-parse,zeek-parse,suricata-parse,beats-parse` (assuming you named the pipeline address from the previous step `cooltool-parse`) so that logs sent from `filebeat` to `logstash` are forwarded to all parse pipelines. ## Parsing new Zeek logs diff --git a/docs/contributing-zeek.md b/docs/contributing-zeek.md index db8bfc935..6846bbb46 100644 --- a/docs/contributing-zeek.md +++ b/docs/contributing-zeek.md @@ -2,7 +2,7 @@ ## `local.zeek` -Some Zeek behavior can be tweaked without having to manually edit configuration files through the use of environment variables: search for `ZEEK` in the [`docker-compose.yml` parameters](malcolm-config.md#DockerComposeYml) section of the documentation. +Some Zeek behavior can be tweaked through the use of [environment variables](malcolm-config.md#MalcolmConfigEnvVars) in the `.env` files beginning with `zeek…`. Other changes to Zeek's behavior could be made by modifying [local.zeek]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/zeek/config/local.zeek) and either using a [bind mount](contributing-local-modifications.md#Bind) or [rebuilding](development.md#Build) the `zeek` Docker image with the modification. See the [Zeek documentation](https://docs.zeek.org/en/master/quickstart.html#local-site-customization) for more information on customizing a Zeek instance. Note that changing Zeek's behavior could result in changes to the format of the logs Zeek generates, which could break Malcolm's parsing of those logs, so exercise caution. diff --git a/docs/file-scanning.md b/docs/file-scanning.md index 6d6396a9e..7847bb144 100644 --- a/docs/file-scanning.md +++ b/docs/file-scanning.md @@ -1,6 +1,6 @@ # Automatic file extraction and scanning -Malcolm can leverage Zeek's knowledge of network protocols to automatically detect file transfers and extract those files from PCAPs as Zeek processes them. This behavior can be enabled globally by modifying the `ZEEK_EXTRACTOR_MODE` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml), or on a per-upload basis for PCAP files uploaded via the [browser-based upload form](upload.md#Upload) when **Analyze with Zeek** is selected. +Malcolm can leverage Zeek's knowledge of network protocols to automatically detect file transfers and extract those files from PCAPs as Zeek processes them. This behavior can be enabled globally by modifying the `ZEEK_EXTRACTOR_MODE` [variable in `zeek.env`](malcolm-config.md#MalcolmConfigEnvVars), or on a per-upload basis for PCAP files uploaded via the [browser-based upload form](upload.md#Upload) when **Analyze with Zeek** is selected. To specify which files should be extracted, the following values are acceptable in `ZEEK_EXTRACTOR_MODE`: @@ -12,17 +12,17 @@ To specify which files should be extracted, the following values are acceptable Extracted files can be examined through any of the following methods: -* submitting file hashes to [**VirusTotal**](https://www.virustotal.com/en/#search); to enable this method, specify the `VTOT_API2_KEY` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) -* scanning files with [**ClamAV**](https://www.clamav.net/); to enable this method, set the `EXTRACTED_FILE_ENABLE_CLAMAV` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) to `true` -* scanning files with [**Yara**](https://github.com/VirusTotal/yara); to enable this method, set the `EXTRACTED_FILE_ENABLE_YARA` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) to `true` -* scanning PE (portable executable) files with [**Capa**](https://github.com/fireeye/capa); to enable this method, set the `EXTRACTED_FILE_ENABLE_CAPA` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) to `true` +* submitting file hashes to [**VirusTotal**](https://www.virustotal.com/en/#search); to enable this method, specify the `VTOT_API2_KEY` [environment variable in `zeek.env`](malcolm-config.md#MalcolmConfigEnvVars) +* scanning files with [**ClamAV**](https://www.clamav.net/); to enable this method, set the `EXTRACTED_FILE_ENABLE_CLAMAV` [environment variable in `zeek.env`](malcolm-config.md#MalcolmConfigEnvVars) to `true` +* scanning files with [**Yara**](https://github.com/VirusTotal/yara); to enable this method, set the `EXTRACTED_FILE_ENABLE_YARA` [environment variable in `zeek.env`](malcolm-config.md#MalcolmConfigEnvVars) to `true` +* scanning PE (portable executable) files with [**Capa**](https://github.com/fireeye/capa); to enable this method, set the `EXTRACTED_FILE_ENABLE_CAPA` [environment variable in `zeek.env`](malcolm-config.md#MalcolmConfigEnvVars) to `true` Files which are flagged via any of these methods will be logged as Zeek `signatures.log` entries, and can be viewed in the **Signatures** dashboard in OpenSearch Dashboards. -The `EXTRACTED_FILE_PRESERVATION` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) determines the behavior for preservation of Zeek-extracted files: +The `EXTRACTED_FILE_PRESERVATION` [environment variable in `zeek.env`](malcolm-config.md#MalcolmConfigEnvVars) determines the behavior for preservation of Zeek-extracted files: * `quarantined`: preserve only flagged files in `./zeek-logs/extract_files/quarantine` * `all`: preserve flagged files in `./zeek-logs/extract_files/quarantine` and all other extracted files in `./zeek-logs/extract_files/preserved` * `none`: preserve no extracted files -The `EXTRACTED_FILE_HTTP_SERVER_…` [environment variables in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) configure access to the Zeek-extracted files path through the means of a simple HTTPS directory server. Beware that Zeek-extracted files may contain malware. As such, the files may be optionally encrypted upon download. \ No newline at end of file +The `EXTRACTED_FILE_HTTP_SERVER_…` [environment variables in `zeek.env`](malcolm-config.md#MalcolmConfigEnvVars) configure access to the Zeek-extracted files path through the means of a simple HTTPS directory server. Beware that Zeek-extracted files may contain malware. As such, the files may be optionally encrypted upon download (and decrypted using `openssl`, e.g., `openssl enc -aes-256-cbc -d -in example.exe.encrypted -out example.exe`) diff --git a/docs/host-config-windows.md b/docs/host-config-windows.md index c024bf79d..9545bea36 100644 --- a/docs/host-config-windows.md +++ b/docs/host-config-windows.md @@ -13,4 +13,4 @@ Installing and configuring [Docker to run under Windows](https://docs.docker.com ## Finish Malcolm's configuration -Once Docker is installed, configured and running as described in the previous section, run [`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning) to finish configuration of the local Malcolm installation. Malcolm will be controlled and run from within your WSL distribution's terminal environment. \ No newline at end of file +Once Docker is installed, configured and running as described in the previous section, run [`./scripts/configure`](malcolm-config.md#ConfigAndTuning) to finish configuration of the local Malcolm installation. Malcolm will be controlled and run from within your WSL distribution's terminal environment. \ No newline at end of file diff --git a/docs/ics-best-guess.md b/docs/ics-best-guess.md index 08f5bb0c9..4e7ee6a6e 100644 --- a/docs/ics-best-guess.md +++ b/docs/ics-best-guess.md @@ -8,4 +8,4 @@ Naturally, these lookups could produce false positives, so these connections are ![](./images/screenshots/dashboards_bestguess.png) -This feature is disabled by default, but it can be enabled by clearing (setting to `''`) the value of the `ZEEK_DISABLE_BEST_GUESS_ICS` environment variable in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml). \ No newline at end of file +This feature is disabled by default, but it can be enabled by clearing (setting to `''`) the value of the `ZEEK_DISABLE_BEST_GUESS_ICS` [environment variable in `zeek.env`](malcolm-config.md#MalcolmConfigEnvVars). \ No newline at end of file diff --git a/docs/index-management.md b/docs/index-management.md index 2277fbd07..19441fb4f 100644 --- a/docs/index-management.md +++ b/docs/index-management.md @@ -2,6 +2,6 @@ Malcolm releases prior to v6.2.0 used environment variables to configure OpenSearch [Index State Management](https://opensearch.org/docs/latest/im-plugin/ism/index/) [policies](https://opensearch.org/docs/latest/im-plugin/ism/policies/). -Since then, OpenSearch Dashboards has developed and released plugins with UIs for [Index State Management](https://opensearch.org/docs/latest/im-plugin/ism/index/) and [Snapshot Management](https://opensearch.org/docs/latest/opensearch/snapshots/sm-dashboards/). Because these plugins provide a more comprehensive and user-friendly interfaces for these features, the old environment variable-based configuration code has been removed from Malcolm, with the exception of the code that uses `OPENSEARCH_INDEX_SIZE_PRUNE_LIMIT` and `OPENSEARCH_INDEX_SIZE_PRUNE_NAME_SORT` which deals with deleting the oldest network session metadata indices when the database exceeds a certain size. +Since then, OpenSearch Dashboards has developed and released plugins with UIs for [Index State Management](https://opensearch.org/docs/latest/im-plugin/ism/index/) and [Snapshot Management](https://opensearch.org/docs/latest/opensearch/snapshots/sm-dashboards/). Because these plugins provide a more comprehensive and user-friendly interfaces for these features, the old environment variable-based configuration code has been removed from Malcolm, with the exception of the code that uses the `OPENSEARCH_INDEX_SIZE_PRUNE_LIMIT` and `OPENSEARCH_INDEX_SIZE_PRUNE_NAME_SORT` [variables in `dashboards-helper.env`](malcolm-config.md#MalcolmConfigEnvVars) and which deals with deleting the oldest network session metadata indices when the database exceeds a certain size. -Note that OpenSearch index state management and snapshot management only deals with disk space consumed by OpenSearch indices: it does not have anything to do with PCAP file storage. The `MANAGE_PCAP_FILES` environment variable in the [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) file can be used to allow Arkime to prune old PCAP files based on available disk space. \ No newline at end of file +Note that OpenSearch index state management and snapshot management only deals with disk space consumed by OpenSearch indices: it does not have anything to do with PCAP file storage. The `MANAGE_PCAP_FILES` environment variable in the [`arkime.env` file](malcolm-config.md#MalcolmConfigEnvVars) can be used to allow Arkime to prune old PCAP files based on available disk space. \ No newline at end of file diff --git a/docs/live-analysis.md b/docs/live-analysis.md index a43e161f4..d08702b5b 100644 --- a/docs/live-analysis.md +++ b/docs/live-analysis.md @@ -18,19 +18,19 @@ Please see the [Hedgehog Linux README](hedgehog.md) for more information. ## Monitoring local network interfaces -Malcolm's `pcap-capture`, `suricata-live` and `zeek-live` containers can monitor one or more local network interfaces, specified by the `PCAP_IFACE` environment variable in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml). These containers are started with additional privileges (`IPC_LOCK`, `NET_ADMIN`, `NET_RAW`, and `SYS_ADMIN`) to allow opening network interfaces in promiscuous mode for capture. +Malcolm's `pcap-capture`, `suricata-live` and `zeek-live` containers can monitor one or more local network interfaces, specified by the `PCAP_IFACE` environment variable in [`pcap-capture.env`](malcolm-config.md#MalcolmConfigEnvVars). These containers are started with additional privileges (`IPC_LOCK`, `NET_ADMIN`, `NET_RAW`, and `SYS_ADMIN`) to allow opening network interfaces in promiscuous mode for capture. -The instances of Zeek and Suricata (in the `suricata-live` and `zeek-live` containers when the `SURICATA_LIVE_CAPTURE` and `ZEEK_LIVE_CAPTURE` environment variables in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) are set to `true`, respectively) analyze traffic on-the-fly and generate log files containing network session metadata. These log files are in turn scanned by Filebeat and forwarded to Logstash for enrichment and indexing into the OpenSearch document store. +The instances of Zeek and Suricata (in the `suricata-live` and `zeek-live` containers when the `SURICATA_LIVE_CAPTURE` and `ZEEK_LIVE_CAPTURE` [environment variables](malcolm-config.md#MalcolmConfigEnvVars) are set to `true`, respectively) analyze traffic on-the-fly and generate log files containing network session metadata. These log files are in turn scanned by Filebeat and forwarded to Logstash for enrichment and indexing into the OpenSearch document store. -In contrast, the `pcap-capture` container buffers traffic to PCAP files and periodically rotates these files for processing (by Arkime's `capture` utlity in the `arkime` container) according to the thresholds defined by the `PCAP_ROTATE_MEGABYTES` and `PCAP_ROTATE_MINUTES` environment variables in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml). If for some reason (e.g., a low resources environment) you also want Zeek and Suricata to process these intermediate PCAP files rather than monitoring the network interfaces directly, you can set `SURICATA_ROTATED_PCAP`/`ZEEK_ROTATED_PCAP` to `true` and `SURICATA_LIVE_CAPTURE`/`ZEEK_LIVE_CAPTURE` to false. +In contrast, the `pcap-capture` container buffers traffic to PCAP files and periodically rotates these files for processing (by Arkime's `capture` utlity in the `arkime` container) according to the thresholds defined by the `PCAP_ROTATE_MEGABYTES` and `PCAP_ROTATE_MINUTES` environment variables in [`pcap-capture.env`](malcolm-config.md#MalcolmConfigEnvVars). If for some reason (e.g., a low resources environment) you also want Zeek and Suricata to process these intermediate PCAP files rather than monitoring the network interfaces directly, you can set `SURICATA_ROTATED_PCAP`/`ZEEK_ROTATED_PCAP` to `true` and `SURICATA_LIVE_CAPTURE`/`ZEEK_LIVE_CAPTURE` to false. -These various options for monitoring traffic on local network interfaces can also be configured by running [`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning). +These various options for monitoring traffic on local network interfaces can also be configured by running [`./scripts/configure`](malcolm-config.md#ConfigAndTuning). Note that currently Microsoft Windows and Apple macOS platforms run Docker inside of a virtualized environment. Live traffic capture and analysis on those platforms would require additional configuration of virtual interfaces and port forwarding in Docker which is outside of the scope of this document. ## Manually forwarding logs from an external source -Malcolm's Logstash instance can also be configured to accept logs from a [remote forwarder](https://www.elastic.co/products/beats/filebeat) by running [`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning) and answering "yes" to "`Expose Logstash port to external hosts?`." Enabling encrypted transport of these logs files is discussed in [Configure authentication](authsetup.md#AuthSetup) and the description of the `BEATS_SSL` environment variable in the [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) file. +Malcolm's Logstash instance can also be configured to accept logs from a [remote forwarder](https://www.elastic.co/products/beats/filebeat) by running [`./scripts/configure`](malcolm-config.md#ConfigAndTuning) and answering "yes" to "`Expose Logstash port to external hosts?`." Enabling encrypted transport of these logs files is discussed in [Configure authentication](authsetup.md#AuthSetup) and the description of the `BEATS_SSL` environment variable in [`beats-common.env`](malcolm-config.md#MalcolmConfigEnvVars). Configuring Filebeat to forward Zeek logs to Malcolm might look something like this example [`filebeat.yml`](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-reference-yml.html): ``` diff --git a/docs/malcolm-config.md b/docs/malcolm-config.md index 7817a1419..0fa5d4789 100644 --- a/docs/malcolm-config.md +++ b/docs/malcolm-config.md @@ -1,78 +1,92 @@ # Malcolm Configuration -If you already have Docker and Docker Compose installed, the `install.py` script can still help you tune system configuration and `docker-compose.yml` parameters for Malcolm. To run it in "configuration only" mode, bypassing the steps to install Docker and Docker Compose, run it like this: -``` -./scripts/install.py --configure -``` +Malcolm's runtime settings are stored (with a few exceptions) as environment variables in configuration files ending with a `.env` suffix in the `./config` directory. The `./scripts/configure` script can help you configure and tune these settings. -Although `install.py` will attempt to automate many of the following configuration and tuning parameters, they are nonetheless listed in the following sections for reference: +Run `./scripts/configure` and answer the questions to configure Malcolm. For an in-depth treatment of these configuration questions, see the **Configuration** section in **[End-to-end Malcolm and Hedgehog Linux ISO Installation](malcolm-hedgehog-e2e-iso-install.md#MalcolmConfig)**. -## `docker-compose.yml` parameters +## Environment Variable Files -Edit `docker-compose.yml` and search for the `OPENSEARCH_JAVA_OPTS` key. Edit the `-Xms4g -Xmx4g` values, replacing `4g` with a number that is half of your total system memory, or just under 32 gigabytes, whichever is less. So, for example, if I had 64 gigabytes of memory I would edit those values to be `-Xms31g -Xmx31g`. This indicates how much memory can be allocated to the OpenSearch heaps. For a pleasant experience, I would suggest not using a value under 10 gigabytes. Similar values can be modified for Logstash with `LS_JAVA_OPTS`, where using 3 or 4 gigabytes is recommended. +Although the configuration script automates many of the following configuration and tuning parameters, some environment variables of particular interest are listed here for reference. -Various other environment variables inside of `docker-compose.yml` can be tweaked to control aspects of how Malcolm behaves, particularly with regards to processing PCAP files and Zeek logs. The environment variables of particular interest are located near the top of that file under **Commonly tweaked configuration options**, which include: - -* `ARKIME_ANALYZE_PCAP_THREADS` – the number of threads available to Arkime for analyzing PCAP files (default `1`) -* `AUTO_TAG` – if set to `true`, Malcolm will automatically create Arkime sessions and Zeek logs with tags based on the filename, as described in [Tagging](upload.md#Tagging) (default `true`) -* `BEATS_SSL` – if set to `true`, Logstash will use require encrypted communications for any external [Beats](https://www.elastic.co/guide/en/logstash/current/plugins-inputs-beats.html)-based forwarders from which it will accept logs (default `true`) -* `CONNECTION_SECONDS_SEVERITY_THRESHOLD` - when [severity scoring](severity.md#Severity) is enabled, this variable indicates the duration threshold (in seconds) for assigning severity to long connections (default `3600`) -* `DASHBOARDS_DARKMODE` – if set to `true`, [OpenSearch Dashboards](dashboards.md#DashboardsVisualizations) will be set to dark mode upon initialization (default `true`) -* `EXTRACTED_FILE_CAPA_VERBOSE` – if set to `true`, all Capa rule hits will be logged; otherwise (`false`) only [MITRE ATT&CK® technique](https://attack.mitre.org/techniques) classifications will be logged -* `EXTRACTED_FILE_ENABLE_CAPA` – if set to `true`, [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) that are determined to be PE (portable executable) files will be scanned with [Capa](https://github.com/fireeye/capa) -* `EXTRACTED_FILE_ENABLE_CLAMAV` – if set to `true`, [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) will be scanned with [ClamAV](https://www.clamav.net/) -* `EXTRACTED_FILE_ENABLE_YARA` – if set to `true`, [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) will be scanned with [Yara](https://github.com/VirusTotal/yara) -* `EXTRACTED_FILE_HTTP_SERVER_ENABLE` – if set to `true`, the directory containing [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) will be served over HTTP at `./extracted-files/` (e.g., [https://localhost/extracted-files/](https://localhost/extracted-files/) if you are connecting locally) -* `EXTRACTED_FILE_HTTP_SERVER_ENCRYPT` – if set to `true`, those Zeek-extracted files will be AES-256-CBC-encrypted in an `openssl enc`-compatible format (e.g., `openssl enc -aes-256-cbc -d -in example.exe.encrypted -out example.exe`) -* `EXTRACTED_FILE_HTTP_SERVER_KEY` – specifies the AES-256-CBC decryption password for encrypted Zeek-extracted files; used in conjunction with `EXTRACTED_FILE_HTTP_SERVER_ENCRYPT` -* `EXTRACTED_FILE_IGNORE_EXISTING` – if set to `true`, files extant in `./zeek-logs/extract_files/` directory will be ignored on startup rather than scanned -* `EXTRACTED_FILE_PRESERVATION` – determines behavior for preservation of [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) -* `EXTRACTED_FILE_UPDATE_RULES` – if set to `true`, file scanner engines (e.g., ClamAV, Capa, Yara) will periodically update their rule definitions (default `false`) -* `EXTRACTED_FILE_YARA_CUSTOM_ONLY` – if set to `true`, Malcolm will bypass the default Yara rulesets ([Neo23x0/signature-base](https://github.com/Neo23x0/signature-base) and [bartblaze/Yara-rules](https://github.com/bartblaze/Yara-rules)) and use only user-defined rules in `./yara/rules` -* `FREQ_LOOKUP` - if set to `true`, domain names (from DNS queries and SSL server names) will be assigned entropy scores as calculated by [`freq`](https://github.com/MarkBaggett/freq) (default `false`) -* `FREQ_SEVERITY_THRESHOLD` - when [severity scoring](severity.md#Severity) is enabled, this variable indicates the entropy threshold for assigning severity to events with entropy scores calculated by [`freq`](https://github.com/MarkBaggett/freq); a lower value will only assign severity scores to fewer domain names with higher entropy (e.g., `2.0` for `NQZHTFHRMYMTVBQJE.COM`), while a higher value will assign severity scores to more domain names with lower entropy (e.g., `7.5` for `naturallanguagedomain.example.org`) (default `2.0`) -* `LOGSTASH_OUI_LOOKUP` – if set to `true`, Logstash will map MAC addresses to vendors for all source and destination MAC addresses when analyzing Zeek logs (default `true`) -* `LOGSTASH_REVERSE_DNS` – if set to `true`, Logstash will perform a reverse DNS lookup for all external source and destination IP address values when analyzing Zeek logs (default `false`) -* `LOGSTASH_SEVERITY_SCORING` - if set to `true`, Logstash will perform [severity scoring](severity.md#Severity) when analyzing Zeek logs (default `true`) -* `LOGSTASH_NETBOX_ENRICHMENT` - if set to `true`, Logstash will enrich network traffic metadata via NetBox API calls -* `MANAGE_PCAP_FILES` – if set to `true`, all PCAP files imported into Malcolm will be marked as available for deletion by Arkime if available storage space becomes too low (default `false`) -* `MAXMIND_GEOIP_DB_LICENSE_KEY` - Malcolm uses MaxMind's free GeoLite2 databases for GeoIP lookups. As of December 30, 2019, these databases are [no longer available](https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-geolite2-databases/) for download via a public URL. Instead, they must be downloaded using a MaxMind license key (available without charge [from MaxMind](https://www.maxmind.com/en/geolite2/signup)). The license key can be specified here for GeoIP database downloads during build- and run-time. -* `OPENSEARCH_LOCAL` - if set to `true`, Malcolm will use its own internal [OpenSearch instance](opensearch-instances.md#OpenSearchInstance) (default `true`) -* `OPENSEARCH_URL` - when using Malcolm's internal OpenSearch instance (i.e., `OPENSEARCH_LOCAL` is `true`) this should be `http://opensearch:9200`, otherwise this value specifies the primary remote instance URL in the format `protocol://host:port` (default `http://opensearch:9200`) -* `OPENSEARCH_SSL_CERTIFICATE_VERIFICATION` - if set to `true`, connections to the primary remote OpenSearch instance will require full TLS certificate validation (this may fail if using self-signed certificates) (default `false`) -* `OPENSEARCH_SECONDARY` - if set to `true`, Malcolm will forward logs to a secondary remote OpenSearch instance in addition to the primary (local or remote) OpenSearch instance (default `false`) -* `OPENSEARCH_SECONDARY_URL` - when forwarding to a secondary remote OpenSearch instance (i.e., `OPENSEARCH_SECONDARY` is `true`) this value specifies the secondary remote instance URL in the format `protocol://host:port` -* `OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION` - if set to `true`, connections to the secondary remote OpenSearch instance will require full TLS certificate validation (this may fail if using self-signed certificates) (default `false`) -* `NETBOX_DISABLED` - if set to `true`, Malcolm will **not** start and manage a [NetBox](asset-interaction-analysis.md#AssetInteractionAnalysis) instance (default `true`) -* `NGINX_BASIC_AUTH` - if set to `true`, use [TLS-encrypted HTTP basic](authsetup.md#AuthBasicAccountManagement) authentication (default); if set to `false`, use [Lightweight Directory Access Protocol (LDAP)](authsetup.md#AuthLDAP) authentication -* `NGINX_LOG_ACCESS_AND_ERRORS` - if set to `true`, all access to Malcolm via its [web interfaces](quickstart.md#UserInterfaceURLs) will be logged to OpenSearch (default `false`) -* `NGINX_SSL` - if set to `true`, require HTTPS connections to Malcolm's `nginx-proxy` container (default); if set to `false`, use unencrypted HTTP connections (using unsecured HTTP connections is **NOT** recommended unless you are running Malcolm behind another reverse proxy like Traefik, Caddy, etc.) -* `PCAP_ENABLE_NETSNIFF` – if set to `true`, Malcolm will capture network traffic on the local network interface(s) indicated in `PCAP_IFACE` using [netsniff-ng](http://netsniff-ng.org/) -* `PCAP_ENABLE_TCPDUMP` – if set to `true`, Malcolm will capture network traffic on the local network interface(s) indicated in `PCAP_IFACE` using [tcpdump](https://www.tcpdump.org/); there is no reason to enable *both* `PCAP_ENABLE_NETSNIFF` and `PCAP_ENABLE_TCPDUMP` -* `PCAP_FILTER` – specifies a tcpdump-style filter expression for local packet capture; leave blank to capture all traffic -* `PCAP_IFACE` – used to specify the network interface(s) for local packet capture if `PCAP_ENABLE_NETSNIFF`, `PCAP_ENABLE_TCPDUMP`, `ZEEK_LIVE_CAPTURE` or `SURICATA_LIVE_CAPTURE` are enabled; for multiple interfaces, separate the interface names with a comma (e.g., `'enp0s25'` or `'enp10s0,enp11s0'`) -* `PCAP_IFACE_TWEAK` - if set to `true`, Malcolm will [use `ethtool`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/nic-capture-setup.sh) to disable NIC hardware offloading features and adjust ring buffer sizes for capture interface(s); this should be `true` if the interface(s) are being used for capture only, `false` if they are being used for management/communication -* `PCAP_ROTATE_MEGABYTES` – used to specify how large a locally-captured PCAP file can become (in megabytes) before it is closed for processing and a new PCAP file created -* `PCAP_ROTATE_MINUTES` – used to specify a time interval (in minutes) after which a locally-captured PCAP file will be closed for processing and a new PCAP file created -* `pipeline.workers`, `pipeline.batch.size` and `pipeline.batch.delay` - these settings are used to tune the performance and resource utilization of the the `logstash` container; see [Tuning and Profiling Logstash Performance](https://www.elastic.co/guide/en/logstash/current/tuning-logstash.html), [`logstash.yml`](https://www.elastic.co/guide/en/logstash/current/logstash-settings-file.html) and [Multiple Pipelines](https://www.elastic.co/guide/en/logstash/current/multiple-pipelines.html) -* `PUID` and `PGID` - Docker runs all of its containers as the privileged `root` user by default. For better security, Malcolm immediately drops to non-privileged user accounts for executing internal processes wherever possible. The `PUID` (**p**rocess **u**ser **ID**) and `PGID` (**p**rocess **g**roup **ID**) environment variables allow Malcolm to map internal non-privileged user accounts to a corresponding [user account](https://en.wikipedia.org/wiki/User_identifier) on the host. Note that a few containers (including the `logstash` and `netbox` containers) may take a few extra minutes during startup if `PUID` and `PGID` are set to values other than the default `1000`. This is expected and should not affect operation after the initial startup. -* `SENSITIVE_COUNTRY_CODES` - when [severity scoring](severity.md#Severity) is enabled, this variable defines a comma-separated list of sensitive countries (using [ISO 3166-1 alpha-2 codes](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Current_codes)) (default `'AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ'`, taken from the U.S. Department of Energy Sensitive Country List) -* `SURICATA_AUTO_ANALYZE_PCAP_FILES` – if set to `true`, all PCAP files imported into Malcolm will automatically be analyzed by Suricata, and the resulting logs will also be imported (default `false`) -* `SURICATA_AUTO_ANALYZE_PCAP_THREADS` – the number of threads available to Malcolm for analyzing Suricata logs (default `1`) -* `SURICATA_CUSTOM_RULES_ONLY` – if set to `true`, Malcolm will bypass the default [Suricata ruleset](https://github.com/OISF/suricata/tree/master/rules) and use only user-defined rules (`./suricata/rules/*.rules`). -* `SURICATA_UPDATE_RULES` – if set to `true`, Suricata signatures will periodically be updated (default `false`) -* `SURICATA_LIVE_CAPTURE` - if set to `true`, Suricata will monitor live traffic on the local interface(s) defined by `PCAP_FILTER` -* `SURICATA_ROTATED_PCAP` - if set to `true`, Suricata can analyze captured PCAP files captured by `netsniff-ng` or `tcpdump` (see `PCAP_ENABLE_NETSNIFF` and `PCAP_ENABLE_TCPDUMP`, as well as `SURICATA_AUTO_ANALYZE_PCAP_FILES`); if `SURICATA_LIVE_CAPTURE` is `true`, this should be false, otherwise Suricata will see duplicate traffic -* `SURICATA_…` - the [`suricata` container entrypoint script]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/suricata_config_populate.py) can use **many** more environment variables to tweak [suricata.yaml](https://github.com/OISF/suricata/blob/master/suricata.yaml.in); in that script, `DEFAULT_VARS` defines those variables (albeit without the `SURICATA_` prefix you must add to each for use) -* `TOTAL_MEGABYTES_SEVERITY_THRESHOLD` - when [severity scoring](severity.md#Severity) is enabled, this variable indicates the size threshold (in megabytes) for assigning severity to large connections or file transfers (default `1000`) -* `VTOT_API2_KEY` – used to specify a [VirusTotal Public API v.20](https://www.virustotal.com/en/documentation/public-api/) key, which, if specified, will be used to submit hashes of [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) to VirusTotal -* `ZEEK_AUTO_ANALYZE_PCAP_FILES` – if set to `true`, all PCAP files imported into Malcolm will automatically be analyzed by Zeek, and the resulting logs will also be imported (default `false`) -* `ZEEK_AUTO_ANALYZE_PCAP_THREADS` – the number of threads available to Malcolm for analyzing Zeek logs (default `1`) -* `ZEEK_DISABLE_…` - if set to any non-blank value, each of these variables can be used to disable a certain Zeek function when it analyzes PCAP files (for example, setting `ZEEK_DISABLE_LOG_PASSWORDS` to `true` to disable logging of cleartext passwords) -* `ZEEK_DISABLE_BEST_GUESS_ICS` - see ["Best Guess" Fingerprinting for ICS Protocols](ics-best-guess.md#ICSBestGuess) -* `ZEEK_EXTRACTOR_MODE` – determines the file extraction behavior for file transfers detected by Zeek; see [Automatic file extraction and scanning](file-scanning.md#ZeekFileExtraction) for more details -* `ZEEK_INTEL_FEED_SINCE` - when querying a [TAXII](zeek-intel.md#ZeekIntelSTIX) or [MISP](zeek-intel.md#ZeekIntelMISP) feed, only process threat indicators that have been created or modified since the time represented by this value; it may be either a fixed date/time (`01/01/2021`) or relative interval (`30 days ago`) -* `ZEEK_INTEL_ITEM_EXPIRATION` - specifies the value for Zeek's [`Intel::item_expiration`](https://docs.zeek.org/en/current/scripts/base/frameworks/intel/main.zeek.html#id-Intel::item_expiration) timeout as used by the [Zeek Intelligence Framework](zeek-intel.md#ZeekIntel) (default `-1min`, which disables item expiration) -* `ZEEK_INTEL_REFRESH_CRON_EXPRESSION` - specifies a [cron expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) indicating the refresh interval for generating the [Zeek Intelligence Framework](zeek-intel.md#ZeekIntel) files (defaults to empty, which disables automatic refresh) -* `ZEEK_LIVE_CAPTURE` - if set to `true`, Zeek will monitor live traffic on the local interface(s) defined by `PCAP_FILTER` -* `ZEEK_ROTATED_PCAP` - if set to `true`, Zeek can analyze captured PCAP files captured by `netsniff-ng` or `tcpdump` (see `PCAP_ENABLE_NETSNIFF` and `PCAP_ENABLE_TCPDUMP`, as well as `ZEEK_AUTO_ANALYZE_PCAP_FILES`); if `ZEEK_LIVE_CAPTURE` is `true`, this should be false, otherwise Zeek will see duplicate traffic \ No newline at end of file +* **`arkime.env`** - settings for [Arkime](https://arkime.com/) + - `ARKIME_ANALYZE_PCAP_THREADS` – the number of threads available to Arkime for analyzing PCAP files (default `1`) + - `MANAGE_PCAP_FILES` – if set to `true`, all PCAP files imported into Malcolm will be marked as available for deletion by Arkime if available storage space becomes too low (default `false`) + - `MAXMIND_GEOIP_DB_LICENSE_KEY` - Malcolm uses MaxMind's free GeoLite2 databases for GeoIP lookups. As of December 30, 2019, these databases are [no longer available](https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-geolite2-databases/) for download via a public URL. Instead, they must be downloaded using a MaxMind license key (available without charge [from MaxMind](https://www.maxmind.com/en/geolite2/signup)). The license key can be specified here for GeoIP database downloads during build- and run-time. +* **`auth-common.env`** - [authentication](#MalcolmAuthSetup)-related settings + - `NGINX_BASIC_AUTH` - if set to `true`, use [TLS-encrypted HTTP basic](authsetup.md#AuthBasicAccountManagement) authentication (default); if set to `false`, use [Lightweight Directory Access Protocol (LDAP)](authsetup.md#AuthLDAP) authentication +* **`auth.env`** - stores the Malcolm administrator's username and password hash for its nginx reverse proxy +* **`beats-common.env`** - settings for interactions between [Logstash](https://www.elastic.co/products/logstash) and [Filebeat](https://www.elastic.co/products/beats/filebeat) + - `BEATS_SSL` – if set to `true`, Logstash will use require encrypted communications for any external [Beats](https://www.elastic.co/guide/en/logstash/current/plugins-inputs-beats.html)-based forwarders from which it will accept logs (default `true`) +* **`dashboards-helper.env`** - settings for the container that helps configure and maintain [OpenSearch](https://opensearch.org/) and [OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/index/) + - `DASHBOARDS_DARKMODE` – if set to `true`, [OpenSearch Dashboards](dashboards.md#DashboardsVisualizations) will be set to dark mode upon initialization (default `true`) +* **`filebeat.env`** - settings specific to [Filebeat](https://www.elastic.co/products/beats/filebeat), particularly for how Filebeat watches for new log files to parse and how it receives and stores [third-Party logs](third-party-logs.md#ThirdPartyLogs) +* **`logstash.env`** - settings specific to [Logstash](https://www.elastic.co/products/logstash) + - `LOGSTASH_OUI_LOOKUP` – if set to `true`, Logstash will map MAC addresses to vendors for all source and destination MAC addresses when analyzing Zeek logs (default `true`) + - `LOGSTASH_REVERSE_DNS` – if set to `true`, Logstash will perform a reverse DNS lookup for all external source and destination IP address values when analyzing Zeek logs (default `false`) + - `LOGSTASH_SEVERITY_SCORING` - if set to `true`, Logstash will perform [severity scoring](severity.md#Severity) when analyzing Zeek logs (default `true`) + - `LOGSTASH_NETBOX_ENRICHMENT` - if set to `true`, Logstash will enrich network traffic metadata via NetBox API calls + - `LS_JAVA_OPTS` - part of LogStash's [JVM settings](https://www.elastic.co/guide/en/logstash/current/jvm-settings.html), the `-Xms` and `-Xmx` values set the size of LogStash's Java heap (we recommend somewhere between `1500m` and `4g`) + * `pipeline.workers`, `pipeline.batch.size` and `pipeline.batch.delay` - these settings are used to tune the performance and resource utilization of the the `logstash` container; see [Tuning and Profiling Logstash Performance](https://www.elastic.co/guide/en/logstash/current/tuning-logstash.html), [`logstash.yml`](https://www.elastic.co/guide/en/logstash/current/logstash-settings-file.html) and [Multiple Pipelines](https://www.elastic.co/guide/en/logstash/current/multiple-pipelines.html) +* **`lookup-common.env`** - settings for enrichment lookups, including those used for [customizing event severity scoring](severity.md#SeverityConfig) + - `CONNECTION_SECONDS_SEVERITY_THRESHOLD` - when [severity scoring](severity.md#Severity) is enabled, this variable indicates the duration threshold (in seconds) for assigning severity to long connections (default `3600`) + - `FREQ_LOOKUP` - if set to `true`, domain names (from DNS queries and SSL server names) will be assigned entropy scores as calculated by [`freq`](https://github.com/MarkBaggett/freq) (default `false`) + - `FREQ_SEVERITY_THRESHOLD` - when [severity scoring](severity.md#Severity) is enabled, this variable indicates the entropy threshold for assigning severity to events with entropy scores calculated by [`freq`](https://github.com/MarkBaggett/freq); a lower value will only assign severity scores to fewer domain names with higher entropy (e.g., `2.0` for `NQZHTFHRMYMTVBQJE.COM`), while a higher value will assign severity scores to more domain names with lower entropy (e.g., `7.5` for `naturallanguagedomain.example.org`) (default `2.0`) + - `SENSITIVE_COUNTRY_CODES` - when [severity scoring](severity.md#Severity) is enabled, this variable defines a comma-separated list of sensitive countries (using [ISO 3166-1 alpha-2 codes](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Current_codes)) (default `'AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ'`, taken from the U.S. Department of Energy Sensitive Country List) + - `TOTAL_MEGABYTES_SEVERITY_THRESHOLD` - when [severity scoring](severity.md#Severity) is enabled, this variable indicates the size threshold (in megabytes) for assigning severity to large connections or file transfers (default `1000`) +* **`netbox-common.env`**, `netbox.env`, `netbox-postgres.env`, `netbox-redis-cache.env` and `netbox-redis.env` - settings related to [NetBox](https://netbox.dev/) and [Asset Interaction Analysis](asset-interaction-analysis.md#AssetInteractionAnalysis) + - `NETBOX_DISABLED` - if set to `true`, Malcolm will **not** start and manage a [NetBox](asset-interaction-analysis.md#AssetInteractionAnalysis) instance (default `true`) +* **`nginx.env`** - settings specific to Malcolm's nginx reverse proxy + - `NGINX_LOG_ACCESS_AND_ERRORS` - if set to `true`, all access to Malcolm via its [web interfaces](quickstart.md#UserInterfaceURLs) will be logged to OpenSearch (default `false`) + - `NGINX_SSL` - if set to `true`, require HTTPS connections to Malcolm's `nginx-proxy` container (default); if set to `false`, use unencrypted HTTP connections (using unsecured HTTP connections is **NOT** recommended unless you are running Malcolm behind another reverse proxy like Traefik, Caddy, etc.) +* **`opensearch.env`** - settings specific to [OpenSearch](https://opensearch.org/) + - `OPENSEARCH_JAVA_OPTS` - one of OpenSearch's most [important settings](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/index/#important-settings), the `-Xms` and `-Xmx` values set the size of OpenSearch's Java heap (we recommend setting this value to half of system RAM, up to 32 gigabytes) + - `OPENSEARCH_LOCAL` - if set to `true`, Malcolm will use its own internal [OpenSearch instance](opensearch-instances.md#OpenSearchInstance) (default `true`) + - `OPENSEARCH_URL` - when using Malcolm's internal OpenSearch instance (i.e., `OPENSEARCH_LOCAL` is `true`) this should be `http://opensearch:9200`, otherwise this value specifies the primary remote instance URL in the format `protocol://host:port` (default `http://opensearch:9200`) + - `OPENSEARCH_SSL_CERTIFICATE_VERIFICATION` - if set to `true`, connections to the primary remote OpenSearch instance will require full TLS certificate validation (this may fail if using self-signed certificates) (default `false`) + - `OPENSEARCH_SECONDARY` - if set to `true`, Malcolm will forward logs to a secondary remote OpenSearch instance in addition to the primary (local or remote) OpenSearch instance (default `false`) + - `OPENSEARCH_SECONDARY_URL` - when forwarding to a secondary remote OpenSearch instance (i.e., `OPENSEARCH_SECONDARY` is `true`) this value specifies the secondary remote instance URL in the format `protocol://host:port` + - `OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION` - if set to `true`, connections to the secondary remote OpenSearch instance will require full TLS certificate validation (this may fail if using self-signed certificates) (default `false`) +* **`pcap-capture.env`** - settings specific to capturing traffic for [live traffic analysis](live-analysis.md#LocalPCAP) + - `PCAP_ENABLE_NETSNIFF` – if set to `true`, Malcolm will capture network traffic on the local network interface(s) indicated in `PCAP_IFACE` using [netsniff-ng](http://netsniff-ng.org/) + - `PCAP_ENABLE_TCPDUMP` – if set to `true`, Malcolm will capture network traffic on the local network interface(s) indicated in `PCAP_IFACE` using [tcpdump](https://www.tcpdump.org/); there is no reason to enable *both* `PCAP_ENABLE_NETSNIFF` and `PCAP_ENABLE_TCPDUMP` + - `PCAP_FILTER` – specifies a tcpdump-style filter expression for local packet capture; leave blank to capture all traffic + - `PCAP_IFACE` – used to specify the network interface(s) for local packet capture if `PCAP_ENABLE_NETSNIFF`, `PCAP_ENABLE_TCPDUMP`, `ZEEK_LIVE_CAPTURE` or `SURICATA_LIVE_CAPTURE` are enabled; for multiple interfaces, separate the interface names with a comma (e.g., `'enp0s25'` or `'enp10s0,enp11s0'`) + - `PCAP_IFACE_TWEAK` - if set to `true`, Malcolm will [use `ethtool`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/nic-capture-setup.sh) to disable NIC hardware offloading features and adjust ring buffer sizes for capture interface(s); this should be `true` if the interface(s) are being used for capture only, `false` if they are being used for management/communication + - `PCAP_ROTATE_MEGABYTES` – used to specify how large a locally-captured PCAP file can become (in megabytes) before it is closed for processing and a new PCAP file created + - `PCAP_ROTATE_MINUTES` – used to specify a time interval (in minutes) after which a locally-captured PCAP file will be closed for processing and a new PCAP file created +* **`process.env`** - settings for how the processes running inside Malcolm containers are executed + - `PUID` and `PGID` - Docker runs all of its containers as the privileged `root` user by default. For better security, Malcolm immediately drops to non-privileged user accounts for executing internal processes wherever possible. The `PUID` (**p**rocess **u**ser **ID**) and `PGID` (**p**rocess **g**roup **ID**) environment variables allow Malcolm to map internal non-privileged user accounts to a corresponding [user account](https://en.wikipedia.org/wiki/User_identifier) on the host. Note that a few containers (including the `logstash` and `netbox` containers) may take a few extra minutes during startup if `PUID` and `PGID` are set to values other than the default `1000`. This is expected and should not affect operation after the initial startup. +* **`ssl.env`** - TLS-related settings used by many containers +* **`suricata.env`**, **`suricata-live.env`** and **`suricata-offline.env`** - settings for [Suricata](https://suricata.io/) + - `SURICATA_AUTO_ANALYZE_PCAP_FILES` – if set to `true`, all PCAP files imported into Malcolm will automatically be analyzed by Suricata, and the resulting logs will also be imported (default `false`) + - `SURICATA_AUTO_ANALYZE_PCAP_THREADS` – the number of threads available to Malcolm for analyzing Suricata logs (default `1`) + - `SURICATA_CUSTOM_RULES_ONLY` – if set to `true`, Malcolm will bypass the default [Suricata ruleset](https://github.com/OISF/suricata/tree/master/rules) and use only user-defined rules (`./suricata/rules/*.rules`). + - `SURICATA_UPDATE_RULES` – if set to `true`, Suricata signatures will periodically be updated (default `false`) + - `SURICATA_LIVE_CAPTURE` - if set to `true`, Suricata will monitor live traffic on the local interface(s) defined by `PCAP_FILTER` + - `SURICATA_ROTATED_PCAP` - if set to `true`, Suricata can analyze captured PCAP files captured by `netsniff-ng` or `tcpdump` (see `PCAP_ENABLE_NETSNIFF` and `PCAP_ENABLE_TCPDUMP`, as well as `SURICATA_AUTO_ANALYZE_PCAP_FILES`); if `SURICATA_LIVE_CAPTURE` is `true`, this should be false, otherwise Suricata will see duplicate traffic + - `SURICATA_…` - the [`suricata` container entrypoint script]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/suricata_config_populate.py) can use **many** more environment variables to tweak [suricata.yaml](https://github.com/OISF/suricata/blob/master/suricata.yaml.in); in that script, `DEFAULT_VARS` defines those variables (albeit without the `SURICATA_` prefix you must add to each for use) +* **`upload-common.env`** and **`upload.env`** - settings for dealing with PCAP files [uploaded](upload.md#Upload) to Malcolm for analysis + - `AUTO_TAG` – if set to `true`, Malcolm will automatically create Arkime sessions and Zeek logs with tags based on the filename, as described in [Tagging](upload.md#Tagging) (default `true`) +* **`zeek.env`**, **`zeek-live.env`** and **`zeek-offline.env`** - settings for [Zeek](https://www.zeek.org/index.html) and for scanning [extracted files](file-scanning.md#ZeekFileExtraction) Zeek observes in network traffic + - `EXTRACTED_FILE_CAPA_VERBOSE` – if set to `true`, all Capa rule hits will be logged; otherwise (`false`) only [MITRE ATT&CK® technique](https://attack.mitre.org/techniques) classifications will be logged + - `EXTRACTED_FILE_ENABLE_CAPA` – if set to `true`, [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) that are determined to be PE (portable executable) files will be scanned with [Capa](https://github.com/fireeye/capa) + - `EXTRACTED_FILE_ENABLE_CLAMAV` – if set to `true`, [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) will be scanned with [ClamAV](https://www.clamav.net/) + - `EXTRACTED_FILE_ENABLE_YARA` – if set to `true`, [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) will be scanned with [Yara](https://github.com/VirusTotal/yara) + - `EXTRACTED_FILE_HTTP_SERVER_ENABLE` – if set to `true`, the directory containing [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) will be served over HTTP at `./extracted-files/` (e.g., [https://localhost/extracted-files/](https://localhost/extracted-files/) if you are connecting locally) + - `EXTRACTED_FILE_HTTP_SERVER_ENCRYPT` – if set to `true`, those Zeek-extracted files will be AES-256-CBC-encrypted in an `openssl enc`-compatible format (e.g., `openssl enc -aes-256-cbc -d -in example.exe.encrypted -out example.exe`) + - `EXTRACTED_FILE_HTTP_SERVER_KEY` – specifies the AES-256-CBC decryption password for encrypted Zeek-extracted files; used in conjunction with `EXTRACTED_FILE_HTTP_SERVER_ENCRYPT` + - `EXTRACTED_FILE_IGNORE_EXISTING` – if set to `true`, files extant in `./zeek-logs/extract_files/` directory will be ignored on startup rather than scanned + - `EXTRACTED_FILE_PRESERVATION` – determines behavior for preservation of [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) + - `EXTRACTED_FILE_UPDATE_RULES` – if set to `true`, file scanner engines (e.g., ClamAV, Capa, Yara) will periodically update their rule definitions (default `false`) + - `EXTRACTED_FILE_YARA_CUSTOM_ONLY` – if set to `true`, Malcolm will bypass the default Yara rulesets ([Neo23x0/signature-base](https://github.com/Neo23x0/signature-base) and [bartblaze/Yara-rules](https://github.com/bartblaze/Yara-rules)) and use only user-defined rules in `./yara/rules` + - `VTOT_API2_KEY` – used to specify a [VirusTotal Public API v.20](https://www.virustotal.com/en/documentation/public-api/) key, which, if specified, will be used to submit hashes of [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) to VirusTotal + - `ZEEK_AUTO_ANALYZE_PCAP_FILES` – if set to `true`, all PCAP files imported into Malcolm will automatically be analyzed by Zeek, and the resulting logs will also be imported (default `false`) + - `ZEEK_AUTO_ANALYZE_PCAP_THREADS` – the number of threads available to Malcolm for analyzing Zeek logs (default `1`) + - `ZEEK_DISABLE_…` - if set to any non-blank value, each of these variables can be used to disable a certain Zeek function when it analyzes PCAP files (for example, setting `ZEEK_DISABLE_LOG_PASSWORDS` to `true` to disable logging of cleartext passwords) + - `ZEEK_DISABLE_BEST_GUESS_ICS` - see ["Best Guess" Fingerprinting for ICS Protocols](ics-best-guess.md#ICSBestGuess) + - `ZEEK_EXTRACTOR_MODE` – determines the file extraction behavior for file transfers detected by Zeek; see [Automatic file extraction and scanning](file-scanning.md#ZeekFileExtraction) for more details + - `ZEEK_INTEL_FEED_SINCE` - when querying a [TAXII](zeek-intel.md#ZeekIntelSTIX) or [MISP](zeek-intel.md#ZeekIntelMISP) feed, only process threat indicators that have been created or modified since the time represented by this value; it may be either a fixed date/time (`01/01/2021`) or relative interval (`30 days ago`) + - `ZEEK_INTEL_ITEM_EXPIRATION` - specifies the value for Zeek's [`Intel::item_expiration`](https://docs.zeek.org/en/current/scripts/base/frameworks/intel/main.zeek.html#id-Intel::item_expiration) timeout as used by the [Zeek Intelligence Framework](zeek-intel.md#ZeekIntel) (default `-1min`, which disables item expiration) + - `ZEEK_INTEL_REFRESH_CRON_EXPRESSION` - specifies a [cron expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) indicating the refresh interval for generating the [Zeek Intelligence Framework](zeek-intel.md#ZeekIntel) files (defaults to empty, which disables automatic refresh) + - `ZEEK_LIVE_CAPTURE` - if set to `true`, Zeek will monitor live traffic on the local interface(s) defined by `PCAP_FILTER` + - `ZEEK_ROTATED_PCAP` - if set to `true`, Zeek can analyze captured PCAP files captured by `netsniff-ng` or `tcpdump` (see `PCAP_ENABLE_NETSNIFF` and `PCAP_ENABLE_TCPDUMP`, as well as `ZEEK_AUTO_ANALYZE_PCAP_FILES`); if `ZEEK_LIVE_CAPTURE` is `true`, this should be false, otherwise Zeek will see duplicate traffic \ No newline at end of file diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md index 7303d9ad4..c43df9d9b 100644 --- a/docs/malcolm-hedgehog-e2e-iso-install.md +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -131,11 +131,12 @@ The panel bordering the top of the Malcolm desktop is home to a number of useful ### Configuration -The first time the Malcolm base operating system boots the **Malcolm Configuration** wizard will start automatically. This same configuration script can be run again later by running [`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning) from the Malcolm installation directory, or clicking the **Configure Malcolm** 🔳 icon in the top panel. +The first time the Malcolm base operating system boots the **Malcolm Configuration** wizard will start automatically. This same configuration script can be run again later by running [`./scripts/configure`](malcolm-config.md#ConfigAndTuning) from the Malcolm installation directory, or clicking the **Configure Malcolm** 🔳 icon in the top panel. ![Malcolm Configuration on first boot](./images/screenshots/malcolm_first_boot_config.png) -The [configuration and tuning](malcolm-config.md#ConfigAndTuning) wizard's questions proceed as follows. Note that you may not necessarily see every question listed here depending on how you answered earlier questions. Usually the default selection is what you'll want to select unless otherwise indicated below. +The [configuration and tuning](malcolm-config.md#ConfigAndTuning) wizard's questions proceed as follows. Note that you may not necessarily see every question listed here depending on how you answered earlier questions. Usually the default selection is what you'll want to select unless otherwise indicated below. The configuration values resulting from these questions are stored in [environment variable files](malcolm-config.md#MalcolmConfigEnvVars) in the `./config` directory. + * Malcolm processes will run as UID 1000 and GID 1000. Is this OK? - Docker runs all of its containers as the privileged `root` user by default. For better security, Malcolm immediately drops to non-privileged user accounts for executing internal processes wherever possible. The `PUID` (**p**rocess **u**ser **ID**) and `PGID` (**p**rocess **g**roup **ID**) environment variables allow Malcolm to map internal non-privileged user accounts to a corresponding [user account](https://en.wikipedia.org/wiki/User_identifier) on the host. diff --git a/docs/malcolm-iso.md b/docs/malcolm-iso.md index d0b1530ff..89fcf1f23 100644 --- a/docs/malcolm-iso.md +++ b/docs/malcolm-iso.md @@ -82,6 +82,6 @@ Following these prompts, the installer will reboot and the Malcolm base operatin When the system boots for the first time, the Malcolm Docker images will load if the installer was built with pre-packaged installation files as described above. Wait for this operation to continue (the progress dialog will disappear when they have finished loading) before continuing the setup. -Open a terminal (click the red terminal 🗔 icon next to the Debian swirl logo 🍥 menu button in the menu bar). At this point, setup is similar to the steps described in the [Quick start](quickstart.md#QuickStart) section. Navigate to the Malcolm directory (`cd ~/Malcolm`) and run [`auth_setup`](authsetup.md#AuthSetup) to configure authentication. If the ISO didn't have pre-packaged Malcolm images, or if you'd like to retrieve the latest updates, run `docker-compose pull`. Finalize your configuration by running `scripts/install.py --configure` and follow the prompts as illustrated in the [installation example](ubuntu-install-example.md#InstallationExample). +Open a terminal (click the red terminal 🗔 icon next to the Debian swirl logo 🍥 menu button in the menu bar). At this point, setup is similar to the steps described in the [Quick start](quickstart.md#QuickStart) section. Navigate to the Malcolm directory (`cd ~/Malcolm`) and run [`auth_setup`](authsetup.md#AuthSetup) to configure authentication. If the ISO didn't have pre-packaged Malcolm images, or if you'd like to retrieve the latest updates, run `docker-compose pull`. Finalize your configuration by running `scripts/configure` and follow the prompts as illustrated in the [installation example](malcolm-hedgehog-e2e-iso-install.md#MalcolmConfig). Once Malcolm is configured, you can [start Malcolm](running.md#Starting) via the command line or by clicking the circular yellow Malcolm icon in the menu bar. \ No newline at end of file diff --git a/docs/malcolm-preparation.md b/docs/malcolm-preparation.md index 6e0861c60..468138b53 100644 --- a/docs/malcolm-preparation.md +++ b/docs/malcolm-preparation.md @@ -3,7 +3,7 @@ * [Configuration](#Configuration) - [Recommended system requirements](system-requirements.md#SystemRequirements) - [Malcolm Configuration](malcolm-config.md#ConfigAndTuning) - + [`docker-compose.yml` parameters](malcolm-config.md#DockerComposeYml) + + [Environment Variable Files](malcolm-config.md#MalcolmConfigEnvVars) - [Configure authentication](authsetup.md#AuthSetup) + [Local account management](authsetup.md#AuthBasicAccountManagement) + [Lightweight Directory Access Protocol (LDAP) authentication](authsetup.md#AuthLDAP) diff --git a/docs/malcolm-upgrade.md b/docs/malcolm-upgrade.md index 75e898ce1..8fc5ea6a0 100644 --- a/docs/malcolm-upgrade.md +++ b/docs/malcolm-upgrade.md @@ -23,7 +23,7 @@ If you checked out a working copy of the Malcolm repository from GitHub with a ` 5. apply saved configuration change stashed earlier * `git stash pop` 6. if you see `Merge conflict` messages, resolve the [conflicts](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging#_basic_merge_conflicts) with your favorite text editor -7. you may wish to re-run `install.py --configure` as described in [System configuration and tuning](malcolm-config.md#ConfigAndTuning) in case there are any new `docker-compose.yml` parameters for Malcolm that need to be set up +7. you may wish to re-run `./scripts/configure` as described in [Malcolm Configuration](malcolm-config.md#ConfigAndTuning) in case there are any new configuration parameters for Malcolm that need to be set up 8. start Malcolm * `./scripts/start` 9. you may be prompted to [configure authentication](authsetup.md#AuthSetup) if there are new authentication-related files that need to be generated @@ -45,8 +45,8 @@ If you installed Malcolm from [pre-packaged installation files]({{ site.github.r * `cp -r ./malcolm_YYYYMMDD_HHNNSS_xxxxxxx/scripts ./malcolm_YYYYMMDD_HHNNSS_xxxxxxx/README.md ./` 4. replace (overwrite) `docker-compose.yml` file with new version * `cp ./malcolm_YYYYMMDD_HHNNSS_xxxxxxx/docker-compose.yml ./docker-compose.yml` -5. re-run `./scripts/install.py --configure` as described in [System configuration and tuning](malcolm-config.md#ConfigAndTuning) -6. using a file comparison tool (e.g., `diff`, `meld`, `Beyond Compare`, etc.), compare `docker-compose.yml` and the `docker-compare.yml` file you backed up in step 3, and manually migrate over any customizations you wish to preserve from that file (e.g., `PCAP_FILTER`, `MAXMIND_GEOIP_DB_LICENSE_KEY`, `MANAGE_PCAP_FILES`; [anything else](malcolm-config.md#DockerComposeYml) you may have edited by hand in `docker-compose.yml` that's not prompted for in `install.py --configure`) +5. re-run `./scripts/configure` as described in [Malcolm Configuration](malcolm-config.md#ConfigAndTuning) +6. using a file comparison tool (e.g., `diff`, `meld`, `Beyond Compare`, etc.), compare `docker-compose.yml` and the `docker-compare.yml` file you backed up in step 3, and manually migrate over any customizations you wish to preserve from that file (e.g., `PCAP_FILTER`, `MAXMIND_GEOIP_DB_LICENSE_KEY`, `MANAGE_PCAP_FILES`; [anything else](malcolm-config.md#MalcolmConfigEnvVars) you may have edited by hand in `docker-compose.yml` that's not prompted for in `configure`) 7. pull the new docker images (this will take a while) * `docker-compose pull` to pull them from [GitHub](https://github.com/orgs/idaholab/packages?repo_name=Malcolm) or `docker-compose load -i malcolm_YYYYMMDD_HHNNSS_xxxxxxx_images.tar.gz` if you have an offline tarball of the Malcolm docker images 8. start Malcolm diff --git a/docs/opensearch-instances.md b/docs/opensearch-instances.md index 3c981d5c2..b68633051 100644 --- a/docs/opensearch-instances.md +++ b/docs/opensearch-instances.md @@ -7,7 +7,7 @@ Malcolm's default standalone configuration is to use a local [OpenSearch](https: As the permutations of OpenSearch cluster configurations are numerous, it is beyond Malcolm's scope to set up multi-node clusters. However, Malcolm can be configured to use a remote OpenSearch cluster rather than its own internal instance. -The `OPENSEARCH_…` [environment variables in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) control whether Malcolm uses its own local OpenSearch instance or a remote OpenSearch instance as its primary data store. The configuration portion of Malcolm install script ([`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning)) can help you configure these options. +The `OPENSEARCH_…` [environment variables in `opensearch.env`](malcolm-config.md#MalcolmConfigEnvVars) control whether Malcolm uses its own local OpenSearch instance or a remote OpenSearch instance as its primary data store. The configuration portion of Malcolm install script ([`./scripts/configure`](malcolm-config.md#ConfigAndTuning)) can help you configure these options. For example, to use the default standalone configuration, answer `Y` when prompted `Should Malcolm use and maintain its own OpenSearch instance?`. @@ -25,7 +25,7 @@ You must run auth_setup after install.py to store OpenSearch connection credenti … ``` -Whether the primary OpenSearch instance is a locally maintained single-node instance or is a remote cluster, Malcolm can be configured additionally forward logs to a secondary remote OpenSearch instance. The `OPENSEARCH_SECONDARY_…` [environment variables in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) control this behavior. Configuration of a remote secondary OpenSearch instance is similar to that of a remote primary OpenSearch instance: +Whether the primary OpenSearch instance is a locally maintained single-node instance or is a remote cluster, Malcolm can be configured additionally forward logs to a secondary remote OpenSearch instance. The `OPENSEARCH_SECONDARY_…` [environment variables in `opensearch.env`](malcolm-config.md#MalcolmConfigEnvVars) control this behavior. Configuration of a remote secondary OpenSearch instance is similar to that of a remote primary OpenSearch instance: ``` @@ -42,7 +42,7 @@ You must run auth_setup after install.py to store OpenSearch connection credenti ## Authentication and authorization for remote OpenSearch clusters -In addition to setting the environment variables in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) as described above, you must provide Malcolm with credentials for it to be able to communicate with remote OpenSearch instances. These credentials are stored in the Malcolm installation directory as `.opensearch.primary.curlrc` and `.opensearch.secondary.curlrc` for the primary and secondary OpenSearch connections, respectively, and are bind mounted into the Docker containers which need to communicate with OpenSearch. These [cURL-formatted](https://everything.curl.dev/cmdline/configfile) config files can be generated for you by the [`auth_setup`](authsetup.md#AuthSetup) script as illustrated: +In addition to setting the environment variables in [`opensearch.env`](malcolm-config.md#MalcolmConfigEnvVars) as described above, you must provide Malcolm with credentials for it to be able to communicate with remote OpenSearch instances. These credentials are stored in the Malcolm installation directory as `.opensearch.primary.curlrc` and `.opensearch.secondary.curlrc` for the primary and secondary OpenSearch connections, respectively, and are bind mounted into the Docker containers which need to communicate with OpenSearch. These [cURL-formatted](https://everything.curl.dev/cmdline/configfile) config files can be generated for you by the [`auth_setup`](authsetup.md#AuthSetup) script as illustrated: ``` $ ./scripts/auth_setup diff --git a/docs/quickstart.md b/docs/quickstart.md index 1f1b23933..ebf741e77 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -20,9 +20,9 @@ The `build.sh` script can build Malcolm's Docker images from scratch. See [Build ### Initial configuration -The scripts to control Malcolm require Python 3. The [`install.py`](malcolm-config.md#ConfigAndTuning) script requires the [requests](https://docs.python-requests.org/en/latest/) module for Python 3, and will make use of the [pythondialog](https://pythondialog.sourceforge.io/) module for user interaction (on Linux) if it is available. +The scripts to control Malcolm require Python 3. The [`install.py`](malcolm-config.md#ConfigAndTuning) script requires the [dotenv](https://github.com/theskumar/python-dotenv), [requests](https://docs.python-requests.org/en/latest/) and [PyYAML](https://pyyaml.org/) modules for Python 3, and will make use of the [pythondialog](https://pythondialog.sourceforge.io/) module for user interaction (on Linux) if it is available. -You must run [`auth_setup`](authsetup.md#AuthSetup) prior to pulling Malcolm's Docker images. You should also ensure your system configuration and `docker-compose.yml` settings are tuned by running `./scripts/install.py` or `./scripts/install.py --configure` (see [System configuration and tuning](malcolm-config.md#ConfigAndTuning)). +You must run [`auth_setup`](authsetup.md#AuthSetup) prior to pulling Malcolm's Docker images. You should also ensure your system configuration and Malcolm settings are tuned by running `./scripts/install.py` and `./scripts/configure` (see [Malcolm Configuration](malcolm-config.md#ConfigAndTuning)). ### Pull Malcolm's Docker images diff --git a/docs/running.md b/docs/running.md index 72dd39bbf..3f02502be 100644 --- a/docs/running.md +++ b/docs/running.md @@ -24,7 +24,7 @@ You can also use `docker stats` to monitor the resource utilization of running c You can run `./scripts/stop` to stop the docker containers and remove their virtual network. Alternatively, `./scripts/restart` will restart an instance of Malcolm. Because the data on disk is stored on the host in docker volumes, doing these operations will not result in loss of data. -Malcolm can be configured to be automatically restarted when the Docker system daemon restart (for example, on system reboot). This behavior depends on the [value](https://docs.docker.com/config/containers/start-containers-automatically/) of the [`restart:`](https://docs.docker.com/compose/compose-file/#restart) setting for each service in the `docker-compose.yml` file. This value can be set by running [`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning) and answering "yes" to "`Restart Malcolm upon system or Docker daemon restart?`." +Malcolm can be configured to be automatically restarted when the Docker system daemon restart (for example, on system reboot). This behavior depends on the [value](https://docs.docker.com/config/containers/start-containers-automatically/) of the [`restart:`](https://docs.docker.com/compose/compose-file/#restart) setting for each service in the `docker-compose.yml` file. This value can be set by running [`./scripts/configure`](malcolm-config.md#ConfigAndTuning) and answering "yes" to "`Restart Malcolm upon system or Docker daemon restart?`." ## Clearing Malcolm's data diff --git a/docs/severity.md b/docs/severity.md index 45bd31d03..88f3b230d 100644 --- a/docs/severity.md +++ b/docs/severity.md @@ -8,9 +8,9 @@ As Zeek logs are parsed and enriched prior to indexing, a severity score up to ` * cross-segment network traffic (if [network subnets were defined](asset-interaction-analysis.md#AssetInteractionAnalysis)) * connection origination and destination (e.g., inbound, outbound, external, internal) * traffic to or from sensitive countries - - The comma-separated list of countries (by [ISO 3166-1 alpha-2 code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Current_codes)) can be customized by setting the `SENSITIVE_COUNTRY_CODES` environment variable in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml). + - The comma-separated list of countries (by [ISO 3166-1 alpha-2 code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Current_codes)) can be customized by setting the `SENSITIVE_COUNTRY_CODES` environment variable in [`lookup-common.env`](malcolm-config.md#MalcolmConfigEnvVars). * domain names (from DNS queries and SSL server names) with high entropy as calculated by [freq](https://github.com/MarkBaggett/freq) - - The entropy threshold for this condition to trigger can be adjusted by setting the `FREQ_SEVERITY_THRESHOLD` environment variable in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml). A lower value will only assign severity scores to fewer domain names with higher entropy (e.g., `2.0` for `NQZHTFHRMYMTVBQJE.COM`), while a higher value will assign severity scores to more domain names with lower entropy (e.g., `7.5` for `naturallanguagedomain.example.org`). + - The entropy threshold for this condition to trigger can be adjusted by setting the `FREQ_SEVERITY_THRESHOLD` environment variable in [`lookup-common.env`](malcolm-config.md#MalcolmConfigEnvVars). A lower value will only assign severity scores to fewer domain names with higher entropy (e.g., `2.0` for `NQZHTFHRMYMTVBQJE.COM`), while a higher value will assign severity scores to more domain names with lower entropy (e.g., `7.5` for `naturallanguagedomain.example.org`). * file transfers (categorized by mime type) * `notice.log`, [`intel.log`](zeek-intel.md#ZeekIntel) and `weird.log` entries, including those generated by Zeek plugins detecting vulnerabilities (see the list of Zeek plugins under [Components](components.md#Components)) * detection of cleartext passwords @@ -20,9 +20,9 @@ As Zeek logs are parsed and enriched prior to indexing, a severity score up to ` * common network services communicating over non-standard ports * file scanning engine hits on [extracted files](file-scanning.md#ZeekFileExtraction) * large connection or file transfer - - The size (in megabytes) threshold for this condition to trigger can be adjusted by setting the `TOTAL_MEGABYTES_SEVERITY_THRESHOLD` environment variable in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml). + - The size (in megabytes) threshold for this condition to trigger can be adjusted by setting the `TOTAL_MEGABYTES_SEVERITY_THRESHOLD` environment variable in [`lookup-common.env`](malcolm-config.md#MalcolmConfigEnvVars). * long connection duration - - The duration (in seconds) threshold for this condition to trigger can be adjusted by setting the `CONNECTION_SECONDS_SEVERITY_THRESHOLD` environment variable in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml). + - The duration (in seconds) threshold for this condition to trigger can be adjusted by setting the `CONNECTION_SECONDS_SEVERITY_THRESHOLD` environment variable in [`lookup-common.env`](malcolm-config.md#MalcolmConfigEnvVars). As this [feature]({{ site.github.repository_url }}/issues/19) is improved it's expected that additional categories will be identified and implemented for severity scoring. @@ -44,4 +44,4 @@ These categories' severity scores can be customized by editing `logstash/maps/ma Restart Logstash after modifying `malcolm_severity.yaml` for the changes to take effect. -Severity scoring can be disabled globally by setting the `LOGSTASH_SEVERITY_SCORING` environment variable to `false` in the [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) file and [restarting Malcolm](running.md#StopAndRestart). \ No newline at end of file +Severity scoring can be disabled globally by setting the `LOGSTASH_SEVERITY_SCORING` environment variable to `false` in the [`logstash.env`](malcolm-config.md#MalcolmConfigEnvVars) file and [restarting Malcolm](running.md#StopAndRestart). \ No newline at end of file diff --git a/docs/third-party-logs.md b/docs/third-party-logs.md index 54090ff6b..a3fb8aaff 100644 --- a/docs/third-party-logs.md +++ b/docs/third-party-logs.md @@ -26,7 +26,7 @@ The types of third-party logs and metrics discussed in this document are *not* t ## Configuring Malcolm -The environment variables in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) for configuring how Malcolm accepts external logs are prefixed with `FILEBEAT_TCP_…`. These values can be specified during Malcolm configuration (i.e., when running [`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning)), as can be seen from the following excerpt from the [Installation example](ubuntu-install-example.md#InstallationExample): +The environment variables in [`filebeat.env`](malcolm-config.md#MalcolmConfigEnvVars) for configuring how Malcolm accepts external logs are prefixed with `FILEBEAT_TCP_…`. These values can be specified during Malcolm configuration (i.e., when running [`./scripts/configure`](malcolm-config.md#ConfigAndTuning)), as can be seen from the following excerpt from the [Installation example](ubuntu-install-example.md#InstallationExample): ``` … @@ -47,7 +47,7 @@ Tag to apply to messages sent to Filebeat TCP listener (_malcolm_beats): _malcol … ``` -The variables corresponding to these questions can be found in the `filebeat-variables` section of`docker-compose.yml`: +The variables corresponding to these questions can be found in [`filebeat.env`](malcolm-config.md#MalcolmConfigEnvVars): * `FILEBEAT_TCP_LISTEN` - whether or not to expose a [Filebeat TCP input listener](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-tcp.html) to which logs may be sent (the default TCP port is `5045`: you may need to adjust your firewall accordingly) * `FILEBEAT_TCP_LOG_FORMAT` - log format expected for logs sent to the Filebeat TCP input listener (`json` or `raw`) @@ -60,7 +60,7 @@ These variables' values will depend on your forwarder and the format of the data ### Secure communication -In order to maintain the integrity and confidentiality of your data, Malcolm's default (set via the `BEATS_SSL` environment variable in `docker-compose.yml`) is to require connections from external forwarders to be encrypted using TLS. When [`./scripts/auth_setup`](authsetup.md#AuthSetup) is run, self-signed certificates are generated which may be used by remote log forwarders. Located in the `filebeat/certs/` directory, the certificate authority and client certificate and key files should be copied to the host on which your forwarder is running and used when defining its settings for connecting to Malcolm. +In order to maintain the integrity and confidentiality of your data, Malcolm's default (set via the `BEATS_SSL` environment variable in [`beats-common.env`](malcolm-config.md#MalcolmConfigEnvVars)) is to require connections from external forwarders to be encrypted using TLS. When [`./scripts/auth_setup`](authsetup.md#AuthSetup) is run, self-signed certificates are generated which may be used by remote log forwarders. Located in the `filebeat/certs/` directory, the certificate authority and client certificate and key files should be copied to the host on which your forwarder is running and used when defining its settings for connecting to Malcolm. ## Fluent Bit @@ -276,7 +276,7 @@ Running fluentbit_winev... fluentbit_winevtlog Elastic [Beats](https://www.elastic.co/beats/) can also be used to forward data to Malcolm's Filebeat TCP listener. Follow the [Get started with Beats](https://www.elastic.co/guide/en/beats/libbeat/current/getting-started.html) documentation for configuring Beats on your system. -In contrast to Fluent Bit, Beats forwarders write to Malcolm's Logstash input over TCP port 5044 (rather than its Filebeat TCP input). Answer `Y` when prompted `Expose Logstash port to external hosts?` during Malcolm configuration (i.e., when running [`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning)) to allow external remote Beats forwarders to send logs to Logstash. +In contrast to Fluent Bit, Beats forwarders write to Malcolm's Logstash input over TCP port 5044 (rather than its Filebeat TCP input). Answer `Y` when prompted `Expose Logstash port to external hosts?` during Malcolm configuration (i.e., when running [`./scripts/configure`](malcolm-config.md#ConfigAndTuning)) to allow external remote Beats forwarders to send logs to Logstash. Your Beat's [configuration YML file](https://www.elastic.co/guide/en/beats/libbeat/current/config-file-format.html) file might look something like this sample [filebeat.yml](https://www.elastic.co/guide/en/beats/filebeat/current/configuring-howto-filebeat.html) file: diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index a3c0ea965..fed210823 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -8,7 +8,7 @@ The commands in this example should be executed as a non-root user. You can use `git` to clone Malcolm into a local working copy, or you can download and extract the artifacts from the [latest release]({{ site.github.repository_url }}/releases). -To install Malcolm from the latest Malcolm release, browse to the [Malcolm releases page on GitHub]({{ site.github.repository_url }}/releases) and download at a minimum `install.py` and the `malcolm_YYYYMMDD_HHNNSS_xxxxxxx.tar.gz` file, then navigate to your downloads directory: +To install Malcolm from the latest Malcolm release, browse to the [Malcolm releases page on GitHub]({{ site.github.repository_url }}/releases) and download at a minimum the files ending in `.py` and the `malcolm_YYYYMMDD_HHNNSS_xxxxxxx.tar.gz` file, then navigate to your downloads directory: ``` user@host:~$ cd Downloads/ user@host:~/Downloads$ ls @@ -83,12 +83,12 @@ vm.dirty_ratio= appears to be missing from /etc/sysctl.conf, append it? (Y/n): y /etc/security/limits.d/limits.conf does not exist, create it? (Y/n): y ``` -If you are configuring Malcolm from within a git working copy, `install.py` will now exit. Run `install.py` again like you did at the beginning of the example, only remove the `sudo` and add `--configure` to run `install.py` in "configuration only" mode. +If you are configuring Malcolm from within a git working copy, `install.py` will now exit. Run `./scripts/configure` to continue with configuration: ``` -user@host:~/Malcolm$ ./scripts/install.py --configure +user@host:~/Malcolm$ ./scripts/configure ``` -Alternately, if you are configuring Malcolm from the release tarball you will be asked if you would like to extract the contents of the tarball and to specify the installation directory and `install.py` will continue: +Alternately, if you are configuring Malcolm from the release tarball you will be asked if you would like to extract the contents of the tarball and to specify the installation directory and Malcolm configuration will continue: ``` Extract Malcolm runtime files from /home/user/Downloads/malcolm_20190611_095410_ce2d8de.tar.gz (Y/n): y diff --git a/docs/upload.md b/docs/upload.md index a0a4e5fe1..c48ee3938 100644 --- a/docs/upload.md +++ b/docs/upload.md @@ -21,10 +21,10 @@ Files uploaded via these methods are monitored and moved automatically to other ## Tagging -In addition to be processed for uploading, Malcolm events will be tagged according to the components of the filenames of the PCAP files or Zeek log archives files from which the events were parsed. For example, records created from a PCAP file named `ACME_Scada_VLAN10.pcap` would be tagged with `ACME`, `Scada`, and `VLAN10`. Tags are extracted from filenames by splitting on the characters `,` (comma), `-` (dash), and `_` (underscore). These tags are viewable and searchable (via the `tags` field) in Arkime and OpenSearch Dashboards. This behavior can be changed by modifying the `AUTO_TAG` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml). +In addition to be processed for uploading, Malcolm events will be tagged according to the components of the filenames of the PCAP files or Zeek log archives files from which the events were parsed. For example, records created from a PCAP file named `ACME_Scada_VLAN10.pcap` would be tagged with `ACME`, `Scada`, and `VLAN10`. Tags are extracted from filenames by splitting on the characters `,` (comma), `-` (dash), and `_` (underscore). These tags are viewable and searchable (via the `tags` field) in Arkime and OpenSearch Dashboards. This behavior can be changed by modifying the `AUTO_TAG` [environment variable in `upload-common.env`](malcolm-config.md#MalcolmConfigEnvVars). Tags may also be specified manually with the [browser-based upload form](#Upload). ## Processing uploaded PCAPs with Zeek and Suricata -The **Analyze with Zeek** and **Analyze with Suricata** checkboxes may be used when uploading PCAP files to cause them to be analyzed by Zeek and Suricata, respectively. This is functionally equivalent to the `ZEEK_AUTO_ANALYZE_PCAP_FILES` and `SURICATA_AUTO_ANALYZE_PCAP_FILES` environment variables [described above](malcolm-config.md#DockerComposeYml), only on a per-upload basis. Zeek can also automatically carve out files from file transfers; see [Automatic file extraction and scanning](file-scanning.md#ZeekFileExtraction) for more details. +The **Analyze with Zeek** and **Analyze with Suricata** checkboxes may be used when uploading PCAP files to cause them to be analyzed by Zeek and Suricata, respectively. This is functionally equivalent to the `ZEEK_AUTO_ANALYZE_PCAP_FILES` and `SURICATA_AUTO_ANALYZE_PCAP_FILES` environment variables [described above](malcolm-config.md#MalcolmConfigEnvVars), only on a per-upload basis. Zeek can also automatically carve out files from file transfers; see [Automatic file extraction and scanning](file-scanning.md#ZeekFileExtraction) for more details. diff --git a/malcolm-iso/build.sh b/malcolm-iso/build.sh index 1d2e0445b..38721dd79 100755 --- a/malcolm-iso/build.sh +++ b/malcolm-iso/build.sh @@ -138,6 +138,7 @@ if [ -d "$WORKDIR" ]; then ln -s ./control.py status ln -s ./control.py stop ln -s ./control.py wipe + ln -s ./install.py configure popd >/dev/null 2>&1 cp ./scripts/malcolm_common.py "$MALCOLM_DEST_DIR/scripts/" cp ./scripts/malcolm_kubernetes.py "$MALCOLM_DEST_DIR/scripts/" diff --git a/scripts/configure b/scripts/configure new file mode 120000 index 000000000..7f4fe4b08 --- /dev/null +++ b/scripts/configure @@ -0,0 +1 @@ +install.py \ No newline at end of file diff --git a/scripts/install.py b/scripts/install.py index 895dbb27c..e5e9ab45a 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -2680,6 +2680,9 @@ def main(): parser.print_help() exit(2) + if os.path.islink(os.path.join(ScriptPath, ScriptName)) and ScriptName.startswith('configure'): + args.configOnly = True + if args.debug: eprint(os.path.join(ScriptPath, ScriptName)) eprint(f"Arguments: {sys.argv[1:]}") diff --git a/scripts/malcolm_appliance_packager.sh b/scripts/malcolm_appliance_packager.sh index 8855e03ef..27a9621d3 100755 --- a/scripts/malcolm_appliance_packager.sh +++ b/scripts/malcolm_appliance_packager.sh @@ -116,6 +116,7 @@ if mkdir "$DESTDIR"; then ln -s ./control.py status ln -s ./control.py stop ln -s ./control.py wipe + ln -s ./install.py configure popd >/dev/null 2>&1 pushd .. >/dev/null 2>&1 DESTNAME="$RUN_PATH/$(basename $DESTDIR).tar.gz" From 0ff6c3561ceedb760b8ef6b55d141049d1f2859f Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 09:23:05 -0600 Subject: [PATCH 195/239] for idaholab/Malcolm#174, check for required PersistentVolumeClaim entries before starting --- scripts/control.py | 59 ++++++++++++++++++++------------ scripts/malcolm_kubernetes.py | 63 +++++++++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 25 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index bf75974f7..21e04b840 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -41,8 +41,8 @@ OrchestrationFramework, OrchestrationFrameworksSupported, PLATFORM_WINDOWS, - ProcessLogLine, posInt, + ProcessLogLine, ScriptPath, YAMLDynamic, YesOrNo, @@ -52,35 +52,37 @@ deep_get, dictsearch, eprint, - EscapeForCurl, EscapeAnsi, + EscapeForCurl, get_iterable, get_primary_ip, LoadStrIfJson, ParseCurlFile, + pushd, RemoveEmptyFolders, run_process, same_file_or_dir, - which, str2bool, - pushd, + which, ) from malcolm_kubernetes import ( - PrintPodStatus, - PrintNodeStatus, + CheckPersistentStorageDefs, DeleteNamespace, - StartMalcolm, get_node_hostnames_and_ips, - PodExec, GetPodNamesForService, + PodExec, + PrintNodeStatus, + PrintPodStatus, + REQUIRED_VOLUME_OBJECTS, + StartMalcolm, ) from base64 import b64encode from collections import defaultdict, namedtuple from subprocess import PIPE, STDOUT, DEVNULL, Popen, TimeoutExpired from urllib.parse import urlparse -from itertools import chain +from itertools import chain, groupby try: from contextlib import nullcontext @@ -1022,23 +1024,36 @@ def start(): exit(err) elif orchMode is OrchestrationFramework.KUBERNETES: - startResults = StartMalcolm( + if CheckPersistentStorageDefs( namespace=args.namespace, malcolmPath=MalcolmPath, - configPath=args.configDir, - ) - - if dictsearch(startResults, 'error'): - eprint( - f"Starting the {args.namespace} namespace and creating its underlying resources returned the following error(s):\n" + ): + startResults = StartMalcolm( + namespace=args.namespace, + malcolmPath=MalcolmPath, + configPath=args.configDir, ) - eprint(startResults) - eprint() - elif args.debug: - eprint() - eprint(startResults) - eprint() + if dictsearch(startResults, 'error'): + eprint( + f"Starting the {args.namespace} namespace and creating its underlying resources returned the following error(s):\n" + ) + eprint(startResults) + eprint() + + elif args.debug: + eprint() + eprint(startResults) + eprint() + + else: + groupedStorageEntries = { + i: [j[0] for j in j] + for i, j in groupby(sorted(REQUIRED_VOLUME_OBJECTS.items(), key=lambda x: x[1]), lambda x: x[1]) + } + raise Exception( + f'Storage objects required by Malcolm are not defined in {os.path.join(MalcolmPath, "kubernetes")}: {groupedStorageEntries}' + ) else: raise Exception(f'{sys._getframe().f_code.co_name} does not yet support {orchMode}') diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index b11b03637..73dc83158 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -94,6 +94,25 @@ ], } +REQUIRED_VOLUME_OBJECTS = { + 'pcap-claim': 'PersistentVolumeClaim', + 'zeek-claim': 'PersistentVolumeClaim', + 'suricata-claim': 'PersistentVolumeClaim', + 'config-claim': 'PersistentVolumeClaim', + 'runtime-logs-claim': 'PersistentVolumeClaim', + 'opensearch-claim': 'PersistentVolumeClaim', + 'opensearch-backup-claim': 'PersistentVolumeClaim', + # the PersistentVolumes themselves aren't used directly, + # so we only need to define the PersistentVolumeClaims + # 'pcap-volume': 'PersistentVolume', + # 'zeek-volume': 'PersistentVolume', + # 'suricata-volume': 'PersistentVolume', + # 'config-volume': 'PersistentVolume', + # 'runtime-logs-volume': 'PersistentVolume', + # 'opensearch-volume': 'PersistentVolume', + # 'opensearch-backup-volume': 'PersistentVolume', +} + ################################################################################################### def _nanocore_to_millicore(n): @@ -647,9 +666,17 @@ def StartMalcolm(namespace, malcolmPath, configPath): # apply manifests results_dict['create_from_yaml']['result'] = dict() - for yamlName in sorted( - glob.iglob(os.path.join(os.path.join(malcolmPath, 'kubernetes'), '*.yml'), recursive=False) - ): + yamlFiles = sorted( + list( + chain( + *[ + glob.iglob(os.path.join(os.path.join(malcolmPath, 'kubernetes'), ftype), recursive=False) + for ftype in ['*.yml', '*.yaml'] + ] + ) + ) + ) + for yamlName in yamlFiles: try: results_dict['create_from_yaml']['result'][ os.path.basename(yamlName) @@ -674,3 +701,33 @@ def StartMalcolm(namespace, malcolmPath, configPath): results_dict['create_from_yaml']['error'][os.path.basename(yamlName)] = str(fe) return results_dict + + +def CheckPersistentStorageDefs(namespace, malcolmPath): + foundObjects = {k: False for (k, v) in REQUIRED_VOLUME_OBJECTS.items()} + + if yamlImported := YAMLDynamic(): + allYamlContents = [] + yamlFiles = sorted( + list( + chain( + *[ + glob.iglob(os.path.join(os.path.join(malcolmPath, 'kubernetes'), ftype), recursive=False) + for ftype in ['*.yml', '*.yaml'] + ] + ) + ) + ) + for yamlName in yamlFiles: + with open(yamlName, 'r') as cf: + allYamlContents.extend(list(yamlImported.safe_load_all(cf))) + for name, kind in REQUIRED_VOLUME_OBJECTS.items(): + for doc in allYamlContents: + if ( + (doc.get('kind', None) == kind) + and (deep_get(doc, ['metadata', 'namespace']) == namespace) + and (deep_get(doc, ['metadata', 'name']) == name) + ): + foundObjects[name] = True + + return all([v for k, v in foundObjects.items()]) From 92ea3aa146d7f2b17aaec0d77e1091a950153be1 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 09:31:05 -0600 Subject: [PATCH 196/239] for idaholab/Malcolm#174, don't define volumes but give example in 01-volumes.yml.example --- kubernetes/.gitignore | 2 + ...{01-volumes.yml => 01-volumes.yml.example} | 38 +++++++++---------- 2 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 kubernetes/.gitignore rename kubernetes/{01-volumes.yml => 01-volumes.yml.example} (92%) diff --git a/kubernetes/.gitignore b/kubernetes/.gitignore new file mode 100644 index 000000000..8f3d61bcb --- /dev/null +++ b/kubernetes/.gitignore @@ -0,0 +1,2 @@ +# user-defined volumes can be defined in 01-volumes.yml, patterned after 01-volumes.yml.example +01-volumes.yml diff --git a/kubernetes/01-volumes.yml b/kubernetes/01-volumes.yml.example similarity index 92% rename from kubernetes/01-volumes.yml rename to kubernetes/01-volumes.yml.example index a1256f0a8..60555b6da 100644 --- a/kubernetes/01-volumes.yml +++ b/kubernetes/01-volumes.yml.example @@ -8,7 +8,7 @@ metadata: namespace: malcolm spec: capacity: - storage: 250Gi + storage: 500Gi volumeMode: Filesystem accessModes: - ReadWriteMany @@ -22,7 +22,7 @@ spec: - retrans=2 nfs: path: /malcolm/pcap - server: 10.9.0.226 + server: 192.168.0.100 readOnly: false --- @@ -38,7 +38,7 @@ spec: volumeMode: Filesystem resources: requests: - storage: 250Gi + storage: 500Gi volumeName: pcap-volume --- @@ -51,7 +51,7 @@ metadata: namespace: malcolm spec: capacity: - storage: 100Gi + storage: 250Gi volumeMode: Filesystem accessModes: - ReadWriteMany @@ -65,7 +65,7 @@ spec: - retrans=2 nfs: path: /malcolm/zeek-logs - server: 10.9.0.226 + server: 192.168.0.100 readOnly: false --- @@ -81,7 +81,7 @@ spec: volumeMode: Filesystem resources: requests: - storage: 100Gi + storage: 250Gi volumeName: zeek-volume --- @@ -108,7 +108,7 @@ spec: - retrans=2 nfs: path: /malcolm/suricata-logs - server: 10.9.0.226 + server: 192.168.0.100 readOnly: false --- @@ -137,7 +137,7 @@ metadata: namespace: malcolm spec: capacity: - storage: 50Gi + storage: 25Gi volumeMode: Filesystem accessModes: - ReadWriteMany @@ -151,7 +151,7 @@ spec: - retrans=2 nfs: path: /malcolm/config - server: 10.9.0.226 + server: 192.168.0.100 readOnly: false --- @@ -167,7 +167,7 @@ spec: volumeMode: Filesystem resources: requests: - storage: 50Gi + storage: 25Gi volumeName: config-volume --- @@ -180,7 +180,7 @@ metadata: namespace: malcolm spec: capacity: - storage: 50Gi + storage: 25Gi volumeMode: Filesystem accessModes: - ReadWriteMany @@ -194,7 +194,7 @@ spec: - retrans=2 nfs: path: /malcolm/runtime-logs - server: 10.9.0.226 + server: 192.168.0.100 readOnly: false --- @@ -210,7 +210,7 @@ spec: volumeMode: Filesystem resources: requests: - storage: 50Gi + storage: 25Gi volumeName: runtime-logs-volume --- @@ -223,7 +223,7 @@ metadata: namespace: malcolm spec: capacity: - storage: 250Gi + storage: 500Gi volumeMode: Filesystem accessModes: - ReadWriteMany @@ -237,7 +237,7 @@ spec: - retrans=2 nfs: path: /malcolm/opensearch - server: 10.9.0.226 + server: 192.168.0.100 readOnly: false --- @@ -253,7 +253,7 @@ spec: volumeMode: Filesystem resources: requests: - storage: 250Gi + storage: 500Gi volumeName: opensearch-volume --- @@ -266,7 +266,7 @@ metadata: namespace: malcolm spec: capacity: - storage: 250Gi + storage: 500Gi volumeMode: Filesystem accessModes: - ReadWriteMany @@ -280,7 +280,7 @@ spec: - retrans=2 nfs: path: /malcolm/opensearch-backup - server: 10.9.0.226 + server: 192.168.0.100 readOnly: false --- @@ -296,5 +296,5 @@ spec: volumeMode: Filesystem resources: requests: - storage: 250Gi + storage: 500Gi volumeName: opensearch-backup-volume From 70cf4891cad7434cfa60c72c01950384faf77e82 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 10:32:31 -0600 Subject: [PATCH 197/239] k8s docs work in progress, for idaholab/Malcolm#173 --- docs/kubernetes.md | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/docs/kubernetes.md b/docs/kubernetes.md index e3c2c49d3..1ffbd0dc2 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -1,5 +1,33 @@ # Deploying Malcolm with Kubernetes -* [TODO](#Todo) +* [Deploying Malcolm with Kubernetes](#Kubernetes) + - [Configuration](#Config) + + [PersistentVolumeClaim definitions](#PVC) +* [Future Enhancements](#Future) + - [Live Traffic Analysis](#FutureLiveCap) + - [Horizontal Scaling](#FutureScaleOut) + - [Helm Chart](#FutureHelmChart) -## TODO +Malcolm can be + +## Configuration + +### PersistentVolumeClaim definitions + +## Running Malcolm + +# Future Enhancements + +Deploying Malcolm with Kubernetes is a new (and still somewhat experimental) feature, and does not yet support the full range of Malcolm features. Development around these features is [ongoing](https://github.com/idaholab/Malcolm/issues?q=is%3Aissue+is%3Aopen+kubernetes). Some of the notable features that are still a work in progress for Kubernetes deployment include: + +## Live Traffic Analysis + +For now, network traffic artifacts for analysis are provided to a Malcolm deployment on Kubernetes via [forwarding](malcolm-hedgehog-e2e-iso-install.md#HedgehogConfigForwarding) from a remote instance of [Hedgehog Linux](hedgehog.md) or via PCAP [upload](upload.md#Upload). [Future work](https://github.com/idaholab/Malcolm/issues/175) is needed to design and implement monitoring of network traffic in the cloud. + +## Horizontal Scaling + +For now, the Malcolm services running in Kubernetes are configured with `replicas: 1`. There is [more investigation and development](https://github.com/idaholab/Malcolm/issues/182) needed to ensure Malcolm's containers work correctly when horizontally scaled. + +## Helm Chart + +For now, Malcolm's Kubernetes deployment is managed via vanilla [Kubernetes manifests]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/kubernetes/). We need to [look into](https://github.com/idaholab/Malcolm/issues/187) what a Malcolm Helm chart would look like and how it would fit in with the [deployment scripts](https://github.com/idaholab/Malcolm/issues/172) for [configuring](#Config) and [running](#Running) Malcolm, if at all. \ No newline at end of file From c40976a7afbcbf0fb8040b303b5f9dc477897536 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 10:48:46 -0600 Subject: [PATCH 198/239] work for idaholab/Malcolm#172 script consolidation --- malcolm-iso/build.sh | 7 +++++-- scripts/build.sh | 1 - scripts/malcolm_appliance_packager.sh | 18 +++++++++++++++--- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/malcolm-iso/build.sh b/malcolm-iso/build.sh index 38721dd79..590970043 100755 --- a/malcolm-iso/build.sh +++ b/malcolm-iso/build.sh @@ -98,16 +98,17 @@ if [ -d "$WORKDIR" ]; then MALCOLM_DEST_DIR="$WORKDIR/work/$IMAGE_NAME-Live-Build/config/includes.chroot/etc/skel/Malcolm" mkdir -p "$MALCOLM_DEST_DIR/arkime-logs/" mkdir -p "$MALCOLM_DEST_DIR/arkime-raw/" + mkdir -p "$MALCOLM_DEST_DIR/config/" mkdir -p "$MALCOLM_DEST_DIR/filebeat/certs/" mkdir -p "$MALCOLM_DEST_DIR/htadmin/" mkdir -p "$MALCOLM_DEST_DIR/logstash/certs/" mkdir -p "$MALCOLM_DEST_DIR/logstash/maps/" - mkdir -p "$MALCOLM_DEST_DIR/netbox/env/" mkdir -p "$MALCOLM_DEST_DIR/netbox/media/" mkdir -p "$MALCOLM_DEST_DIR/netbox/postgres/" mkdir -p "$MALCOLM_DEST_DIR/netbox/redis/" mkdir -p "$MALCOLM_DEST_DIR/nginx/ca-trust/" mkdir -p "$MALCOLM_DEST_DIR/nginx/certs/" + mkdir -p "$MALCOLM_DEST_DIR/kubernetes/" mkdir -p "$MALCOLM_DEST_DIR/opensearch-backup/" mkdir -p "$MALCOLM_DEST_DIR/opensearch/nodes/" mkdir -p "$MALCOLM_DEST_DIR/pcap/processed/" @@ -140,13 +141,15 @@ if [ -d "$WORKDIR" ]; then ln -s ./control.py wipe ln -s ./install.py configure popd >/dev/null 2>&1 + cp ./config/*.example "$MALCOLM_DEST_DIR/config/" cp ./scripts/malcolm_common.py "$MALCOLM_DEST_DIR/scripts/" cp ./scripts/malcolm_kubernetes.py "$MALCOLM_DEST_DIR/scripts/" cp ./scripts/malcolm_utils.py "$MALCOLM_DEST_DIR/scripts/" + cp ./kubernetes/*.* "$MALCOLM_DEST_DIR/kubernetes/" + grep -v "^#" ./kubernetes/.gitignore | xargs -r -I XXX rm -f "$MALCOLM_DEST_DIR/kubernetes/XXX" cp ./logstash/certs/*.conf "$MALCOLM_DEST_DIR/logstash/certs/" cp ./logstash/maps/malcolm_severity.yaml "$MALCOLM_DEST_DIR/logstash/maps/" cp -r ./netbox/config/ "$MALCOLM_DEST_DIR/netbox/" - cp ./netbox/env/netbox.env.example "$MALCOLM_DEST_DIR/netbox/env/" touch "$MALCOLM_DEST_DIR"/firstrun popd >/dev/null 2>&1 diff --git a/scripts/build.sh b/scripts/build.sh index d6c950864..751bc78b1 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -73,7 +73,6 @@ VCS_REVISION="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)" GITHUB_API_TOKEN="${GITHUB_TOKEN:-}" mkdir -p ./config -[[ ! -f ./config/auth.env ]] && touch ./config/auth.env # MaxMind now requires a (free) license key to download the free versions of their GeoIP databases. if [ ${#MAXMIND_GEOIP_DB_LICENSE_KEY} -gt 1 ]; then diff --git a/scripts/malcolm_appliance_packager.sh b/scripts/malcolm_appliance_packager.sh index 27a9621d3..8eb037d9e 100755 --- a/scripts/malcolm_appliance_packager.sh +++ b/scripts/malcolm_appliance_packager.sh @@ -67,7 +67,7 @@ if mkdir "$DESTDIR"; then mkdir $VERBOSE -p "$DESTDIR/htadmin/" mkdir $VERBOSE -p "$DESTDIR/logstash/certs/" mkdir $VERBOSE -p "$DESTDIR/logstash/maps/" - mkdir $VERBOSE -p "$DESTDIR/netbox/env/" + mkdir $VERBOSE -p "$DESTDIR/netbox/" mkdir $VERBOSE -p "$DESTDIR/netbox/media/" mkdir $VERBOSE -p "$DESTDIR/netbox/postgres/" mkdir $VERBOSE -p "$DESTDIR/netbox/redis/" @@ -90,8 +90,9 @@ if mkdir "$DESTDIR"; then mkdir $VERBOSE -p "$DESTDIR/zeek-logs/upload/" mkdir $VERBOSE -p "$DESTDIR/zeek/intel/MISP" mkdir $VERBOSE -p "$DESTDIR/zeek/intel/STIX" + + cp $VERBOSE ./config/*.example "$DESTDIR/config/" cp $VERBOSE ./docker-compose-standalone.yml "$DESTDIR/docker-compose.yml" - touch "$DESTDIR/"config/auth.env cp $VERBOSE ./net-map.json "$DESTDIR/" cp $VERBOSE ./scripts/install.py "$DESTDIR/scripts/" cp $VERBOSE ./scripts/control.py "$DESTDIR/scripts/" @@ -102,7 +103,17 @@ if mkdir "$DESTDIR"; then cp $VERBOSE ./logstash/certs/*.conf "$DESTDIR/logstash/certs/" cp $VERBOSE ./logstash/maps/malcolm_severity.yaml "$DESTDIR/logstash/maps/" cp $VERBOSE -r ./netbox/config/ "$DESTDIR/netbox/" - cp $VERBOSE ./netbox/env/netbox.env.example "$DESTDIR/netbox/env/" + + unset CONFIRMATION + echo "" + read -p "Package Kubernetes manifests in addition to docker-compose.yml [y/N]? " CONFIRMATION + CONFIRMATION=${CONFIRMATION:-N} + if [[ $CONFIRMATION =~ ^[Yy]$ ]]; then + mkdir $VERBOSE -p "$DESTDIR/kubernetes/" + cp $VERBOSE ./kubernetes/*.* "$DESTDIR/kubernetes/" + grep -v '^#' ./kubernetes/.gitignore | xargs -r -I XXX rm -f "$DESTDIR/kubernetes/XXX" + fi + pushd "$DESTDIR" >/dev/null 2>&1 touch ./.opensearch.primary.curlrc ./.opensearch.secondary.curlrc chmod 600 ./.opensearch.primary.curlrc ./.opensearch.secondary.curlrc @@ -125,6 +136,7 @@ if mkdir "$DESTDIR"; then cp $VERBOSE "$SCRIPT_PATH/malcolm_common.py" "$RUN_PATH/" cp $VERBOSE "$SCRIPT_PATH/malcolm_kubernetes.py" "$RUN_PATH/" cp $VERBOSE "$SCRIPT_PATH/malcolm_utils.py" "$RUN_PATH/" + tar -czf $VERBOSE "$DESTNAME" "./$(basename $DESTDIR)/" echo "Packaged Malcolm to \"$DESTNAME\"" From f4ae512d4530f0ddf23775c1f0413dc01144193c Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 11:54:01 -0600 Subject: [PATCH 199/239] work in progress for idaholab/Malcolm#173, documentation changes for kubernetes deployment --- docs/kubernetes.md | 170 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 169 insertions(+), 1 deletion(-) diff --git a/docs/kubernetes.md b/docs/kubernetes.md index 1ffbd0dc2..c4b2e609f 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -1,6 +1,8 @@ # Deploying Malcolm with Kubernetes * [Deploying Malcolm with Kubernetes](#Kubernetes) + - [System](#System) + + [Ingress Controller](#Ingress) - [Configuration](#Config) + [PersistentVolumeClaim definitions](#PVC) * [Future Enhancements](#Future) @@ -8,10 +10,176 @@ - [Horizontal Scaling](#FutureScaleOut) - [Helm Chart](#FutureHelmChart) -Malcolm can be +## System + +### Ingress Controller + +Malcolm's [ingress controller manifest]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/kubernetes/00-ingress.yml) uses the [Ingress-NGINX controller for Kubernetes](https://github.com/kubernetes/ingress-nginx). A few Malcolm features require some customization when installing and configuring the Ingress-NGINX controller: + +* To [forward](malcolm-hedgehog-e2e-iso-install.md#HedgehogConfigForwarding) logs from a remote instance of [Hedgehog Linux](hedgehog.md): + - See ["Exposing TCP and UDP services"](https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/) in the Ingress-NGINX documentation. + - You must configure the controller to start up with the `--tcp-services-configmap=ingress-nginx/tcp-services` flag: +```yml +apiVersion: apps/v1 +kind: Deployment +metadata: +… + name: ingress-nginx-controller + namespace: ingress-nginx +spec: +… + template: +… + spec: + containers: + * args: + * /nginx-ingress-controller + * --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller + * --election-id=ingress-nginx-leader + * --controller-class=k8s.io/ingress-nginx + * --ingress-class=nginx + * --configmap=$(POD_NAMESPACE)/ingress-nginx-controller + * --validating-webhook=:8443 + * --validating-webhook-certificate=/usr/local/certificates/cert + * --validating-webhook-key=/usr/local/certificates/key + * --enable-ssl-passthrough + * --tcp-services-configmap=ingress-nginx/tcp-services +… +``` + - You must add the appropriate ports (minimally TCP ports 5044 and 9200) to the `ingress-nginx-controller` load-balancer service definition: +```yml +--- +apiVersion: v1 +kind: Service +metadata: +… + name: ingress-nginx-controller + namespace: ingress-nginx +spec: + externalTrafficPolicy: Local + ipFamilies: + + IPv4 + ipFamilyPolicy: SingleStack + ports: + + appProtocol: http + name: http + port: 80 + protocol: TCP + targetPort: http + + appProtocol: https + name: https + port: 443 + protocol: TCP + targetPort: https + + appProtocol: tcp + name: lumberjack + port: 5044 + targetPort: 5044 + protocol: TCP + + appProtocol: tcp + name: tcpjson + port: 5045 + targetPort: 5045 + protocol: TCP ++ appProtocol: tcp + name: opensearch + port: 9200 + targetPort: 9200 + protocol: TCP +… + type: LoadBalancer +``` + - You must add the appropriate ports (minimally TCP ports 5044 and 9200) to the `ingress-nginx-controller` deployment container's definition: +```yml +apiVersion: apps/v1 +kind: Deployment +metadata: +… + name: ingress-nginx-controller + namespace: ingress-nginx +spec: +… + template: +… + spec: + containers: +… + ports: + - containerPort: 80 + name: http + protocol: TCP + - containerPort: 443 + name: https + protocol: TCP + - containerPort: 8443 + name: webhook + protocol: TCP + - name: lumberjack + containerPort: 5044 + protocol: TCP + - name: tcpjson + containerPort: 5045 + protocol: TCP + - name: opensearch + containerPort: 9200 + protocol: TCP +… +``` +* To use [SSL Passthrough](https://kubernetes.github.io/ingress-nginx/user-guide/tls/) to have the Kubernetes gateway use Malcolm's TLS certificates rather than its own: + - You must configure the controller to start up with the `--enable-ssl-passthrough` flag. +``` +apiVersion: apps/v1 +kind: Deployment +metadata: +… + name: ingress-nginx-controller + namespace: ingress-nginx +spec: +… + template: +… + spec: + containers: + - args: + - /nginx-ingress-controller + - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller + - --election-id=ingress-nginx-leader + - --controller-class=k8s.io/ingress-nginx + - --ingress-class=nginx + - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller + - --validating-webhook=:8443 + - --validating-webhook-certificate=/usr/local/certificates/cert + - --validating-webhook-key=/usr/local/certificates/key + - --enable-ssl-passthrough + - --tcp-services-configmap=ingress-nginx/tcp-services +… +``` + - You must modify Malcolm's [ingress controller manifest]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/kubernetes/00-ingress.yml) to specify the `host:` value and use [host-based routing](https://kubernetes.github.io/ingress-nginx/user-guide/basic-usage/): +``` +… +spec: + rules: + - host: malcolm.example.org + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: nginx-proxy + port: + number: 443 +… +``` + +### System Limits ## Configuration +The steps to configure and tune Malcolm for a Kubernetes deployment are [very similar](malcolm-config.md#ConfigAndTuning) to those for a Docker-based deployment. Both methods use [environment variable files](malcolm-config.md#MalcolmConfigEnvVars) for Malcolm's runtime configuration. + +Malcolm's configuration and runtime scripts (e.g., `./scripts/configure`, `./scripts/auth_setup`, `./scripts/start`, etc.) are used for both Docker- and Kubernetes-based deployments. To indicate to these scripts that you're working with Kubernetes rather than `docker-compose`, provide the script with the [kubeconfig file](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) used to communicate with the API server of the Kubernetes cluster (e.g., `./scripts/configure -f k3s.yaml` or `./scripts/start -f kubeconfig.yaml`, etc.). The scripts will detect whether YAML file specified is a kubeconfig file or a Docker compose file and act accordingly + ### PersistentVolumeClaim definitions ## Running Malcolm From fdbf19fd78ff579842e9f2aee07c4beb0f8e6ce8 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 14:08:49 -0600 Subject: [PATCH 200/239] work in progress for idaholab/Malcolm#173, documentation changes for kubernetes deployment --- docs/kubernetes.md | 64 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/docs/kubernetes.md b/docs/kubernetes.md index c4b2e609f..54b300cae 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -3,8 +3,10 @@ * [Deploying Malcolm with Kubernetes](#Kubernetes) - [System](#System) + [Ingress Controller](#Ingress) - - [Configuration](#Config) - + [PersistentVolumeClaim definitions](#PVC) + + [Kubernetes Provider Settings](#Limits) +- [Configuration](#Config) + + [OpenSearch Instances](#OpenSearchInstances) + + [PersistentVolumeClaim Definitions](#PVC) * [Future Enhancements](#Future) - [Live Traffic Analysis](#FutureLiveCap) - [Horizontal Scaling](#FutureScaleOut) @@ -19,7 +21,7 @@ Malcolm's [ingress controller manifest]({{ site.github.repository_url }}/blob/{{ * To [forward](malcolm-hedgehog-e2e-iso-install.md#HedgehogConfigForwarding) logs from a remote instance of [Hedgehog Linux](hedgehog.md): - See ["Exposing TCP and UDP services"](https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/) in the Ingress-NGINX documentation. - You must configure the controller to start up with the `--tcp-services-configmap=ingress-nginx/tcp-services` flag: -```yml +``` apiVersion: apps/v1 kind: Deployment metadata: @@ -47,7 +49,7 @@ spec: … ``` - You must add the appropriate ports (minimally TCP ports 5044 and 9200) to the `ingress-nginx-controller` load-balancer service definition: -```yml +``` --- apiVersion: v1 kind: Service @@ -90,7 +92,7 @@ spec: type: LoadBalancer ``` - You must add the appropriate ports (minimally TCP ports 5044 and 9200) to the `ingress-nginx-controller` deployment container's definition: -```yml +``` apiVersion: apps/v1 kind: Deployment metadata: @@ -172,15 +174,61 @@ spec: … ``` -### System Limits +### Kubernetes Provider Settings + +OpenSearch has some [important settings](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/index/#important-settings) that must be present on its underlying Linux system. How this settings are configured depends largely on the underlying host(s) running Kubernetes, how Kubernetes is installed or the cloud provider on which it is running. Consult your operating system or cloud provider documentation for how to configure these settings. + +Settings which likely need to be changed in the underlying host running Kubernetes include: + +* System settings (e.g., in `/etc/sysctl.conf`) +``` +# the maximum number of memory map areas a process may have +vm.max_map_count=262144 +``` +* System limits (e.g., in `/etc/security/limits.d/limits.conf`) +``` +* soft nofile 65535 +* hard nofile 65535 +* soft memlock unlimited +* hard memlock unlimited +* soft nproc 262144 +* hard nproc 524288 +* soft core 0 +* hard core 0 +``` ## Configuration The steps to configure and tune Malcolm for a Kubernetes deployment are [very similar](malcolm-config.md#ConfigAndTuning) to those for a Docker-based deployment. Both methods use [environment variable files](malcolm-config.md#MalcolmConfigEnvVars) for Malcolm's runtime configuration. -Malcolm's configuration and runtime scripts (e.g., `./scripts/configure`, `./scripts/auth_setup`, `./scripts/start`, etc.) are used for both Docker- and Kubernetes-based deployments. To indicate to these scripts that you're working with Kubernetes rather than `docker-compose`, provide the script with the [kubeconfig file](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) used to communicate with the API server of the Kubernetes cluster (e.g., `./scripts/configure -f k3s.yaml` or `./scripts/start -f kubeconfig.yaml`, etc.). The scripts will detect whether YAML file specified is a kubeconfig file or a Docker compose file and act accordingly +Malcolm's configuration and runtime scripts (e.g., `./scripts/configure`, `./scripts/auth_setup`, `./scripts/start`, etc.) are used for both Docker- and Kubernetes-based deployments. To indicate to these scripts that you're working with Kubernetes rather than `docker-compose`, provide the script with the [kubeconfig file](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) used to communicate with the API server of the Kubernetes cluster (e.g., `./scripts/configure -f k3s.yaml` or `./scripts/start -f kubeconfig.yaml`, etc.). The scripts will detect whether the YAML file specified is a kubeconfig file or a Docker compose file and act accordingly. -### PersistentVolumeClaim definitions +Run `./scripts/configure` and answer the questions to configure Malcolm. For an in-depth treatment of these configuration questions, see the **Configuration** section in **[End-to-end Malcolm and Hedgehog Linux ISO Installation](malcolm-hedgehog-e2e-iso-install.md#MalcolmConfig)**. You'll also need to run [`./scripts/auth_setup`](authsetup.md#AuthSetup) to configure authentication. + +### OpenSearch Instances + +While Malcolm can manage its own single-node OpenSearch instance as part of its Kubernetes deployment, it's likely you'll want to use an existing multi-node OpenSearch cluster hosted on Kubernetes or some other provider (see, for example, ["Setup OpenSearch multi-node cluster on Kubernetes using Helm Charts"](https://opensearch.org/blog/setup-multinode-cluster-kubernetes/) on the OpenSearch blog and ["OpenSearch Kubernetes Operator"](https://opensearch.org/docs/latest/tools/k8s-operator/) in the OpenSearch documentation). Review Malcolm's documentation on [OpenSearch instances](opensearch-instances.md#OpenSearchInstance) to configure your Malcolm deployment to use an OpenSearch cluster. + +### PersistentVolumeClaim Definitions + +Malcolm requires persistent [storage](https://kubernetes.io/docs/concepts/storage/) to be configured for its configuration and data files. There are various implementations for provisioning PersistentVolume resources using [storage classes](https://kubernetes.io/docs/concepts/storage/storage-classes/). Regardless of the types of storage underlying the PersistentVolumes, Malcolm requires the following PersistentVolumeClaims to be defined in the `malcolm` namespace: + +* `config-claim` - storage for configuration files +* `opensearch-backup-claim` - storage for OpenSearch snapshots (if using a local [OpenSearch instance](opensearch-instances.md#OpenSearchInstance)) +* `opensearch-claim` - storage for OpenSearch indices (if using a local [OpenSearch instance](opensearch-instances.md#OpenSearchInstance)) +* `pcap-claim` - storage for PCAP artifacts +* `runtime-logs-claim` - storage for runtime logs for some containers (e.g., nginx, Arkime) +* `suricata-claim` - storage for Suricata logs +* `zeek-claim` - storage for Zeek logs and files extracted by Zeek + +An example of how these PersistentVolume and PersistentVolumeClaim objects could be defined in the [kubernetes/01-volumes.yml.example]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/kubernetes/01-volumes.yml.example) manifest file. Before [running](#Running) Malcolm, copy the `01-volumes.yml.example` file to `01-volumes.yml` and modify (or replace) its contents to define your PersistentVolumeClaim objects. + +If you attempt to start Malcolm without these PersistentVolumeClaims defined in a YAML file in Malcolm's `./kubernetes/` directory, you'll get an error like this: + +``` +$ ./scripts/start -f /path/to/kubeconfig.yml +Exception: Storage objects required by Malcolm are not defined in /home/user/Malcolm/kubernetes: {'PersistentVolumeClaim': ['pcap-claim', 'zeek-claim', 'suricata-claim', 'config-claim', 'runtime-logs-claim', 'opensearch-claim', 'opensearch-backup-claim']} +``` ## Running Malcolm From 2b6f09d8bda049a2e18c39a9f5e7dcf04e148f77 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 14:09:34 -0600 Subject: [PATCH 201/239] work in progress for idaholab/Malcolm#173, documentation changes for kubernetes deployment --- docs/kubernetes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/kubernetes.md b/docs/kubernetes.md index 54b300cae..00595297c 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -48,6 +48,7 @@ spec: * --tcp-services-configmap=ingress-nginx/tcp-services … ``` + - You must add the appropriate ports (minimally TCP ports 5044 and 9200) to the `ingress-nginx-controller` load-balancer service definition: ``` --- @@ -91,6 +92,7 @@ spec: … type: LoadBalancer ``` + - You must add the appropriate ports (minimally TCP ports 5044 and 9200) to the `ingress-nginx-controller` deployment container's definition: ``` apiVersion: apps/v1 @@ -156,6 +158,7 @@ spec: - --tcp-services-configmap=ingress-nginx/tcp-services … ``` + - You must modify Malcolm's [ingress controller manifest]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/kubernetes/00-ingress.yml) to specify the `host:` value and use [host-based routing](https://kubernetes.github.io/ingress-nginx/user-guide/basic-usage/): ``` … From aef954ae87fbc464c52cd2ecb26572d75eb3ed9f Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 14:10:39 -0600 Subject: [PATCH 202/239] work in progress for idaholab/Malcolm#173, documentation changes for kubernetes deployment --- docs/kubernetes.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/kubernetes.md b/docs/kubernetes.md index 00595297c..e6f1d09d0 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -21,6 +21,7 @@ Malcolm's [ingress controller manifest]({{ site.github.repository_url }}/blob/{{ * To [forward](malcolm-hedgehog-e2e-iso-install.md#HedgehogConfigForwarding) logs from a remote instance of [Hedgehog Linux](hedgehog.md): - See ["Exposing TCP and UDP services"](https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/) in the Ingress-NGINX documentation. - You must configure the controller to start up with the `--tcp-services-configmap=ingress-nginx/tcp-services` flag: + ``` apiVersion: apps/v1 kind: Deployment @@ -50,6 +51,7 @@ spec: ``` - You must add the appropriate ports (minimally TCP ports 5044 and 9200) to the `ingress-nginx-controller` load-balancer service definition: + ``` --- apiVersion: v1 @@ -94,6 +96,7 @@ spec: ``` - You must add the appropriate ports (minimally TCP ports 5044 and 9200) to the `ingress-nginx-controller` deployment container's definition: + ``` apiVersion: apps/v1 kind: Deployment @@ -129,8 +132,11 @@ spec: protocol: TCP … ``` + * To use [SSL Passthrough](https://kubernetes.github.io/ingress-nginx/user-guide/tls/) to have the Kubernetes gateway use Malcolm's TLS certificates rather than its own: + - You must configure the controller to start up with the `--enable-ssl-passthrough` flag. + ``` apiVersion: apps/v1 kind: Deployment @@ -160,6 +166,7 @@ spec: ``` - You must modify Malcolm's [ingress controller manifest]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/kubernetes/00-ingress.yml) to specify the `host:` value and use [host-based routing](https://kubernetes.github.io/ingress-nginx/user-guide/basic-usage/): + ``` … spec: From 0c847dce8126f31f6142e39815709f5619978fbd Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 14:13:12 -0600 Subject: [PATCH 203/239] work in progress for idaholab/Malcolm#173, documentation changes for kubernetes deployment --- docs/kubernetes.md | 315 ++++++++++++++++++++++----------------------- 1 file changed, 155 insertions(+), 160 deletions(-) diff --git a/docs/kubernetes.md b/docs/kubernetes.md index e6f1d09d0..a8a7b509f 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -21,168 +21,163 @@ Malcolm's [ingress controller manifest]({{ site.github.repository_url }}/blob/{{ * To [forward](malcolm-hedgehog-e2e-iso-install.md#HedgehogConfigForwarding) logs from a remote instance of [Hedgehog Linux](hedgehog.md): - See ["Exposing TCP and UDP services"](https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/) in the Ingress-NGINX documentation. - You must configure the controller to start up with the `--tcp-services-configmap=ingress-nginx/tcp-services` flag: - -``` -apiVersion: apps/v1 -kind: Deployment -metadata: -… - name: ingress-nginx-controller - namespace: ingress-nginx -spec: -… - template: -… - spec: - containers: - * args: - * /nginx-ingress-controller - * --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller - * --election-id=ingress-nginx-leader - * --controller-class=k8s.io/ingress-nginx - * --ingress-class=nginx - * --configmap=$(POD_NAMESPACE)/ingress-nginx-controller - * --validating-webhook=:8443 - * --validating-webhook-certificate=/usr/local/certificates/cert - * --validating-webhook-key=/usr/local/certificates/key - * --enable-ssl-passthrough - * --tcp-services-configmap=ingress-nginx/tcp-services -… -``` + ``` + apiVersion: apps/v1 + kind: Deployment + metadata: + … + name: ingress-nginx-controller + namespace: ingress-nginx + spec: + … + template: + … + spec: + containers: + + args: + + /nginx-ingress-controller + + --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller + + --election-id=ingress-nginx-leader + + --controller-class=k8s.io/ingress-nginx + + --ingress-class=nginx + + --configmap=$(POD_NAMESPACE)/ingress-nginx-controller + + --validating-webhook=:8443 + + --validating-webhook-certificate=/usr/local/certificates/cert + + --validating-webhook-key=/usr/local/certificates/key + + --enable-ssl-passthrough + + --tcp-services-configmap=ingress-nginx/tcp-services + … + ``` - You must add the appropriate ports (minimally TCP ports 5044 and 9200) to the `ingress-nginx-controller` load-balancer service definition: - -``` ---- -apiVersion: v1 -kind: Service -metadata: -… - name: ingress-nginx-controller - namespace: ingress-nginx -spec: - externalTrafficPolicy: Local - ipFamilies: - + IPv4 - ipFamilyPolicy: SingleStack - ports: - + appProtocol: http - name: http - port: 80 - protocol: TCP - targetPort: http - + appProtocol: https - name: https - port: 443 - protocol: TCP - targetPort: https - + appProtocol: tcp - name: lumberjack - port: 5044 - targetPort: 5044 - protocol: TCP - + appProtocol: tcp - name: tcpjson - port: 5045 - targetPort: 5045 - protocol: TCP -+ appProtocol: tcp - name: opensearch - port: 9200 - targetPort: 9200 - protocol: TCP -… - type: LoadBalancer -``` - - - You must add the appropriate ports (minimally TCP ports 5044 and 9200) to the `ingress-nginx-controller` deployment container's definition: - -``` -apiVersion: apps/v1 -kind: Deployment -metadata: -… - name: ingress-nginx-controller - namespace: ingress-nginx -spec: -… - template: -… - spec: - containers: -… + ``` + --- + apiVersion: v1 + kind: Service + metadata: + … + name: ingress-nginx-controller + namespace: ingress-nginx + spec: + externalTrafficPolicy: Local + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack ports: - - containerPort: 80 + - appProtocol: http name: http + port: 80 protocol: TCP - - containerPort: 443 + targetPort: http + - appProtocol: https name: https + port: 443 protocol: TCP - - containerPort: 8443 - name: webhook - protocol: TCP - - name: lumberjack - containerPort: 5044 + targetPort: https + - appProtocol: tcp + name: lumberjack + port: 5044 + targetPort: 5044 protocol: TCP - - name: tcpjson - containerPort: 5045 + - appProtocol: tcp + name: tcpjson + port: 5045 + targetPort: 5045 protocol: TCP - - name: opensearch - containerPort: 9200 + - appProtocol: tcp + name: opensearch + port: 9200 + targetPort: 9200 protocol: TCP -… -``` + … + type: LoadBalancer + ``` -* To use [SSL Passthrough](https://kubernetes.github.io/ingress-nginx/user-guide/tls/) to have the Kubernetes gateway use Malcolm's TLS certificates rather than its own: + - You must add the appropriate ports (minimally TCP ports 5044 and 9200) to the `ingress-nginx-controller` deployment container's definition: + ``` + apiVersion: apps/v1 + kind: Deployment + metadata: + … + name: ingress-nginx-controller + namespace: ingress-nginx + spec: + … + template: + … + spec: + containers: + … + ports: + * containerPort: 80 + name: http + protocol: TCP + * containerPort: 443 + name: https + protocol: TCP + * containerPort: 8443 + name: webhook + protocol: TCP + * name: lumberjack + containerPort: 5044 + protocol: TCP + * name: tcpjson + containerPort: 5045 + protocol: TCP + * name: opensearch + containerPort: 9200 + protocol: TCP + … + ``` +* To use [SSL Passthrough](https://kubernetes.github.io/ingress-nginx/user-guide/tls/) to have the Kubernetes gateway use Malcolm's TLS certificates rather than its own: - You must configure the controller to start up with the `--enable-ssl-passthrough` flag. - -``` -apiVersion: apps/v1 -kind: Deployment -metadata: -… - name: ingress-nginx-controller - namespace: ingress-nginx -spec: -… - template: -… - spec: - containers: - - args: - - /nginx-ingress-controller - - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller - - --election-id=ingress-nginx-leader - - --controller-class=k8s.io/ingress-nginx - - --ingress-class=nginx - - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller - - --validating-webhook=:8443 - - --validating-webhook-certificate=/usr/local/certificates/cert - - --validating-webhook-key=/usr/local/certificates/key - - --enable-ssl-passthrough - - --tcp-services-configmap=ingress-nginx/tcp-services -… -``` + ``` + apiVersion: apps/v1 + kind: Deployment + metadata: + … + name: ingress-nginx-controller + namespace: ingress-nginx + spec: + … + template: + … + spec: + containers: + * args: + * /nginx-ingress-controller + * --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller + * --election-id=ingress-nginx-leader + * --controller-class=k8s.io/ingress-nginx + * --ingress-class=nginx + * --configmap=$(POD_NAMESPACE)/ingress-nginx-controller + * --validating-webhook=:8443 + * --validating-webhook-certificate=/usr/local/certificates/cert + * --validating-webhook-key=/usr/local/certificates/key + * --enable-ssl-passthrough + * --tcp-services-configmap=ingress-nginx/tcp-services + … + ``` - You must modify Malcolm's [ingress controller manifest]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/kubernetes/00-ingress.yml) to specify the `host:` value and use [host-based routing](https://kubernetes.github.io/ingress-nginx/user-guide/basic-usage/): -``` -… -spec: - rules: - - host: malcolm.example.org - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: nginx-proxy - port: - number: 443 -… -``` + ``` + … + spec: + rules: + + host: malcolm.example.org + http: + paths: + + path: / + pathType: Prefix + backend: + service: + name: nginx-proxy + port: + number: 443 + … + ``` ### Kubernetes Provider Settings @@ -191,21 +186,21 @@ OpenSearch has some [important settings](https://opensearch.org/docs/latest/inst Settings which likely need to be changed in the underlying host running Kubernetes include: * System settings (e.g., in `/etc/sysctl.conf`) -``` -# the maximum number of memory map areas a process may have -vm.max_map_count=262144 -``` + ``` + # the maximum number of memory map areas a process may have + vm.max_map_count=262144 + ``` * System limits (e.g., in `/etc/security/limits.d/limits.conf`) -``` -* soft nofile 65535 -* hard nofile 65535 -* soft memlock unlimited -* hard memlock unlimited -* soft nproc 262144 -* hard nproc 524288 -* soft core 0 -* hard core 0 -``` + ``` + + soft nofile 65535 + + hard nofile 65535 + + soft memlock unlimited + + hard memlock unlimited + + soft nproc 262144 + + hard nproc 524288 + + soft core 0 + + hard core 0 + ``` ## Configuration From 1753dcd854eb922be5d39c1a3323eb61ebc0aa07 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 15:10:53 -0600 Subject: [PATCH 204/239] work in progress for idaholab/Malcolm#173, documentation changes for kubernetes deployment --- docs/kubernetes.md | 349 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 346 insertions(+), 3 deletions(-) diff --git a/docs/kubernetes.md b/docs/kubernetes.md index a8a7b509f..65e74ad47 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -4,9 +4,11 @@ - [System](#System) + [Ingress Controller](#Ingress) + [Kubernetes Provider Settings](#Limits) -- [Configuration](#Config) - + [OpenSearch Instances](#OpenSearchInstances) - + [PersistentVolumeClaim Definitions](#PVC) +* [Configuration](#Config) + - [OpenSearch Instances](#OpenSearchInstances) + - [PersistentVolumeClaim Definitions](#PVC) +* [Running Malcolm](#Running) +* [Deployment Example](#Example) * [Future Enhancements](#Future) - [Live Traffic Analysis](#FutureLiveCap) - [Horizontal Scaling](#FutureScaleOut) @@ -237,6 +239,347 @@ Exception: Storage objects required by Malcolm are not defined in /home/user/Mal ## Running Malcolm +After you've [configured](#Config) Malcolm, use the `./scripts/start` script to create the Malcolm Kubernetes deployment, providing your kubeconfig file with the `-f`/`--file` argument: + +``` +$ ./scripts/start -f /path/to/kubeconfig.yml +``` + +The Kubernetes resources under the `malcolm` namespace (its pods, storage volumes, containers, etc.) will be initialized and started using the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/), including: + +* creating [ConfigMap objects](https://kubernetes.io/docs/concepts/configuration/configmap/) from Malcolm's [environment variable files](malcolm-config.md#MalcolmConfigEnvVars) +* creating [ConfigMap objects](https://kubernetes.io/docs/concepts/configuration/configmap/) from other configuration files stored locally below the Malcolm directory +* deploying the objects defined in the [Kubernetes manifests]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/kubernetes/) in `./kubernetes` + +After a few moments you can check the status of the deployment: + +``` +$ ./scripts/status -f /path/to/kubeconfig.yml +Node Name | Hostname | IP | Provider ID | Instance Type | Total CPU | CPU Usage | Percent CPU | Total Memory | Memory Usage | Total Storage | Current Pods | +server | server | 192.168.56.10 | server | k3s | 4000m | 30.37m | 0.76% | 7.77Gi | 1.2Gi | 61.28Gi | 7 | +agent2 | agent2 | 192.168.56.12 | agent2 | k3s | 6000m | 156.42m | 2.61% | 19.55Gi | 14.47Gi | 61.28Gi | 13 | +agent1 | agent1 | 192.168.56.11 | agent1 | k3s | 6000m | 861.34m | 14.36% | 19.55Gi | 9.29Gi | 61.28Gi | 11 | + +Pod Name | State | Pod IP | Pod Kind | Worker Node | CPU Usage | Memory Usage | Container Name:Restarts | Container Image | +api-deployment-6f4686cf59-bn286 | Running | 10.42.2.14 | ReplicaSet | agent1 | 0.11m | 59.62Mi | api-container:0 | api:kubernetes | +file-monitor-deployment-855646bd75-vk7st | Running | 10.42.2.16 | ReplicaSet | agent1 | 8.47m | 1.46Gi | file-monitor-container:0 | file-monitor:kubernetes | +zeek-live-deployment-64b69d4b6f-947vr | Running | 10.42.2.17 | ReplicaSet | agent1 | 0.02m | 12.44Mi | zeek-live-container:0 | zeek:kubernetes | +dashboards-helper-deployment-69dc54f6b6-ln4sq | Running | 10.42.2.15 | ReplicaSet | agent1 | 10.77m | 38.43Mi | dashboards-helper-container:0 | dashboards-helper:kubernetes | +upload-deployment-586568844b-4jnk9 | Running | 10.42.2.18 | ReplicaSet | agent1 | 0.15m | 29.78Mi | upload-container:0 | file-upload:kubernetes | +filebeat-deployment-6ff8bc444f-t7h49 | Running | 10.42.2.20 | ReplicaSet | agent1 | 2.84m | 70.71Mi | filebeat-container:0 | filebeat-oss:kubernetes | +zeek-offline-deployment-844f4865bd-g2sdm | Running | 10.42.2.21 | ReplicaSet | agent1 | 0.17m | 41.92Mi | zeek-offline-container:0 | zeek:kubernetes | +logstash-deployment-6fbc9fdcd5-hwx8s | Running | 10.42.2.22 | ReplicaSet | agent1 | 85.55m | 2.91Gi | logstash-container:0 | logstash-oss:kubernetes | +netbox-deployment-cdcff4977-hbbw5 | Running | 10.42.2.23 | ReplicaSet | agent1 | 807.64m | 702.86Mi | netbox-container:0 | netbox:kubernetes | +suricata-offline-deployment-6ccdb89478-z5696 | Running | 10.42.2.19 | ReplicaSet | agent1 | 0.22m | 34.88Mi | suricata-offline-container:0 | suricata:kubernetes | +dashboards-deployment-69b5465db-vz88g | Running | 10.42.1.14 | ReplicaSet | agent2 | 0.94m | 100.12Mi | dashboards-container:0 | dashboards:kubernetes | +netbox-redis-cache-deployment-5f77d47b8b-z7t2z | Running | 10.42.1.15 | ReplicaSet | agent2 | 3.57m | 7.36Mi | netbox-redis-cache-container:0 | redis:kubernetes | +suricata-live-deployment-6494c77759-9rlnt | Running | 10.42.1.16 | ReplicaSet | agent2 | 0.02m | 9.69Mi | suricata-live-container:0 | suricata:kubernetes | +freq-deployment-cfd84fd97-dnngf | Running | 10.42.1.17 | ReplicaSet | agent2 | 0.2m | 26.36Mi | freq-container:0 | freq:kubernetes | +arkime-deployment-56999cdd66-s98pp | Running | 10.42.1.18 | ReplicaSet | agent2 | 4.15m | 113.07Mi | arkime-container:0 | arkime:kubernetes | +pcap-monitor-deployment-594ff674c4-fsm7m | Running | 10.42.1.19 | ReplicaSet | agent2 | 1.24m | 48.44Mi | pcap-monitor-container:0 | pcap-monitor:kubernetes | +pcap-capture-deployment-7c8bf6957-jzpzn | Running | 10.42.1.20 | ReplicaSet | agent2 | 0.02m | 9.64Mi | pcap-capture-container:0 | pcap-capture:kubernetes | +netbox-postgres-deployment-5879b8dffc-kkt56 | Running | 10.42.1.21 | ReplicaSet | agent2 | 70.91m | 33.02Mi | netbox-postgres-container:0 | postgresql:kubernetes | +htadmin-deployment-6fc46888b9-sq6ln | Running | 10.42.1.23 | ReplicaSet | agent2 | 0.14m | 30.53Mi | htadmin-container:0 | htadmin:kubernetes | +netbox-redis-deployment-5bcd8f6c96-j5xpf | Running | 10.42.1.24 | ReplicaSet | agent2 | 1.46m | 7.34Mi | netbox-redis-container:0 | redis:kubernetes | +nginx-proxy-deployment-69fcc4968d-f68tq | Running | 10.42.1.22 | ReplicaSet | agent2 | 0.31m | 22.63Mi | nginx-proxy-container:0 | nginx-proxy:kubernetes | +opensearch-deployment-75498799f6-4zmwd | Running | 10.42.1.25 | ReplicaSet | agent2 | 89.8m | 11.03Gi | opensearch-container:0 | opensearch:kubernetes | +``` + +The other control scripts (`stop`, `restart`, `logs`, etc.) work in a similar manner as in a Docker-based deployment. One notable difference is the `wipe` script: data on PersistentVolume storage cannot be deleted by `wipe`. It must be deleted manually on the storage media underlying the PersistentVolumes. + +Malcolm's control scripts require the [official Python 3 client library for Kubernetes](https://github.com/kubernetes-client/python) to configure and run Malcolm with Kubernetes. It is also recommended to install **[stern](https://github.com/stern/stern)**, which is used by the `./scripts/logs` script to tail Malcolm's container logs. + +# Deployment Example + +Here's a basic step-by-step example illustrating how to deploy Malcolm with Kubernetes. For the sake of simplicity, this example uses vagrant (see [kubernetes/vagrant/Vagrantfile]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/kubernetes/vagrant/Vagrantfile)) to create a virtualized Kubernetes cluster with one control plane node and two worker nodes. It assumes you've downloaded and extracted the [release tarball]({{ site.github.repository_url }}/releases) or used `./scripts/malcolm_appliance_packager.sh` to package up the files needed to run Malcolm. + +``` +$ ls -l +total 45,056 +drwxr-xr-x 2 user user 6 Apr 24 14:35 arkime-logs +drwxr-xr-x 2 user user 6 Apr 24 14:35 arkime-raw +drwxr-xr-x 2 user user 4,096 Apr 24 14:35 config +drwxr-xr-x 3 user user 19 Apr 24 14:35 filebeat +drwxr-xr-x 2 user user 6 Apr 24 14:35 htadmin +drwxr-xr-x 3 user user 4,096 Apr 24 14:39 kubernetes +drwxr-xr-x 4 user user 31 Apr 24 14:35 logstash +drwxr-xr-x 6 user user 62 Apr 24 14:35 netbox +drwxr-xr-x 4 user user 35 Apr 24 14:35 nginx +drwxr-xr-x 3 user user 19 Apr 24 14:35 opensearch +drwxr-xr-x 2 user user 6 Apr 24 14:35 opensearch-backup +drwxr-xr-x 4 user user 37 Apr 24 14:35 pcap +drwxr-xr-x 2 user user 4,096 Apr 24 14:35 scripts +drwxr-xr-x 3 user user 19 Apr 24 14:35 suricata +drwxr-xr-x 3 user user 18 Apr 24 14:35 suricata-logs +drwxr-xr-x 3 user user 19 Apr 24 14:35 yara +drwxr-xr-x 3 user user 19 Apr 24 14:35 zeek +drwxr-xr-x 7 user user 85 Apr 24 14:35 zeek-logs +-rw-r--r-- 1 user user 18,761 Apr 24 14:35 docker-compose.yml +-rw-r--r-- 1 user user 2 Apr 24 14:35 net-map.json +-rw-r--r-- 1 user user 3,453 Apr 24 14:35 README.md +``` + +Even before starting Malcolm, we can use the `status` script to make sure we're communicating with the Kubernetes cluster: + +``` +$ ./scripts/status -f /path/to/kubeconfig.yaml +Node Name | Hostname | IP | Provider ID | Instance Type | Total CPU | CPU Usage | Percent CPU | Total Memory | Memory Usage | Total Storage | Current Pods | +agent2 | agent2 | 192.168.56.12 | agent2 | k3s | 6000m | 32.06m | 0.53% | 19.55Gi | 346.3Mi | 61.28Gi | 1 | +agent1 | agent1 | 192.168.56.11 | agent1 | k3s | 6000m | 26.7m | 0.45% | 19.55Gi | 353.2Mi | 61.28Gi | 1 | +server | server | 192.168.56.10 | server | k3s | 4000m | 290.15m | 7.25% | 7.77Gi | 1.04Gi | 61.28Gi | 7 | + +Pod Name | State | Pod IP | Pod Kind | Worker Node | CPU Usage | Memory Usage | Container Name:Restarts | Container Image | +``` + +Run `./scripts/configure` and answer the questions to configure Malcolm. For an in-depth treatment of these configuration questions, see the **Configuration** section in **[End-to-end Malcolm and Hedgehog Linux ISO Installation](malcolm-hedgehog-e2e-iso-install.md#MalcolmConfig)**: + + +``` +$ ./scripts/configure -f /path/to/kubeconfig.yaml + +Malcolm processes will run as UID 1000 and GID 1000. Is this OK? (Y/n): y + +Should Malcolm use and maintain its own OpenSearch instance? (Y/n): y + +Forward Logstash logs to a secondary remote OpenSearch instance? (y/N): n + +Setting 16g for OpenSearch and 3g for Logstash. Is this OK? (Y/n): y + +Setting 6 workers for Logstash pipelines. Is this OK? (Y/n): y + +Require encrypted HTTPS connections? (Y/n): y + +1: Basic +2: Lightweight Directory Access Protocol (LDAP) +3: None +Select authentication method (Basic): 1 + +Delete the oldest indices when the database exceeds a certain size? (y/N): y + +Enter index threshold (e.g., 250GB, 1TB, 60%, etc.): 250G + +Determine oldest indices by name (instead of creation time)? (Y/n): y + +Should Arkime delete PCAP files based on available storage (see https://arkime.com/faq#pcap-deletion)? (y/N): y + +Automatically analyze all PCAP files with Suricata? (Y/n): y + +Download updated Suricata signatures periodically? (y/N): y + +Automatically analyze all PCAP files with Zeek? (Y/n): y + +Should Malcolm use "best guess" to identify potential OT/ICS traffic with Zeek? (y/N): n + +Perform reverse DNS lookup locally for source and destination IP addresses in logs? (y/N): n + +Perform hardware vendor OUI lookups for MAC addresses? (Y/n): y + +Perform string randomness scoring on some fields? (Y/n): y + +Use default field values for Filebeat TCP listener? (Y/n): y + +Enable file extraction with Zeek? (y/N): y +1: none +2: known +3: mapped +4: all +5: interesting + +Select file extraction behavior (none): 5 +1: quarantined +2: all +3: none +Select file preservation behavior (quarantined): 1 + +Expose web interface for downloading preserved files? (y/N): y + +Enter AES-256-CBC encryption password for downloaded preserved files (or leave blank for unencrypted): quarantined + +Scan extracted files with ClamAV? (Y/n): y + +Scan extracted files with Yara? (Y/n): y + +Scan extracted PE files with Capa? (Y/n): y + +Lookup extracted file hashes with VirusTotal? (y/N): n + +Download updated file scanner signatures periodically? (y/N): y + +Should Malcolm run and maintain an instance of NetBox, an infrastructure resource modeling tool? (y/N): y + +Should Malcolm enrich network traffic using NetBox? (Y/n): y + +Specify default NetBox site name: Malcolm + +Enable dark mode for OpenSearch Dashboards? (Y/n): y + +Malcolm has been installed to /home/user/Malcolm. See README.md for more information. + +Scripts for starting and stopping Malcolm and changing authentication-related settings can be found in /home/user/Malcolm/scripts. +``` + +Run `./scripts/auth_setup` and answer the questions to [configure authentication](authsetup.md#AuthSetup): + +``` +$ ./scripts/auth_setup -f /path/to/kubeconfig.yaml + +1: all - Configure all authentication-related settings +2: admin - Store administrator username/password for local Malcolm access +3: webcerts - (Re)generate self-signed certificates for HTTPS access +4: fwcerts - (Re)generate self-signed certificates for a remote log forwarder +5: remoteos - Configure remote primary or secondary OpenSearch instance +6: email - Store username/password for email alert sender account +7: netbox - (Re)generate internal passwords for NetBox +8: txfwcerts - Transfer self-signed client certificates to a remote log forwarder + +Configure Authentication (all): 1 + +Store administrator username/password for local Malcolm access? (Y/n): y + +Administrator username: analyst +analyst password: +analyst password (again): + +Additional local accounts can be created at https://localhost/auth/ when Malcolm is running + +(Re)generate self-signed certificates for HTTPS access? (Y/n): y + +(Re)generate self-signed certificates for a remote log forwarder? (Y/n): y + +Configure remote primary or secondary OpenSearch instance? (y/N): n + +Store username/password for email alert sender account? (y/N): n + +(Re)generate internal passwords for NetBox? (Y/n): y + +Transfer self-signed client certificates to a remote log forwarder? (y/N): n + +``` + +Next, copy `./kubernetes/01-volumes.yml.example` to `./kubernetes/01-volumes.yml` and edit that file to define the [required PersistentVolumeClaims](#PVC) there. + +``` +$ cp -v ./kubernetes/01-volumes.yml.example ./kubernetes/01-volumes.yml +'./kubernetes/01-volumes.yml.example' -> './kubernetes/01-volumes.yml' + +$ vi ./kubernetes/01-volumes.yml +… + +$ grep -A 3 PersistentVolumeClaim ./kubernetes/01-volumes.yml +kind: PersistentVolumeClaim +metadata: + name: pcap-claim + namespace: malcolm +-- +kind: PersistentVolumeClaim +metadata: + name: zeek-claim + namespace: malcolm +-- +kind: PersistentVolumeClaim +metadata: + name: suricata-claim + namespace: malcolm +-- +kind: PersistentVolumeClaim +metadata: + name: config-claim + namespace: malcolm +-- +kind: PersistentVolumeClaim +metadata: + name: runtime-logs-claim + namespace: malcolm +-- +kind: PersistentVolumeClaim +metadata: + name: opensearch-claim + namespace: malcolm +-- +kind: PersistentVolumeClaim +metadata: + name: opensearch-backup-claim + namespace: malcolm + +``` + +Start Malcolm: + +``` +$ ./scripts/start -f /path/to/kubeconfig.yaml +… +logstash | [2023-04-24T21:00:34,470][INFO ][logstash.agent ] Pipelines running {:count=>6, :running_pipelines=>[:"malcolm-input", :"malcolm-output", :"malcolm-suricata", :"malcolm-beats", :"malcolm-enrichment", :"malcolm-zeek"], :non_running_pipelines=>[]} + +Started Malcolm + +Malcolm services can be accessed via the following URLs: +------------------------------------------------------------------------------ + - Arkime: https://192.168.56.10/ + - OpenSearch Dashboards: https://192.168.56.10/dashboards/ + - PCAP upload (web): https://192.168.56.10/upload/ + - NetBox: https://192.168.56.10/netbox/ + - Account management: https://192.168.56.10/auth/ + - Documentation: https://192.168.56.10/readme/ + +``` + +Check the status of the Malcolm deployment with `./scripts/status`: + +``` +$ ./scripts/status -f /path/to/kubeconfig.yaml + +Node Name | Hostname | IP | Provider ID | Instance Type | Total CPU | CPU Usage | Percent CPU | Total Memory | Memory Usage | Total Storage | Current Pods | +server | server | 192.168.56.10 | server | k3s | 4000m | 47.03m | 1.18% | 7.77Gi | 1.14Gi | 61.28Gi | 7 | +agent1 | agent1 | 192.168.56.11 | agent1 | k3s | 6000m | 3677.42m | 61.29% | 19.55Gi | 4.95Gi | 61.28Gi | 12 | +agent2 | agent2 | 192.168.56.12 | agent2 | k3s | 6000m | 552.71m | 9.21% | 19.55Gi | 13.27Gi | 61.28Gi | 12 | + +Pod Name | State | Pod IP | Pod Kind | Worker Node | CPU Usage | Memory Usage | Container Name:Restarts | Container Image | +netbox-redis-cache-deployment-5f77d47b8b-jr9nt | Running | 10.42.2.6 | ReplicaSet | agent2 | 1.89m | 7.24Mi | netbox-redis-cache-container:0 | redis:kubernetes | +netbox-redis-deployment-5bcd8f6c96-bkzmh | Running | 10.42.2.5 | ReplicaSet | agent2 | 1.62m | 7.52Mi | netbox-redis-container:0 | redis:kubernetes | +dashboards-helper-deployment-69dc54f6b6-ks7ps | Running | 10.42.2.4 | ReplicaSet | agent2 | 12.95m | 40.75Mi | dashboards-helper-container:0 | dashboards-helper:kubernetes | +freq-deployment-cfd84fd97-5bwp6 | Running | 10.42.2.8 | ReplicaSet | agent2 | 0.11m | 26.33Mi | freq-container:0 | freq:kubernetes | +pcap-capture-deployment-7c8bf6957-hkvkn | Running | 10.42.2.12 | ReplicaSet | agent2 | 0.02m | 9.21Mi | pcap-capture-container:0 | pcap-capture:kubernetes | +nginx-proxy-deployment-69fcc4968d-m57rz | Running | 10.42.2.10 | ReplicaSet | agent2 | 0.91m | 22.72Mi | nginx-proxy-container:0 | nginx-proxy:kubernetes | +htadmin-deployment-6fc46888b9-vpt7l | Running | 10.42.2.7 | ReplicaSet | agent2 | 0.16m | 30.21Mi | htadmin-container:0 | htadmin:kubernetes | +opensearch-deployment-75498799f6-5v92w | Running | 10.42.2.13 | ReplicaSet | agent2 | 139.2m | 10.86Gi | opensearch-container:0 | opensearch:kubernetes | +zeek-live-deployment-64b69d4b6f-fcb6n | Running | 10.42.2.9 | ReplicaSet | agent2 | 0.02m | 109.55Mi | zeek-live-container:0 | zeek:kubernetes | +dashboards-deployment-69b5465db-kgsqk | Running | 10.42.2.3 | ReplicaSet | agent2 | 14.98m | 108.85Mi | dashboards-container:0 | dashboards:kubernetes | +arkime-deployment-56999cdd66-xxpw9 | Running | 10.42.2.11 | ReplicaSet | agent2 | 208.95m | 78.42Mi | arkime-container:0 | arkime:kubernetes | +api-deployment-6f4686cf59-xt9md | Running | 10.42.1.3 | ReplicaSet | agent1 | 0.14m | 56.88Mi | api-container:0 | api:kubernetes | +netbox-postgres-deployment-5879b8dffc-lb4qm | Running | 10.42.1.6 | ReplicaSet | agent1 | 141.2m | 48.02Mi | netbox-postgres-container:0 | postgresql:kubernetes | +pcap-monitor-deployment-594ff674c4-fwq7g | Running | 10.42.1.12 | ReplicaSet | agent1 | 3.93m | 46.44Mi | pcap-monitor-container:0 | pcap-monitor:kubernetes | +suricata-offline-deployment-6ccdb89478-j5fgj | Running | 10.42.1.10 | ReplicaSet | agent1 | 10.42m | 35.12Mi | suricata-offline-container:0 | suricata:kubernetes | +suricata-live-deployment-6494c77759-rpt48 | Running | 10.42.1.8 | ReplicaSet | agent1 | 0.01m | 9.62Mi | suricata-live-container:0 | suricata:kubernetes | +netbox-deployment-cdcff4977-7ns2q | Running | 10.42.1.7 | ReplicaSet | agent1 | 830.47m | 530.7Mi | netbox-container:0 | netbox:kubernetes | +zeek-offline-deployment-844f4865bd-7x68b | Running | 10.42.1.9 | ReplicaSet | agent1 | 1.44m | 43.66Mi | zeek-offline-container:0 | zeek:kubernetes | +filebeat-deployment-6ff8bc444f-pdgzj | Running | 10.42.1.11 | ReplicaSet | agent1 | 0.78m | 75.25Mi | filebeat-container:0 | filebeat-oss:kubernetes | +file-monitor-deployment-855646bd75-nbngq | Running | 10.42.1.4 | ReplicaSet | agent1 | 1.69m | 1.46Gi | file-monitor-container:0 | file-monitor:kubernetes | +upload-deployment-586568844b-9s7f5 | Running | 10.42.1.13 | ReplicaSet | agent1 | 0.14m | 29.62Mi | upload-container:0 | file-upload:kubernetes | +logstash-deployment-6fbc9fdcd5-2hhx8 | Running | 10.42.1.5 | ReplicaSet | agent1 | 3236.29m | 357.36Mi | logstash-container:0 | logstash-oss:kubernetes | +``` + +View container logs for the Malcolm deployment with `./scripts/logs` (if **[stern](https://github.com/stern/stern)** present in `$PATH`): + +``` +$ ./scripts/logs -f /path/to/kubeconfig.yaml +api | [2023-04-24 20:55:59 +0000] [7] [INFO] Booting worker with pid: 7 +dashboards | log [20:59:28.784] [info][server][OpenSearchDashboards][http] http server running at http://0.0.0.0:5601/dashboards +file-monitor | 2023-04-24 20:59:38 INFO: ۞ started [1] +freq | 2023-04-24 20:57:09,481 INFO success: freq entered RUNNING state, process has stayed up for > than 5 seconds (startsecs) +htadmin | 2023-04-24 20:58:04,724 INFO success: nginx entered RUNNING state, process has stayed up for > than 15 seconds (startsecs) +opensearch | [2023-04-24T21:00:18,442][WARN ][o.o.c.m.MetadataIndexTemplateService] [opensearch-deployment-75498799f6-5v92w] index template [malcolm_template] has index patterns [arkime_sessions3-*] matching patterns from existing older templates [arkime_sessions3_ecs_template,arkime_sessions3_template] with patterns (arkime_sessions3_ecs_template => [arkime_sessions3-*],arkime_sessions3_template => [arkime_sessions3-*]); this template [malcolm_template] will take precedence during new index creation +pcap-capture | 8:57PM INF Listening at http://0.0.0.0:80 /... +pcap-monitor | 2023-04-24 20:59:53 INFO: ۞ started [1] +upload | 2023-04-24 20:59:27,496 INFO success: nginx entered RUNNING state, process has stayed up for > than 15 seconds (startsecs) +zeek-live | 8:59PM INF Listening at http://0.0.0.0:80 /... +zeek-offline | 2023-04-24 20:58:16,072 INFO success: pcap-zeek entered RUNNING state, process has stayed up for > than 15 seconds (startsecs) +suricata-live | 8:57PM INF Listening at http://0.0.0.0:80 /... +logstash | [2023-04-24T21:00:34,470][INFO ][logstash.agent ] Pipelines running {:count=>6, :running_pipelines=>[:"malcolm-input", :"malcolm-output", :"malcolm-suricata", :"malcolm-beats", :"malcolm-enrichment", :"malcolm-zeek"], :non_running_pipelines=>[]} +… +``` + +The Malcolm [user interface](quickstart.md#UserInterfaceURLs) should be accessible at the IP address or hostname of the Kubernetes ingress controller. + # Future Enhancements Deploying Malcolm with Kubernetes is a new (and still somewhat experimental) feature, and does not yet support the full range of Malcolm features. Development around these features is [ongoing](https://github.com/idaholab/Malcolm/issues?q=is%3Aissue+is%3Aopen+kubernetes). Some of the notable features that are still a work in progress for Kubernetes deployment include: From 3332cf499362415dc03b39a268d8ec37dac0c7a9 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 15:17:37 -0600 Subject: [PATCH 205/239] work in progress for idaholab/Malcolm#173, documentation changes for kubernetes deployment --- docs/kubernetes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/kubernetes.md b/docs/kubernetes.md index 65e74ad47..da97abdfa 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -594,4 +594,4 @@ For now, the Malcolm services running in Kubernetes are configured with `replica ## Helm Chart -For now, Malcolm's Kubernetes deployment is managed via vanilla [Kubernetes manifests]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/kubernetes/). We need to [look into](https://github.com/idaholab/Malcolm/issues/187) what a Malcolm Helm chart would look like and how it would fit in with the [deployment scripts](https://github.com/idaholab/Malcolm/issues/172) for [configuring](#Config) and [running](#Running) Malcolm, if at all. \ No newline at end of file +For now, Malcolm's Kubernetes deployment is managed via vanilla [Kubernetes manifests]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/kubernetes/). We need to [look into](https://github.com/idaholab/Malcolm/issues/187) what a Malcolm Helm chart would look like and how it would fit in with the [deployment scripts](https://github.com/idaholab/Malcolm/issues/172) for [configuring](#Config) and [running](#Running) Malcolm, if at all. From 175d890b81b40557d6b8b0a5b0fd7704976eefe0 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 15:39:45 -0600 Subject: [PATCH 206/239] restore development branch links --- _config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_config.yml b/_config.yml index f6d466466..0e14813af 100644 --- a/_config.yml +++ b/_config.yml @@ -6,8 +6,8 @@ remote_theme: pages-themes/minimal@v0.2.0 external_download_url: https://malcolm.fyi/docs/download.html youtube_url: https://www.youtube.com/@MalcolmNetworkTrafficAnalysis mastodon: - id: - url: + id: malcolm@malcolm.fyi + url: https://infosec.exchange/@mmguero docs_uri: docs/ alerting_docs_uri: docs/alerting.html anomaly_detection_docs_uri: docs/anomaly-detection.html From 59f886482d7d6b837b6ada366efabcc216552578 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 15:43:30 -0600 Subject: [PATCH 207/239] update supercronic --- Dockerfiles/dashboards-helper.Dockerfile | 4 ++-- Dockerfiles/file-monitor.Dockerfile | 4 ++-- Dockerfiles/filebeat.Dockerfile | 4 ++-- Dockerfiles/netbox.Dockerfile | 4 ++-- Dockerfiles/suricata.Dockerfile | 6 +++--- Dockerfiles/zeek.Dockerfile | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Dockerfiles/dashboards-helper.Dockerfile b/Dockerfiles/dashboards-helper.Dockerfile index 3682a99db..73a410f0e 100644 --- a/Dockerfiles/dashboards-helper.Dockerfile +++ b/Dockerfiles/dashboards-helper.Dockerfile @@ -47,10 +47,10 @@ ENV DASHBOARDS_URL $DASHBOARDS_URL ENV DASHBOARDS_DARKMODE $DASHBOARDS_DARKMODE ENV PATH="/data:${PATH}" -ENV SUPERCRONIC_VERSION "0.2.23" +ENV SUPERCRONIC_VERSION "0.2.24" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" ENV SUPERCRONIC "supercronic-linux-amd64" -ENV SUPERCRONIC_SHA1SUM "dbed853c06aba2c611bbb1955b2c1667b51dcb0a" +ENV SUPERCRONIC_SHA1SUM "6817299e04457e5d6ec4809c72ee13a43e95ba41" ENV SUPERCRONIC_CRONTAB "/etc/crontab" ENV ECS_RELEASES_URL "https://api.github.com/repos/elastic/ecs/releases/latest" diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index 395ac4a15..a8cfb03ab 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -91,10 +91,10 @@ ENV EXTRACTED_FILE_HTTP_SERVER_ENCRYPT $EXTRACTED_FILE_HTTP_SERVER_ENCRYPT ENV EXTRACTED_FILE_HTTP_SERVER_KEY $EXTRACTED_FILE_HTTP_SERVER_KEY ENV EXTRACTED_FILE_HTTP_SERVER_PORT $EXTRACTED_FILE_HTTP_SERVER_PORT -ENV SUPERCRONIC_VERSION "0.2.23" +ENV SUPERCRONIC_VERSION "0.2.24" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" ENV SUPERCRONIC "supercronic-linux-amd64" -ENV SUPERCRONIC_SHA1SUM "dbed853c06aba2c611bbb1955b2c1667b51dcb0a" +ENV SUPERCRONIC_SHA1SUM "6817299e04457e5d6ec4809c72ee13a43e95ba41" ENV SUPERCRONIC_CRONTAB "/etc/crontab" COPY --chmod=755 shared/bin/yara_rules_setup.sh /usr/local/bin/ diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index 8d84c8fd6..f3bcd19d0 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -59,10 +59,10 @@ ARG FILEBEAT_TCP_PARSE_TARGET_FIELD="" ARG FILEBEAT_TCP_PARSE_DROP_FIELD="" ARG FILEBEAT_TCP_TAG="_malcolm_beats" -ENV SUPERCRONIC_VERSION "0.2.23" +ENV SUPERCRONIC_VERSION "0.2.24" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" ENV SUPERCRONIC "supercronic-linux-amd64" -ENV SUPERCRONIC_SHA1SUM "dbed853c06aba2c611bbb1955b2c1667b51dcb0a" +ENV SUPERCRONIC_SHA1SUM "6817299e04457e5d6ec4809c72ee13a43e95ba41" ENV SUPERCRONIC_CRONTAB "/etc/crontab" ENV TINI_VERSION v0.19.0 diff --git a/Dockerfiles/netbox.Dockerfile b/Dockerfiles/netbox.Dockerfile index 9e5abdd6d..a546542aa 100644 --- a/Dockerfiles/netbox.Dockerfile +++ b/Dockerfiles/netbox.Dockerfile @@ -22,10 +22,10 @@ ENV PUSER "boxer" ENV PGROUP "boxer" ENV PUSER_PRIV_DROP true -ENV SUPERCRONIC_VERSION "0.2.23" +ENV SUPERCRONIC_VERSION "0.2.24" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" ENV SUPERCRONIC "supercronic-linux-amd64" -ENV SUPERCRONIC_SHA1SUM "dbed853c06aba2c611bbb1955b2c1667b51dcb0a" +ENV SUPERCRONIC_SHA1SUM "6817299e04457e5d6ec4809c72ee13a43e95ba41" ENV SUPERCRONIC_CRONTAB "/etc/crontab" ENV NETBOX_DEVICETYPE_LIBRARY_URL "https://codeload.github.com/netbox-community/devicetype-library/tar.gz/master" diff --git a/Dockerfiles/suricata.Dockerfile b/Dockerfiles/suricata.Dockerfile index 6961c5b97..0d1303a91 100644 --- a/Dockerfiles/suricata.Dockerfile +++ b/Dockerfiles/suricata.Dockerfile @@ -28,13 +28,13 @@ ENV PGROUP "suricata" ENV PUSER_PRIV_DROP false ENV PUSER_RLIMIT_UNLOCK true -ENV SUPERCRONIC_VERSION "0.2.23" +ENV SUPERCRONIC_VERSION "0.2.24" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" ENV SUPERCRONIC "supercronic-linux-amd64" -ENV SUPERCRONIC_SHA1SUM "dbed853c06aba2c611bbb1955b2c1667b51dcb0a" +ENV SUPERCRONIC_SHA1SUM "6817299e04457e5d6ec4809c72ee13a43e95ba41" ENV SUPERCRONIC_CRONTAB "/etc/crontab" -ENV YQ_VERSION "4.24.2" +ENV YQ_VERSION "4.33.3" ENV YQ_URL "https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/yq_linux_amd64" ENV SURICATA_CONFIG_DIR /etc/suricata diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index 2eb9706f4..9b2c9988b 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -37,10 +37,10 @@ ARG ZEEK_VERSION=5.2.1-0 ENV ZEEK_LTS $ZEEK_LTS ENV ZEEK_VERSION $ZEEK_VERSION -ENV SUPERCRONIC_VERSION "0.2.23" +ENV SUPERCRONIC_VERSION "0.2.24" ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" ENV SUPERCRONIC "supercronic-linux-amd64" -ENV SUPERCRONIC_SHA1SUM "dbed853c06aba2c611bbb1955b2c1667b51dcb0a" +ENV SUPERCRONIC_SHA1SUM "6817299e04457e5d6ec4809c72ee13a43e95ba41" ENV SUPERCRONIC_CRONTAB "/etc/crontab" # for build From 3eb1d7e723eed2062e47150289ecd687ac431e23 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 17:20:18 -0600 Subject: [PATCH 208/239] set development versions to v23.04.1 --- docker-compose-standalone.yml | 44 ++++++++--------- docker-compose.yml | 44 ++++++++--------- docs/download.md | 4 +- docs/hedgehog-iso-build.md | 2 +- docs/kubernetes.md | 88 +++++++++++++++++----------------- docs/malcolm-iso.md | 2 +- docs/quickstart.md | 38 +++++++-------- docs/ubuntu-install-example.md | 38 +++++++-------- 8 files changed, 130 insertions(+), 130 deletions(-) diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 4d4763d26..4066ea3ee 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -4,7 +4,7 @@ version: '3.7' services: opensearch: - image: ghcr.io/idaholab/malcolm/opensearch:kubernetes + image: ghcr.io/idaholab/malcolm/opensearch:23.04.1 restart: "no" stdin_open: false tty: true @@ -37,7 +37,7 @@ services: retries: 3 start_period: 180s dashboards-helper: - image: ghcr.io/idaholab/malcolm/dashboards-helper:kubernetes + image: ghcr.io/idaholab/malcolm/dashboards-helper:23.04.1 restart: "no" stdin_open: false tty: true @@ -64,7 +64,7 @@ services: retries: 3 start_period: 30s dashboards: - image: ghcr.io/idaholab/malcolm/dashboards:kubernetes + image: ghcr.io/idaholab/malcolm/dashboards:23.04.1 restart: "no" stdin_open: false tty: true @@ -90,7 +90,7 @@ services: retries: 3 start_period: 210s logstash: - image: ghcr.io/idaholab/malcolm/logstash-oss:kubernetes + image: ghcr.io/idaholab/malcolm/logstash-oss:23.04.1 restart: "no" stdin_open: false tty: true @@ -131,7 +131,7 @@ services: retries: 3 start_period: 600s filebeat: - image: ghcr.io/idaholab/malcolm/filebeat-oss:kubernetes + image: ghcr.io/idaholab/malcolm/filebeat-oss:23.04.1 restart: "no" stdin_open: false tty: true @@ -166,7 +166,7 @@ services: retries: 3 start_period: 60s arkime: - image: ghcr.io/idaholab/malcolm/arkime:kubernetes + image: ghcr.io/idaholab/malcolm/arkime:23.04.1 restart: "no" stdin_open: false tty: true @@ -201,7 +201,7 @@ services: retries: 3 start_period: 210s zeek: - image: ghcr.io/idaholab/malcolm/zeek:kubernetes + image: ghcr.io/idaholab/malcolm/zeek:23.04.1 restart: "no" stdin_open: false tty: true @@ -238,7 +238,7 @@ services: retries: 3 start_period: 60s zeek-live: - image: ghcr.io/idaholab/malcolm/zeek:kubernetes + image: ghcr.io/idaholab/malcolm/zeek:23.04.1 restart: "no" stdin_open: false tty: true @@ -265,7 +265,7 @@ services: - ./zeek-logs/extract_files:/zeek/extract_files - ./zeek/intel:/opt/zeek/share/zeek/site/intel suricata: - image: ghcr.io/idaholab/malcolm/suricata:kubernetes + image: ghcr.io/idaholab/malcolm/suricata:23.04.1 restart: "no" stdin_open: false tty: true @@ -301,7 +301,7 @@ services: retries: 3 start_period: 120s suricata-live: - image: ghcr.io/idaholab/malcolm/suricata:kubernetes + image: ghcr.io/idaholab/malcolm/suricata:23.04.1 restart: "no" stdin_open: false tty: true @@ -327,7 +327,7 @@ services: - ./suricata-logs:/var/log/suricata - ./suricata/rules:/opt/suricata/rules:ro file-monitor: - image: ghcr.io/idaholab/malcolm/file-monitor:kubernetes + image: ghcr.io/idaholab/malcolm/file-monitor:23.04.1 restart: "no" stdin_open: false tty: true @@ -352,7 +352,7 @@ services: retries: 3 start_period: 60s pcap-capture: - image: ghcr.io/idaholab/malcolm/pcap-capture:kubernetes + image: ghcr.io/idaholab/malcolm/pcap-capture:23.04.1 restart: "no" stdin_open: false tty: true @@ -374,7 +374,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/pcap pcap-monitor: - image: ghcr.io/idaholab/malcolm/pcap-monitor:kubernetes + image: ghcr.io/idaholab/malcolm/pcap-monitor:23.04.1 restart: "no" stdin_open: false tty: true @@ -400,7 +400,7 @@ services: retries: 3 start_period: 90s upload: - image: ghcr.io/idaholab/malcolm/file-upload:kubernetes + image: ghcr.io/idaholab/malcolm/file-upload:23.04.1 restart: "no" stdin_open: false tty: true @@ -428,7 +428,7 @@ services: retries: 3 start_period: 60s htadmin: - image: ghcr.io/idaholab/malcolm/htadmin:kubernetes + image: ghcr.io/idaholab/malcolm/htadmin:23.04.1 restart: "no" stdin_open: false tty: true @@ -453,7 +453,7 @@ services: retries: 3 start_period: 60s freq: - image: ghcr.io/idaholab/malcolm/freq:kubernetes + image: ghcr.io/idaholab/malcolm/freq:23.04.1 restart: "no" stdin_open: false tty: true @@ -475,7 +475,7 @@ services: retries: 3 start_period: 60s netbox: - image: ghcr.io/idaholab/malcolm/netbox:kubernetes + image: ghcr.io/idaholab/malcolm/netbox:23.04.1 restart: "no" stdin_open: false tty: true @@ -507,7 +507,7 @@ services: retries: 3 start_period: 120s netbox-postgres: - image: ghcr.io/idaholab/malcolm/postgresql:kubernetes + image: ghcr.io/idaholab/malcolm/postgresql:23.04.1 restart: "no" stdin_open: false tty: true @@ -531,7 +531,7 @@ services: retries: 3 start_period: 45s netbox-redis: - image: ghcr.io/idaholab/malcolm/redis:kubernetes + image: ghcr.io/idaholab/malcolm/redis:23.04.1 restart: "no" stdin_open: false tty: true @@ -559,7 +559,7 @@ services: retries: 3 start_period: 45s netbox-redis-cache: - image: ghcr.io/idaholab/malcolm/redis:kubernetes + image: ghcr.io/idaholab/malcolm/redis:23.04.1 restart: "no" stdin_open: false tty: true @@ -586,7 +586,7 @@ services: retries: 3 start_period: 45s api: - image: ghcr.io/idaholab/malcolm/api:kubernetes + image: ghcr.io/idaholab/malcolm/api:23.04.1 command: gunicorn --bind 0:5000 manage:app restart: "no" stdin_open: false @@ -610,7 +610,7 @@ services: retries: 3 start_period: 60s nginx-proxy: - image: ghcr.io/idaholab/malcolm/nginx-proxy:kubernetes + image: ghcr.io/idaholab/malcolm/nginx-proxy:23.04.1 restart: "no" stdin_open: false tty: true diff --git a/docker-compose.yml b/docker-compose.yml index 6135775f9..f47da7a76 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: build: context: . dockerfile: Dockerfiles/opensearch.Dockerfile - image: ghcr.io/idaholab/malcolm/opensearch:kubernetes + image: ghcr.io/idaholab/malcolm/opensearch:23.04.1 restart: "no" stdin_open: false tty: true @@ -43,7 +43,7 @@ services: build: context: . dockerfile: Dockerfiles/dashboards-helper.Dockerfile - image: ghcr.io/idaholab/malcolm/dashboards-helper:kubernetes + image: ghcr.io/idaholab/malcolm/dashboards-helper:23.04.1 restart: "no" stdin_open: false tty: true @@ -73,7 +73,7 @@ services: build: context: . dockerfile: Dockerfiles/dashboards.Dockerfile - image: ghcr.io/idaholab/malcolm/dashboards:kubernetes + image: ghcr.io/idaholab/malcolm/dashboards:23.04.1 restart: "no" stdin_open: false tty: true @@ -102,7 +102,7 @@ services: build: context: . dockerfile: Dockerfiles/logstash.Dockerfile - image: ghcr.io/idaholab/malcolm/logstash-oss:kubernetes + image: ghcr.io/idaholab/malcolm/logstash-oss:23.04.1 restart: "no" stdin_open: false tty: true @@ -150,7 +150,7 @@ services: build: context: . dockerfile: Dockerfiles/filebeat.Dockerfile - image: ghcr.io/idaholab/malcolm/filebeat-oss:kubernetes + image: ghcr.io/idaholab/malcolm/filebeat-oss:23.04.1 restart: "no" stdin_open: false tty: true @@ -188,7 +188,7 @@ services: build: context: . dockerfile: Dockerfiles/arkime.Dockerfile - image: ghcr.io/idaholab/malcolm/arkime:kubernetes + image: ghcr.io/idaholab/malcolm/arkime:23.04.1 restart: "no" stdin_open: false tty: true @@ -229,7 +229,7 @@ services: build: context: . dockerfile: Dockerfiles/zeek.Dockerfile - image: ghcr.io/idaholab/malcolm/zeek:kubernetes + image: ghcr.io/idaholab/malcolm/zeek:23.04.1 restart: "no" stdin_open: false tty: true @@ -270,7 +270,7 @@ services: build: context: . dockerfile: Dockerfiles/zeek.Dockerfile - image: ghcr.io/idaholab/malcolm/zeek:kubernetes + image: ghcr.io/idaholab/malcolm/zeek:23.04.1 restart: "no" stdin_open: false tty: true @@ -301,7 +301,7 @@ services: build: context: . dockerfile: Dockerfiles/suricata.Dockerfile - image: ghcr.io/idaholab/malcolm/suricata:kubernetes + image: ghcr.io/idaholab/malcolm/suricata:23.04.1 restart: "no" stdin_open: false tty: true @@ -340,7 +340,7 @@ services: build: context: . dockerfile: Dockerfiles/suricata.Dockerfile - image: ghcr.io/idaholab/malcolm/suricata:kubernetes + image: ghcr.io/idaholab/malcolm/suricata:23.04.1 restart: "no" stdin_open: false tty: true @@ -369,7 +369,7 @@ services: build: context: . dockerfile: Dockerfiles/file-monitor.Dockerfile - image: ghcr.io/idaholab/malcolm/file-monitor:kubernetes + image: ghcr.io/idaholab/malcolm/file-monitor:23.04.1 restart: "no" stdin_open: false tty: true @@ -397,7 +397,7 @@ services: build: context: . dockerfile: Dockerfiles/pcap-capture.Dockerfile - image: ghcr.io/idaholab/malcolm/pcap-capture:kubernetes + image: ghcr.io/idaholab/malcolm/pcap-capture:23.04.1 restart: "no" stdin_open: false tty: true @@ -422,7 +422,7 @@ services: build: context: . dockerfile: Dockerfiles/pcap-monitor.Dockerfile - image: ghcr.io/idaholab/malcolm/pcap-monitor:kubernetes + image: ghcr.io/idaholab/malcolm/pcap-monitor:23.04.1 restart: "no" stdin_open: false tty: true @@ -451,7 +451,7 @@ services: build: context: . dockerfile: Dockerfiles/file-upload.Dockerfile - image: ghcr.io/idaholab/malcolm/file-upload:kubernetes + image: ghcr.io/idaholab/malcolm/file-upload:23.04.1 restart: "no" stdin_open: false tty: true @@ -479,7 +479,7 @@ services: retries: 3 start_period: 60s htadmin: - image: ghcr.io/idaholab/malcolm/htadmin:kubernetes + image: ghcr.io/idaholab/malcolm/htadmin:23.04.1 build: context: . dockerfile: Dockerfiles/htadmin.Dockerfile @@ -507,7 +507,7 @@ services: retries: 3 start_period: 60s freq: - image: ghcr.io/idaholab/malcolm/freq:kubernetes + image: ghcr.io/idaholab/malcolm/freq:23.04.1 build: context: . dockerfile: Dockerfiles/freq.Dockerfile @@ -532,7 +532,7 @@ services: retries: 3 start_period: 60s netbox: - image: ghcr.io/idaholab/malcolm/netbox:kubernetes + image: ghcr.io/idaholab/malcolm/netbox:23.04.1 build: context: . dockerfile: Dockerfiles/netbox.Dockerfile @@ -568,7 +568,7 @@ services: retries: 3 start_period: 120s netbox-postgres: - image: ghcr.io/idaholab/malcolm/postgresql:kubernetes + image: ghcr.io/idaholab/malcolm/postgresql:23.04.1 build: context: . dockerfile: Dockerfiles/postgresql.Dockerfile @@ -595,7 +595,7 @@ services: retries: 3 start_period: 45s netbox-redis: - image: ghcr.io/idaholab/malcolm/redis:kubernetes + image: ghcr.io/idaholab/malcolm/redis:23.04.1 build: context: . dockerfile: Dockerfiles/redis.Dockerfile @@ -626,7 +626,7 @@ services: retries: 3 start_period: 45s netbox-redis-cache: - image: ghcr.io/idaholab/malcolm/redis:kubernetes + image: ghcr.io/idaholab/malcolm/redis:23.04.1 build: context: . dockerfile: Dockerfiles/redis.Dockerfile @@ -656,7 +656,7 @@ services: retries: 3 start_period: 45s api: - image: ghcr.io/idaholab/malcolm/api:kubernetes + image: ghcr.io/idaholab/malcolm/api:23.04.1 build: context: . dockerfile: Dockerfiles/api.Dockerfile @@ -686,7 +686,7 @@ services: build: context: . dockerfile: Dockerfiles/nginx.Dockerfile - image: ghcr.io/idaholab/malcolm/nginx-proxy:kubernetes + image: ghcr.io/idaholab/malcolm/nginx-proxy:23.04.1 restart: "no" stdin_open: false tty: true diff --git a/docs/download.md b/docs/download.md index 2c6703440..2a7f0fc5e 100644 --- a/docs/download.md +++ b/docs/download.md @@ -16,7 +16,7 @@ While official downloads of the Malcolm installer ISO are not provided, an **uno | ISO | SHA256 | |---|---| -| [malcolm-kubernetes.iso](/iso/malcolm-kubernetes.iso) (5.3GiB) | [`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`](/iso/malcolm-kubernetes.iso.sha256.txt) | +| [malcolm-23.04.1.iso](/iso/malcolm-23.04.1.iso) (5.3GiB) | [`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`](/iso/malcolm-23.04.1.iso.sha256.txt) | ## Hedgehog Linux @@ -26,7 +26,7 @@ While official downloads of the Malcolm installer ISO are not provided, an **uno | ISO | SHA256 | |---|---| -| [hedgehog-kubernetes.iso](/iso/hedgehog-kubernetes.iso) (2.3GiB) | [`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`](/iso/hedgehog-kubernetes.iso.sha256.txt) | +| [hedgehog-23.04.1.iso](/iso/hedgehog-23.04.1.iso) (2.3GiB) | [`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`](/iso/hedgehog-23.04.1.iso.sha256.txt) | ## Warning diff --git a/docs/hedgehog-iso-build.md b/docs/hedgehog-iso-build.md index bd3c790d6..746ea6e84 100644 --- a/docs/hedgehog-iso-build.md +++ b/docs/hedgehog-iso-build.md @@ -29,7 +29,7 @@ Building the ISO may take 90 minutes or more depending on your system. As the bu ``` … -Finished, created "/sensor-build/hedgehog-kubernetes.iso" +Finished, created "/sensor-build/hedgehog-23.04.1.iso" … ``` diff --git a/docs/kubernetes.md b/docs/kubernetes.md index da97abdfa..968a6fd39 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -261,28 +261,28 @@ agent2 | agent2 | 192.168.56.12 | agent2 | k3s | 6000m | agent1 | agent1 | 192.168.56.11 | agent1 | k3s | 6000m | 861.34m | 14.36% | 19.55Gi | 9.29Gi | 61.28Gi | 11 | Pod Name | State | Pod IP | Pod Kind | Worker Node | CPU Usage | Memory Usage | Container Name:Restarts | Container Image | -api-deployment-6f4686cf59-bn286 | Running | 10.42.2.14 | ReplicaSet | agent1 | 0.11m | 59.62Mi | api-container:0 | api:kubernetes | -file-monitor-deployment-855646bd75-vk7st | Running | 10.42.2.16 | ReplicaSet | agent1 | 8.47m | 1.46Gi | file-monitor-container:0 | file-monitor:kubernetes | -zeek-live-deployment-64b69d4b6f-947vr | Running | 10.42.2.17 | ReplicaSet | agent1 | 0.02m | 12.44Mi | zeek-live-container:0 | zeek:kubernetes | -dashboards-helper-deployment-69dc54f6b6-ln4sq | Running | 10.42.2.15 | ReplicaSet | agent1 | 10.77m | 38.43Mi | dashboards-helper-container:0 | dashboards-helper:kubernetes | -upload-deployment-586568844b-4jnk9 | Running | 10.42.2.18 | ReplicaSet | agent1 | 0.15m | 29.78Mi | upload-container:0 | file-upload:kubernetes | -filebeat-deployment-6ff8bc444f-t7h49 | Running | 10.42.2.20 | ReplicaSet | agent1 | 2.84m | 70.71Mi | filebeat-container:0 | filebeat-oss:kubernetes | -zeek-offline-deployment-844f4865bd-g2sdm | Running | 10.42.2.21 | ReplicaSet | agent1 | 0.17m | 41.92Mi | zeek-offline-container:0 | zeek:kubernetes | -logstash-deployment-6fbc9fdcd5-hwx8s | Running | 10.42.2.22 | ReplicaSet | agent1 | 85.55m | 2.91Gi | logstash-container:0 | logstash-oss:kubernetes | -netbox-deployment-cdcff4977-hbbw5 | Running | 10.42.2.23 | ReplicaSet | agent1 | 807.64m | 702.86Mi | netbox-container:0 | netbox:kubernetes | -suricata-offline-deployment-6ccdb89478-z5696 | Running | 10.42.2.19 | ReplicaSet | agent1 | 0.22m | 34.88Mi | suricata-offline-container:0 | suricata:kubernetes | -dashboards-deployment-69b5465db-vz88g | Running | 10.42.1.14 | ReplicaSet | agent2 | 0.94m | 100.12Mi | dashboards-container:0 | dashboards:kubernetes | -netbox-redis-cache-deployment-5f77d47b8b-z7t2z | Running | 10.42.1.15 | ReplicaSet | agent2 | 3.57m | 7.36Mi | netbox-redis-cache-container:0 | redis:kubernetes | -suricata-live-deployment-6494c77759-9rlnt | Running | 10.42.1.16 | ReplicaSet | agent2 | 0.02m | 9.69Mi | suricata-live-container:0 | suricata:kubernetes | -freq-deployment-cfd84fd97-dnngf | Running | 10.42.1.17 | ReplicaSet | agent2 | 0.2m | 26.36Mi | freq-container:0 | freq:kubernetes | -arkime-deployment-56999cdd66-s98pp | Running | 10.42.1.18 | ReplicaSet | agent2 | 4.15m | 113.07Mi | arkime-container:0 | arkime:kubernetes | -pcap-monitor-deployment-594ff674c4-fsm7m | Running | 10.42.1.19 | ReplicaSet | agent2 | 1.24m | 48.44Mi | pcap-monitor-container:0 | pcap-monitor:kubernetes | -pcap-capture-deployment-7c8bf6957-jzpzn | Running | 10.42.1.20 | ReplicaSet | agent2 | 0.02m | 9.64Mi | pcap-capture-container:0 | pcap-capture:kubernetes | -netbox-postgres-deployment-5879b8dffc-kkt56 | Running | 10.42.1.21 | ReplicaSet | agent2 | 70.91m | 33.02Mi | netbox-postgres-container:0 | postgresql:kubernetes | -htadmin-deployment-6fc46888b9-sq6ln | Running | 10.42.1.23 | ReplicaSet | agent2 | 0.14m | 30.53Mi | htadmin-container:0 | htadmin:kubernetes | -netbox-redis-deployment-5bcd8f6c96-j5xpf | Running | 10.42.1.24 | ReplicaSet | agent2 | 1.46m | 7.34Mi | netbox-redis-container:0 | redis:kubernetes | -nginx-proxy-deployment-69fcc4968d-f68tq | Running | 10.42.1.22 | ReplicaSet | agent2 | 0.31m | 22.63Mi | nginx-proxy-container:0 | nginx-proxy:kubernetes | -opensearch-deployment-75498799f6-4zmwd | Running | 10.42.1.25 | ReplicaSet | agent2 | 89.8m | 11.03Gi | opensearch-container:0 | opensearch:kubernetes | +api-deployment-6f4686cf59-bn286 | Running | 10.42.2.14 | ReplicaSet | agent1 | 0.11m | 59.62Mi | api-container:0 | api:23.04.1 | +file-monitor-deployment-855646bd75-vk7st | Running | 10.42.2.16 | ReplicaSet | agent1 | 8.47m | 1.46Gi | file-monitor-container:0 | file-monitor:23.04.1 | +zeek-live-deployment-64b69d4b6f-947vr | Running | 10.42.2.17 | ReplicaSet | agent1 | 0.02m | 12.44Mi | zeek-live-container:0 | zeek:23.04.1 | +dashboards-helper-deployment-69dc54f6b6-ln4sq | Running | 10.42.2.15 | ReplicaSet | agent1 | 10.77m | 38.43Mi | dashboards-helper-container:0 | dashboards-helper:23.04.1 | +upload-deployment-586568844b-4jnk9 | Running | 10.42.2.18 | ReplicaSet | agent1 | 0.15m | 29.78Mi | upload-container:0 | file-upload:23.04.1 | +filebeat-deployment-6ff8bc444f-t7h49 | Running | 10.42.2.20 | ReplicaSet | agent1 | 2.84m | 70.71Mi | filebeat-container:0 | filebeat-oss:23.04.1 | +zeek-offline-deployment-844f4865bd-g2sdm | Running | 10.42.2.21 | ReplicaSet | agent1 | 0.17m | 41.92Mi | zeek-offline-container:0 | zeek:23.04.1 | +logstash-deployment-6fbc9fdcd5-hwx8s | Running | 10.42.2.22 | ReplicaSet | agent1 | 85.55m | 2.91Gi | logstash-container:0 | logstash-oss:23.04.1 | +netbox-deployment-cdcff4977-hbbw5 | Running | 10.42.2.23 | ReplicaSet | agent1 | 807.64m | 702.86Mi | netbox-container:0 | netbox:23.04.1 | +suricata-offline-deployment-6ccdb89478-z5696 | Running | 10.42.2.19 | ReplicaSet | agent1 | 0.22m | 34.88Mi | suricata-offline-container:0 | suricata:23.04.1 | +dashboards-deployment-69b5465db-vz88g | Running | 10.42.1.14 | ReplicaSet | agent2 | 0.94m | 100.12Mi | dashboards-container:0 | dashboards:23.04.1 | +netbox-redis-cache-deployment-5f77d47b8b-z7t2z | Running | 10.42.1.15 | ReplicaSet | agent2 | 3.57m | 7.36Mi | netbox-redis-cache-container:0 | redis:23.04.1 | +suricata-live-deployment-6494c77759-9rlnt | Running | 10.42.1.16 | ReplicaSet | agent2 | 0.02m | 9.69Mi | suricata-live-container:0 | suricata:23.04.1 | +freq-deployment-cfd84fd97-dnngf | Running | 10.42.1.17 | ReplicaSet | agent2 | 0.2m | 26.36Mi | freq-container:0 | freq:23.04.1 | +arkime-deployment-56999cdd66-s98pp | Running | 10.42.1.18 | ReplicaSet | agent2 | 4.15m | 113.07Mi | arkime-container:0 | arkime:23.04.1 | +pcap-monitor-deployment-594ff674c4-fsm7m | Running | 10.42.1.19 | ReplicaSet | agent2 | 1.24m | 48.44Mi | pcap-monitor-container:0 | pcap-monitor:23.04.1 | +pcap-capture-deployment-7c8bf6957-jzpzn | Running | 10.42.1.20 | ReplicaSet | agent2 | 0.02m | 9.64Mi | pcap-capture-container:0 | pcap-capture:23.04.1 | +netbox-postgres-deployment-5879b8dffc-kkt56 | Running | 10.42.1.21 | ReplicaSet | agent2 | 70.91m | 33.02Mi | netbox-postgres-container:0 | postgresql:23.04.1 | +htadmin-deployment-6fc46888b9-sq6ln | Running | 10.42.1.23 | ReplicaSet | agent2 | 0.14m | 30.53Mi | htadmin-container:0 | htadmin:23.04.1 | +netbox-redis-deployment-5bcd8f6c96-j5xpf | Running | 10.42.1.24 | ReplicaSet | agent2 | 1.46m | 7.34Mi | netbox-redis-container:0 | redis:23.04.1 | +nginx-proxy-deployment-69fcc4968d-f68tq | Running | 10.42.1.22 | ReplicaSet | agent2 | 0.31m | 22.63Mi | nginx-proxy-container:0 | nginx-proxy:23.04.1 | +opensearch-deployment-75498799f6-4zmwd | Running | 10.42.1.25 | ReplicaSet | agent2 | 89.8m | 11.03Gi | opensearch-container:0 | opensearch:23.04.1 | ``` The other control scripts (`stop`, `restart`, `logs`, etc.) work in a similar manner as in a Docker-based deployment. One notable difference is the `wipe` script: data on PersistentVolume storage cannot be deleted by `wipe`. It must be deleted manually on the storage media underlying the PersistentVolumes. @@ -534,28 +534,28 @@ agent1 | agent1 | 192.168.56.11 | agent1 | k3s | 6000m | agent2 | agent2 | 192.168.56.12 | agent2 | k3s | 6000m | 552.71m | 9.21% | 19.55Gi | 13.27Gi | 61.28Gi | 12 | Pod Name | State | Pod IP | Pod Kind | Worker Node | CPU Usage | Memory Usage | Container Name:Restarts | Container Image | -netbox-redis-cache-deployment-5f77d47b8b-jr9nt | Running | 10.42.2.6 | ReplicaSet | agent2 | 1.89m | 7.24Mi | netbox-redis-cache-container:0 | redis:kubernetes | -netbox-redis-deployment-5bcd8f6c96-bkzmh | Running | 10.42.2.5 | ReplicaSet | agent2 | 1.62m | 7.52Mi | netbox-redis-container:0 | redis:kubernetes | -dashboards-helper-deployment-69dc54f6b6-ks7ps | Running | 10.42.2.4 | ReplicaSet | agent2 | 12.95m | 40.75Mi | dashboards-helper-container:0 | dashboards-helper:kubernetes | -freq-deployment-cfd84fd97-5bwp6 | Running | 10.42.2.8 | ReplicaSet | agent2 | 0.11m | 26.33Mi | freq-container:0 | freq:kubernetes | -pcap-capture-deployment-7c8bf6957-hkvkn | Running | 10.42.2.12 | ReplicaSet | agent2 | 0.02m | 9.21Mi | pcap-capture-container:0 | pcap-capture:kubernetes | -nginx-proxy-deployment-69fcc4968d-m57rz | Running | 10.42.2.10 | ReplicaSet | agent2 | 0.91m | 22.72Mi | nginx-proxy-container:0 | nginx-proxy:kubernetes | -htadmin-deployment-6fc46888b9-vpt7l | Running | 10.42.2.7 | ReplicaSet | agent2 | 0.16m | 30.21Mi | htadmin-container:0 | htadmin:kubernetes | -opensearch-deployment-75498799f6-5v92w | Running | 10.42.2.13 | ReplicaSet | agent2 | 139.2m | 10.86Gi | opensearch-container:0 | opensearch:kubernetes | -zeek-live-deployment-64b69d4b6f-fcb6n | Running | 10.42.2.9 | ReplicaSet | agent2 | 0.02m | 109.55Mi | zeek-live-container:0 | zeek:kubernetes | -dashboards-deployment-69b5465db-kgsqk | Running | 10.42.2.3 | ReplicaSet | agent2 | 14.98m | 108.85Mi | dashboards-container:0 | dashboards:kubernetes | -arkime-deployment-56999cdd66-xxpw9 | Running | 10.42.2.11 | ReplicaSet | agent2 | 208.95m | 78.42Mi | arkime-container:0 | arkime:kubernetes | -api-deployment-6f4686cf59-xt9md | Running | 10.42.1.3 | ReplicaSet | agent1 | 0.14m | 56.88Mi | api-container:0 | api:kubernetes | -netbox-postgres-deployment-5879b8dffc-lb4qm | Running | 10.42.1.6 | ReplicaSet | agent1 | 141.2m | 48.02Mi | netbox-postgres-container:0 | postgresql:kubernetes | -pcap-monitor-deployment-594ff674c4-fwq7g | Running | 10.42.1.12 | ReplicaSet | agent1 | 3.93m | 46.44Mi | pcap-monitor-container:0 | pcap-monitor:kubernetes | -suricata-offline-deployment-6ccdb89478-j5fgj | Running | 10.42.1.10 | ReplicaSet | agent1 | 10.42m | 35.12Mi | suricata-offline-container:0 | suricata:kubernetes | -suricata-live-deployment-6494c77759-rpt48 | Running | 10.42.1.8 | ReplicaSet | agent1 | 0.01m | 9.62Mi | suricata-live-container:0 | suricata:kubernetes | -netbox-deployment-cdcff4977-7ns2q | Running | 10.42.1.7 | ReplicaSet | agent1 | 830.47m | 530.7Mi | netbox-container:0 | netbox:kubernetes | -zeek-offline-deployment-844f4865bd-7x68b | Running | 10.42.1.9 | ReplicaSet | agent1 | 1.44m | 43.66Mi | zeek-offline-container:0 | zeek:kubernetes | -filebeat-deployment-6ff8bc444f-pdgzj | Running | 10.42.1.11 | ReplicaSet | agent1 | 0.78m | 75.25Mi | filebeat-container:0 | filebeat-oss:kubernetes | -file-monitor-deployment-855646bd75-nbngq | Running | 10.42.1.4 | ReplicaSet | agent1 | 1.69m | 1.46Gi | file-monitor-container:0 | file-monitor:kubernetes | -upload-deployment-586568844b-9s7f5 | Running | 10.42.1.13 | ReplicaSet | agent1 | 0.14m | 29.62Mi | upload-container:0 | file-upload:kubernetes | -logstash-deployment-6fbc9fdcd5-2hhx8 | Running | 10.42.1.5 | ReplicaSet | agent1 | 3236.29m | 357.36Mi | logstash-container:0 | logstash-oss:kubernetes | +netbox-redis-cache-deployment-5f77d47b8b-jr9nt | Running | 10.42.2.6 | ReplicaSet | agent2 | 1.89m | 7.24Mi | netbox-redis-cache-container:0 | redis:23.04.1 | +netbox-redis-deployment-5bcd8f6c96-bkzmh | Running | 10.42.2.5 | ReplicaSet | agent2 | 1.62m | 7.52Mi | netbox-redis-container:0 | redis:23.04.1 | +dashboards-helper-deployment-69dc54f6b6-ks7ps | Running | 10.42.2.4 | ReplicaSet | agent2 | 12.95m | 40.75Mi | dashboards-helper-container:0 | dashboards-helper:23.04.1 | +freq-deployment-cfd84fd97-5bwp6 | Running | 10.42.2.8 | ReplicaSet | agent2 | 0.11m | 26.33Mi | freq-container:0 | freq:23.04.1 | +pcap-capture-deployment-7c8bf6957-hkvkn | Running | 10.42.2.12 | ReplicaSet | agent2 | 0.02m | 9.21Mi | pcap-capture-container:0 | pcap-capture:23.04.1 | +nginx-proxy-deployment-69fcc4968d-m57rz | Running | 10.42.2.10 | ReplicaSet | agent2 | 0.91m | 22.72Mi | nginx-proxy-container:0 | nginx-proxy:23.04.1 | +htadmin-deployment-6fc46888b9-vpt7l | Running | 10.42.2.7 | ReplicaSet | agent2 | 0.16m | 30.21Mi | htadmin-container:0 | htadmin:23.04.1 | +opensearch-deployment-75498799f6-5v92w | Running | 10.42.2.13 | ReplicaSet | agent2 | 139.2m | 10.86Gi | opensearch-container:0 | opensearch:23.04.1 | +zeek-live-deployment-64b69d4b6f-fcb6n | Running | 10.42.2.9 | ReplicaSet | agent2 | 0.02m | 109.55Mi | zeek-live-container:0 | zeek:23.04.1 | +dashboards-deployment-69b5465db-kgsqk | Running | 10.42.2.3 | ReplicaSet | agent2 | 14.98m | 108.85Mi | dashboards-container:0 | dashboards:23.04.1 | +arkime-deployment-56999cdd66-xxpw9 | Running | 10.42.2.11 | ReplicaSet | agent2 | 208.95m | 78.42Mi | arkime-container:0 | arkime:23.04.1 | +api-deployment-6f4686cf59-xt9md | Running | 10.42.1.3 | ReplicaSet | agent1 | 0.14m | 56.88Mi | api-container:0 | api:23.04.1 | +netbox-postgres-deployment-5879b8dffc-lb4qm | Running | 10.42.1.6 | ReplicaSet | agent1 | 141.2m | 48.02Mi | netbox-postgres-container:0 | postgresql:23.04.1 | +pcap-monitor-deployment-594ff674c4-fwq7g | Running | 10.42.1.12 | ReplicaSet | agent1 | 3.93m | 46.44Mi | pcap-monitor-container:0 | pcap-monitor:23.04.1 | +suricata-offline-deployment-6ccdb89478-j5fgj | Running | 10.42.1.10 | ReplicaSet | agent1 | 10.42m | 35.12Mi | suricata-offline-container:0 | suricata:23.04.1 | +suricata-live-deployment-6494c77759-rpt48 | Running | 10.42.1.8 | ReplicaSet | agent1 | 0.01m | 9.62Mi | suricata-live-container:0 | suricata:23.04.1 | +netbox-deployment-cdcff4977-7ns2q | Running | 10.42.1.7 | ReplicaSet | agent1 | 830.47m | 530.7Mi | netbox-container:0 | netbox:23.04.1 | +zeek-offline-deployment-844f4865bd-7x68b | Running | 10.42.1.9 | ReplicaSet | agent1 | 1.44m | 43.66Mi | zeek-offline-container:0 | zeek:23.04.1 | +filebeat-deployment-6ff8bc444f-pdgzj | Running | 10.42.1.11 | ReplicaSet | agent1 | 0.78m | 75.25Mi | filebeat-container:0 | filebeat-oss:23.04.1 | +file-monitor-deployment-855646bd75-nbngq | Running | 10.42.1.4 | ReplicaSet | agent1 | 1.69m | 1.46Gi | file-monitor-container:0 | file-monitor:23.04.1 | +upload-deployment-586568844b-9s7f5 | Running | 10.42.1.13 | ReplicaSet | agent1 | 0.14m | 29.62Mi | upload-container:0 | file-upload:23.04.1 | +logstash-deployment-6fbc9fdcd5-2hhx8 | Running | 10.42.1.5 | ReplicaSet | agent1 | 3236.29m | 357.36Mi | logstash-container:0 | logstash-oss:23.04.1 | ``` View container logs for the Malcolm deployment with `./scripts/logs` (if **[stern](https://github.com/stern/stern)** present in `$PATH`): diff --git a/docs/malcolm-iso.md b/docs/malcolm-iso.md index 89fcf1f23..9f29b5615 100644 --- a/docs/malcolm-iso.md +++ b/docs/malcolm-iso.md @@ -41,7 +41,7 @@ Building the ISO may take 30 minutes or more depending on your system. As the bu ``` … -Finished, created "/malcolm-build/malcolm-iso/malcolm-kubernetes.iso" +Finished, created "/malcolm-build/malcolm-iso/malcolm-23.04.1.iso" … ``` diff --git a/docs/quickstart.md b/docs/quickstart.md index ebf741e77..57253e0ae 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -54,25 +54,25 @@ You can then observe that the images have been retrieved by running `docker imag ``` $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE -ghcr.io/idaholab/malcolm/api kubernetes xxxxxxxxxxxx 3 days ago 158MB -ghcr.io/idaholab/malcolm/arkime kubernetes xxxxxxxxxxxx 3 days ago 816MB -ghcr.io/idaholab/malcolm/dashboards kubernetes xxxxxxxxxxxx 3 days ago 1.02GB -ghcr.io/idaholab/malcolm/dashboards-helper kubernetes xxxxxxxxxxxx 3 days ago 184MB -ghcr.io/idaholab/malcolm/file-monitor kubernetes xxxxxxxxxxxx 3 days ago 588MB -ghcr.io/idaholab/malcolm/file-upload kubernetes xxxxxxxxxxxx 3 days ago 259MB -ghcr.io/idaholab/malcolm/filebeat-oss kubernetes xxxxxxxxxxxx 3 days ago 624MB -ghcr.io/idaholab/malcolm/freq kubernetes xxxxxxxxxxxx 3 days ago 132MB -ghcr.io/idaholab/malcolm/htadmin kubernetes xxxxxxxxxxxx 3 days ago 242MB -ghcr.io/idaholab/malcolm/logstash-oss kubernetes xxxxxxxxxxxx 3 days ago 1.35GB -ghcr.io/idaholab/malcolm/netbox kubernetes xxxxxxxxxxxx 3 days ago 1.01GB -ghcr.io/idaholab/malcolm/nginx-proxy kubernetes xxxxxxxxxxxx 3 days ago 121MB -ghcr.io/idaholab/malcolm/opensearch kubernetes xxxxxxxxxxxx 3 days ago 1.17GB -ghcr.io/idaholab/malcolm/pcap-capture kubernetes xxxxxxxxxxxx 3 days ago 121MB -ghcr.io/idaholab/malcolm/pcap-monitor kubernetes xxxxxxxxxxxx 3 days ago 213MB -ghcr.io/idaholab/malcolm/postgresql kubernetes xxxxxxxxxxxx 3 days ago 268MB -ghcr.io/idaholab/malcolm/redis kubernetes xxxxxxxxxxxx 3 days ago 34.2MB -ghcr.io/idaholab/malcolm/suricata kubernetes xxxxxxxxxxxx 3 days ago 278MB -ghcr.io/idaholab/malcolm/zeek kubernetes xxxxxxxxxxxx 3 days ago 1GB +ghcr.io/idaholab/malcolm/api 23.04.1 xxxxxxxxxxxx 3 days ago 158MB +ghcr.io/idaholab/malcolm/arkime 23.04.1 xxxxxxxxxxxx 3 days ago 816MB +ghcr.io/idaholab/malcolm/dashboards 23.04.1 xxxxxxxxxxxx 3 days ago 1.02GB +ghcr.io/idaholab/malcolm/dashboards-helper 23.04.1 xxxxxxxxxxxx 3 days ago 184MB +ghcr.io/idaholab/malcolm/file-monitor 23.04.1 xxxxxxxxxxxx 3 days ago 588MB +ghcr.io/idaholab/malcolm/file-upload 23.04.1 xxxxxxxxxxxx 3 days ago 259MB +ghcr.io/idaholab/malcolm/filebeat-oss 23.04.1 xxxxxxxxxxxx 3 days ago 624MB +ghcr.io/idaholab/malcolm/freq 23.04.1 xxxxxxxxxxxx 3 days ago 132MB +ghcr.io/idaholab/malcolm/htadmin 23.04.1 xxxxxxxxxxxx 3 days ago 242MB +ghcr.io/idaholab/malcolm/logstash-oss 23.04.1 xxxxxxxxxxxx 3 days ago 1.35GB +ghcr.io/idaholab/malcolm/netbox 23.04.1 xxxxxxxxxxxx 3 days ago 1.01GB +ghcr.io/idaholab/malcolm/nginx-proxy 23.04.1 xxxxxxxxxxxx 3 days ago 121MB +ghcr.io/idaholab/malcolm/opensearch 23.04.1 xxxxxxxxxxxx 3 days ago 1.17GB +ghcr.io/idaholab/malcolm/pcap-capture 23.04.1 xxxxxxxxxxxx 3 days ago 121MB +ghcr.io/idaholab/malcolm/pcap-monitor 23.04.1 xxxxxxxxxxxx 3 days ago 213MB +ghcr.io/idaholab/malcolm/postgresql 23.04.1 xxxxxxxxxxxx 3 days ago 268MB +ghcr.io/idaholab/malcolm/redis 23.04.1 xxxxxxxxxxxx 3 days ago 34.2MB +ghcr.io/idaholab/malcolm/suricata 23.04.1 xxxxxxxxxxxx 3 days ago 278MB +ghcr.io/idaholab/malcolm/zeek 23.04.1 xxxxxxxxxxxx 3 days ago 1GB ``` ### Import from pre-packaged tarballs diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index fed210823..8b683a785 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -265,25 +265,25 @@ Pulling zeek ... done user@host:~/Malcolm$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE -ghcr.io/idaholab/malcolm/api kubernetes xxxxxxxxxxxx 3 days ago 158MB -ghcr.io/idaholab/malcolm/arkime kubernetes xxxxxxxxxxxx 3 days ago 816MB -ghcr.io/idaholab/malcolm/dashboards kubernetes xxxxxxxxxxxx 3 days ago 1.02GB -ghcr.io/idaholab/malcolm/dashboards-helper kubernetes xxxxxxxxxxxx 3 days ago 184MB -ghcr.io/idaholab/malcolm/file-monitor kubernetes xxxxxxxxxxxx 3 days ago 588MB -ghcr.io/idaholab/malcolm/file-upload kubernetes xxxxxxxxxxxx 3 days ago 259MB -ghcr.io/idaholab/malcolm/filebeat-oss kubernetes xxxxxxxxxxxx 3 days ago 624MB -ghcr.io/idaholab/malcolm/freq kubernetes xxxxxxxxxxxx 3 days ago 132MB -ghcr.io/idaholab/malcolm/htadmin kubernetes xxxxxxxxxxxx 3 days ago 242MB -ghcr.io/idaholab/malcolm/logstash-oss kubernetes xxxxxxxxxxxx 3 days ago 1.35GB -ghcr.io/idaholab/malcolm/netbox kubernetes xxxxxxxxxxxx 3 days ago 1.01GB -ghcr.io/idaholab/malcolm/nginx-proxy kubernetes xxxxxxxxxxxx 3 days ago 121MB -ghcr.io/idaholab/malcolm/opensearch kubernetes xxxxxxxxxxxx 3 days ago 1.17GB -ghcr.io/idaholab/malcolm/pcap-capture kubernetes xxxxxxxxxxxx 3 days ago 121MB -ghcr.io/idaholab/malcolm/pcap-monitor kubernetes xxxxxxxxxxxx 3 days ago 213MB -ghcr.io/idaholab/malcolm/postgresql kubernetes xxxxxxxxxxxx 3 days ago 268MB -ghcr.io/idaholab/malcolm/redis kubernetes xxxxxxxxxxxx 3 days ago 34.2MB -ghcr.io/idaholab/malcolm/suricata kubernetes xxxxxxxxxxxx 3 days ago 278MB -ghcr.io/idaholab/malcolm/zeek kubernetes xxxxxxxxxxxx 3 days ago 1GB +ghcr.io/idaholab/malcolm/api 23.04.1 xxxxxxxxxxxx 3 days ago 158MB +ghcr.io/idaholab/malcolm/arkime 23.04.1 xxxxxxxxxxxx 3 days ago 816MB +ghcr.io/idaholab/malcolm/dashboards 23.04.1 xxxxxxxxxxxx 3 days ago 1.02GB +ghcr.io/idaholab/malcolm/dashboards-helper 23.04.1 xxxxxxxxxxxx 3 days ago 184MB +ghcr.io/idaholab/malcolm/file-monitor 23.04.1 xxxxxxxxxxxx 3 days ago 588MB +ghcr.io/idaholab/malcolm/file-upload 23.04.1 xxxxxxxxxxxx 3 days ago 259MB +ghcr.io/idaholab/malcolm/filebeat-oss 23.04.1 xxxxxxxxxxxx 3 days ago 624MB +ghcr.io/idaholab/malcolm/freq 23.04.1 xxxxxxxxxxxx 3 days ago 132MB +ghcr.io/idaholab/malcolm/htadmin 23.04.1 xxxxxxxxxxxx 3 days ago 242MB +ghcr.io/idaholab/malcolm/logstash-oss 23.04.1 xxxxxxxxxxxx 3 days ago 1.35GB +ghcr.io/idaholab/malcolm/netbox 23.04.1 xxxxxxxxxxxx 3 days ago 1.01GB +ghcr.io/idaholab/malcolm/nginx-proxy 23.04.1 xxxxxxxxxxxx 3 days ago 121MB +ghcr.io/idaholab/malcolm/opensearch 23.04.1 xxxxxxxxxxxx 3 days ago 1.17GB +ghcr.io/idaholab/malcolm/pcap-capture 23.04.1 xxxxxxxxxxxx 3 days ago 121MB +ghcr.io/idaholab/malcolm/pcap-monitor 23.04.1 xxxxxxxxxxxx 3 days ago 213MB +ghcr.io/idaholab/malcolm/postgresql 23.04.1 xxxxxxxxxxxx 3 days ago 268MB +ghcr.io/idaholab/malcolm/redis 23.04.1 xxxxxxxxxxxx 3 days ago 34.2MB +ghcr.io/idaholab/malcolm/suricata 23.04.1 xxxxxxxxxxxx 3 days ago 278MB +ghcr.io/idaholab/malcolm/zeek 23.04.1 xxxxxxxxxxxx 3 days ago 1GB ``` Finally, we can start Malcolm. When Malcolm starts it will stream informational and debug messages to the console. If you wish, you can safely close the console or use `Ctrl+C` to stop these messages; Malcolm will continue running in the background. From 2fef7b3e4f8a7dabfa353dae68db1c843cc8cd9a Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 24 Apr 2023 21:15:12 -0600 Subject: [PATCH 209/239] work for idaholab/Malcolm#172 script consolidation --- scripts/control.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/scripts/control.py b/scripts/control.py index 21e04b840..6a9129d3b 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -195,8 +195,8 @@ def keystore_op(service, dropPriv=False, *keystore_args, **run_process_kwargs): localKeystore = None localKeystoreDir = None localKeystorePreExists = False - volumeKeystore = None - volumeKeystoreDir = None + volumeKeystore = f"/usr/share/{service}/config/{service}.keystore" + volumeKeystoreDir = os.path.dirname(volumeKeystore) try: composeFileLines = list() @@ -208,16 +208,14 @@ def keystore_op(service, dropPriv=False, *keystore_args, **run_process_kwargs): if (len(composeFileLines) == 1) and (len(composeFileLines[0]) > 0): matches = re.search( - fr'-\s*(?P.*?{service}.keystore)\s*:\s*(?P.*?{service}.keystore)', + fr'-\s*(?P.*?{service}.keystore)\s*:\s*.*?{service}.keystore', composeFileLines[0], ) if matches: localKeystore = os.path.realpath(matches.group('localKeystore')) localKeystoreDir = os.path.dirname(localKeystore) - volumeKeystore = matches.group('volumeKeystore') - volumeKeystoreDir = os.path.dirname(volumeKeystore) - if (localKeystore is not None) and (volumeKeystore is not None) and os.path.isdir(localKeystoreDir): + if (localKeystore is not None) and os.path.isdir(localKeystoreDir): localKeystorePreExists = os.path.isfile(localKeystore) dockerCmd = None From 38a80a11f4cf91fef8cdb284b09062b999155c0b Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 25 Apr 2023 07:25:26 -0600 Subject: [PATCH 210/239] added some support libraries to malcolm ISO --- .../hooks/normal/0910-agg-build.hook.chroot | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/malcolm-iso/config/hooks/normal/0910-agg-build.hook.chroot b/malcolm-iso/config/hooks/normal/0910-agg-build.hook.chroot index aebae33a1..02879baec 100755 --- a/malcolm-iso/config/hooks/normal/0910-agg-build.hook.chroot +++ b/malcolm-iso/config/hooks/normal/0910-agg-build.hook.chroot @@ -115,3 +115,21 @@ chmod 755 /usr/local/bin/step chown root:root /usr/local/bin/step rm -rf /tmp/step* ### + +# stern +RELEASE_URL="https://api.github.com/repos/stern/stern/releases/latest" +RELEASE_FILE_REGEX="_linux_amd64\\\.tar\\\.gz$" +cd /tmp +mkdir -p ./stern +curl "${GITHUB_API_CURL_ARGS[@]}" "$(curl "${GITHUB_API_CURL_ARGS[@]}" "$(curl "${GITHUB_API_CURL_ARGS[@]}" "$RELEASE_URL" | jq '.assets_url' | tr -d '"')" | jq ".[] | select(.browser_download_url|test(\"$RELEASE_FILE_REGEX\")) | .browser_download_url" | tr -d '"')" | tar xzf - -C ./stern +mv ./stern/stern /usr/local/bin/stern +chmod 755 /usr/local/bin/stern +chown root:root /usr/local/bin/stern +rm -rf /tmp/stern* +### + +# kubectl +curl -sSL -o /usr/local/bin/kubectl "https://dl.k8s.io/release/$(curl -sSL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" +chmod 755 /usr/local/bin/kubectl +chown root:root /usr/local/bin/kubectl +### From d63e3cf14201b92909619be504b8cf5df93b58b9 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 25 Apr 2023 09:52:00 -0600 Subject: [PATCH 211/239] don't log timeouts, even in verbose debugging --- shared/bin/pcap_processor.py | 1 - shared/bin/zeek_carve_logger.py | 2 +- shared/bin/zeek_carve_utils.py | 7 ++++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shared/bin/pcap_processor.py b/shared/bin/pcap_processor.py index 17f734e36..c8fba5f37 100755 --- a/shared/bin/pcap_processor.py +++ b/shared/bin/pcap_processor.py @@ -754,7 +754,6 @@ def main(): fileInfo = json.loads(new_files_socket.recv_string()) except zmq.Again: # no file received due to timeout, we'll go around and try again - logging.debug(f"{scriptName}:\t🕑\t(recv)") fileInfo = None if isinstance(fileInfo, dict) and (FILE_INFO_DICT_NAME in fileInfo): diff --git a/shared/bin/zeek_carve_logger.py b/shared/bin/zeek_carve_logger.py index a9a1d9112..0f2e0f884 100755 --- a/shared/bin/zeek_carve_logger.py +++ b/shared/bin/zeek_carve_logger.py @@ -216,8 +216,8 @@ def main(): scanResult = json.loads(scanned_files_socket.recv_string()) logging.info(f"{scriptName}:\t📨\t{scanResult}") except zmq.Again: + # no file received due to timeout, we'll go around and try again scanResult = None - logging.debug(f"{scriptName}:\t🕑\t(recv)") if isinstance(scanResult, dict): # register/deregister scanners diff --git a/shared/bin/zeek_carve_utils.py b/shared/bin/zeek_carve_utils.py index fd4eb451c..1ac1129ea 100644 --- a/shared/bin/zeek_carve_utils.py +++ b/shared/bin/zeek_carve_utils.py @@ -315,9 +315,10 @@ def Pull(self, scanWorkerId=0): # no file received due to timeout, return empty dict. which means no file available pass - self.logger.debug( - f"{self.scriptName}[{scanWorkerId}]:\t{'📨' if (FILE_SCAN_RESULT_FILE in fileinfo) else '🕑'}\t{fileinfo[FILE_SCAN_RESULT_FILE] if (FILE_SCAN_RESULT_FILE in fileinfo) else '(recv)'}", - ) + if FILE_SCAN_RESULT_FILE in fileinfo: + self.logger.debug( + f"{self.scriptName}[{scanWorkerId}]:\t'📨'\t{fileinfo[FILE_SCAN_RESULT_FILE]}", + ) return fileinfo From f460fbeb972f0153c2415d094b0ae6a45eddb1b7 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 25 Apr 2023 13:33:02 -0600 Subject: [PATCH 212/239] idaholab/Malcolm#168; when in non-polling mode, don't expire files after 10 seconds of inactivity. wait for FileClosedEvent instead --- shared/bin/watch_common.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py index 0e08ee7b9..e73b1dcfd 100644 --- a/shared/bin/watch_common.py +++ b/shared/bin/watch_common.py @@ -204,7 +204,16 @@ def on_any_event(self, event): ################################################################################################### def ProcessFileEventWorker(workerArgs): - handler, observer, fileProcessor, fileProcessorKwargs, assumeClosedSec, workerThreadCount, shutDown, logger = ( + ( + handler, + observer, + fileProcessor, + fileProcessorKwargs, + assumeClosedSec, + workerThreadCount, + shutDown, + logger, + ) = ( workerArgs[0], workerArgs[1], workerArgs[2], @@ -220,8 +229,11 @@ def ProcessFileEventWorker(workerArgs): with workerThreadCount as workerId: logger.info(f"۞\tstarted\t[{workerId}]") + sleepInterval = 0.5 while (not shutDown[0]) and observer.is_alive(): - time.sleep(0.5) + time.sleep(sleepInterval) + sleepInterval = min(sleepInterval + 1.0, 5.0) + nowTime = int(time.time()) with handler.deck as d: @@ -236,7 +248,10 @@ def ProcessFileEventWorker(workerArgs): ) break - else: + if handler.polling or (fileHistory[-1].timestamp == 0): + # TODO is this ^ check accurate? I know it is for polling, but will we + # ALWAYS have a FileClosedEvent (timestamp == 0) in non-polling mode? + del d[fileName] if fileProcessor is not None: extraArgs = ( @@ -251,6 +266,7 @@ def ProcessFileEventWorker(workerArgs): logger.info( f"🖄\tprocessed\t{fileName} at {(nowTime-fileHistory[-1].timestamp) if (fileHistory[-1].timestamp > 0) else 0} seconds\t[{workerId}]" ) + sleepInterval = 0.5 time.sleep(1) logger.info(f"⛒\tfinished\t[{workerId}]") From a5a5268ebdcee5ec8fd5aa1b5f19503b7b308cf1 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 25 Apr 2023 13:33:02 -0600 Subject: [PATCH 213/239] idaholab/Malcolm#168; when in non-polling mode, don't expire files after 10 seconds of inactivity, be a little smarter --- shared/bin/watch_common.py | 60 +++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/shared/bin/watch_common.py b/shared/bin/watch_common.py index e73b1dcfd..17caa9300 100644 --- a/shared/bin/watch_common.py +++ b/shared/bin/watch_common.py @@ -10,7 +10,7 @@ from watchdog.events import ( FileSystemEventHandler, - FileSystemMovedEvent, + FileMovedEvent, FileModifiedEvent, FileCreatedEvent, FileClosedEvent, @@ -19,6 +19,7 @@ ) from multiprocessing.pool import ThreadPool +from threading import get_native_id from watchdog.utils import WatchdogShutdown from watchdog.observers import Observer from watchdog.observers.polling import PollingObserver @@ -41,6 +42,7 @@ def __init__( super().__init__(*args, **kwargs) self.polling = polling self.logger = logger if logger else logging + self.workerPid = get_native_id() self.updateTime() # self.deck is a dictionary mapping filenames to a list of OperationEvent of length n, # with [0] being the oldest timestamp/operation and [n-1] being the newest @@ -69,14 +71,14 @@ def on_any_event(self, event): self.updateTime() # if this is a move event, we need to track the old and new filenames - if isinstance(event, FileSystemMovedEvent): + if isinstance(event, FileMovedEvent): fName = event.dest_path fNameOld = event.src_path - self.logger.debug(f"↦\t{event.event_type: >10}\t{event.src_path} {event.dest_path}") + self.logger.debug(f"↦\t{event.event_type: >10}\t{event.src_path} {event.dest_path}\t{self.workerPid}") else: fName = event.src_path fNameOld = None - self.logger.debug(f"🗲\t{event.event_type: <10}\t{event.src_path}") + self.logger.debug(f"🗲\t{event.event_type: <10}\t{event.src_path}\t{self.workerPid}") # This is a pain, but due to this watchdog issue (see # https://github.com/gorakhargosh/watchdog/issues/260 and @@ -108,10 +110,8 @@ def on_any_event(self, event): # a file was simply renamed in the watched directory (not moved # from some other directory) so remove the old filename from our list # and a new one will get added - if fNameOld in d: - d.pop(fNameOld, None) - if fNameOld in self.modDeck: - self.modDeck(d.pop(fNameOld, None)) + d.pop(fNameOld, None) + self.modDeck.pop(fNameOld, None) # insert or update file event(s) @@ -154,7 +154,7 @@ def on_any_event(self, event): and (newOpLog.size != modifyOpSizes[-1]) ): # promote what's already in modDec to the real deck, then append this new history item - self.logger.debug(f"𝦸\t{event.event_type: <10}\t{fName}") + self.logger.debug(f"𝦸\t{event.event_type: <10}\t{fName}\t{self.workerPid}") d[fName] = self.modDeck.pop(fName) d[fName].append(newOpLog) @@ -175,7 +175,7 @@ def on_any_event(self, event): isinstance(event, FileModifiedEvent) or isinstance(event, FileClosedEvent) or isinstance(event, FileCreatedEvent) - or isinstance(event, FileSystemMovedEvent) + or isinstance(event, FileMovedEvent) ): # put FileClosedEvent events (which now have a timestamp of 0) at the front of # the deck (to be processed first), and others to the back @@ -183,23 +183,24 @@ def on_any_event(self, event): elif isinstance(event, FileDeletedEvent): # if a file is deleted I guess we don't need to track it any more - deckInserted.pop(fName, None) + d.pop(fName, None) + self.modDeck.pop(fName, None) fName = None else: noop = True if noop: - self.logger.debug(f"🗑\t{event.event_type: <10}\t{fName}") + self.logger.debug(f"🗑\t{event.event_type: <10}\t{fName}\t{self.workerPid}") elif fName: if fName in d: - self.logger.debug(f"➊\t{fName}\t{json.dumps(d[fName])}") + self.logger.debug(f"➊\t{fName}\t{json.dumps(d[fName])}\t{self.workerPid}") if fName in self.modDeck: - self.logger.debug(f"➋\t{fName}\t{json.dumps(self.modDeck[fName])}") + self.logger.debug(f"➋\t{fName}\t{json.dumps(self.modDeck[fName])}\t{self.workerPid}") except Exception as e: - self.logger.error(f"⨳\t{fName}\t{e}") + self.logger.error(f"⨳\t{fName}\t{e}\t{self.workerPid}") ################################################################################################### @@ -227,7 +228,8 @@ def ProcessFileEventWorker(workerArgs): logger = logging with workerThreadCount as workerId: - logger.info(f"۞\tstarted\t[{workerId}]") + workerPid = get_native_id() + logger.info(f"۞\tstarted\t[{workerPid}:{workerId}]") sleepInterval = 0.5 while (not shutDown[0]) and observer.is_alive(): @@ -238,20 +240,30 @@ def ProcessFileEventWorker(workerArgs): with handler.deck as d: for fileName, fileHistory in list(d.items()): - logger.debug(f"⏿ checking {fileName}\t{json.dumps(fileHistory)}\t[{workerId}]") + logger.debug(f"⏿ checking {fileName}\t{json.dumps(fileHistory)}\t[{workerPid}:{workerId}]") if len(fileHistory) > 0: if nowTime < fileHistory[-1].timestamp + assumeClosedSec: # we can break because the list is ordered logger.debug( - f"⎊\tbreaking early because {nowTime} < {fileHistory[-1].timestamp + assumeClosedSec}\t[{workerId}]" + f"⎊\tbreaking early because {nowTime} < {fileHistory[-1].timestamp + assumeClosedSec}\t[{workerPid}:{workerId}]" ) break - if handler.polling or (fileHistory[-1].timestamp == 0): - # TODO is this ^ check accurate? I know it is for polling, but will we - # ALWAYS have a FileClosedEvent (timestamp == 0) in non-polling mode? - + elif ( + # - If we're polling, rely on the timestamp comparison done above and process this file + handler.polling + # - If we're not polling, but we have a timestamp == 0, then we had a FileClosedEvent and can be processed + or (fileHistory[-1].timestamp == 0) + # - If we're not polling, and the item has expired (timestamp comparison done above) and the only items + # in this item's history are "created" or "moved" then this was atomically moved in from another directory + # on the same filesystem and should be processed now + or ( + not any( + set([x.operation for x in fileHistory if x.operation not in ('created', 'moved')]) + ) + ) + ): del d[fileName] if fileProcessor is not None: extraArgs = ( @@ -264,12 +276,12 @@ def ProcessFileEventWorker(workerArgs): **extraArgs, ) logger.info( - f"🖄\tprocessed\t{fileName} at {(nowTime-fileHistory[-1].timestamp) if (fileHistory[-1].timestamp > 0) else 0} seconds\t[{workerId}]" + f"🖄\tprocessed\t{fileName} at {(nowTime-fileHistory[-1].timestamp) if (fileHistory[-1].timestamp > 0) else 0} seconds\t[{workerPid}:{workerId}]" ) sleepInterval = 0.5 time.sleep(1) - logger.info(f"⛒\tfinished\t[{workerId}]") + logger.info(f"⛒\tfinished\t[{workerPid}:{workerId}]") def WatchAndProcessDirectory( From e2ebd4d375e7e9b9fb3584cbfae16dff1fbd0d54 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 25 Apr 2023 15:00:59 -0600 Subject: [PATCH 214/239] documentation tweak --- docs/kubernetes.md | 2 ++ docs/malcolm-hedgehog-e2e-iso-install.md | 14 ++++++++------ docs/ubuntu-install-example.md | 23 +++++++---------------- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/docs/kubernetes.md b/docs/kubernetes.md index 968a6fd39..c7b6e2c2e 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -341,6 +341,8 @@ Malcolm processes will run as UID 1000 and GID 1000. Is this OK? (Y/n): y Should Malcolm use and maintain its own OpenSearch instance? (Y/n): y +Compress OpenSearch index snapshots? (y/N): n + Forward Logstash logs to a secondary remote OpenSearch instance? (y/N): n Setting 16g for OpenSearch and 3g for Logstash. Is this OK? (Y/n): y diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md index c43df9d9b..ebdf41427 100644 --- a/docs/malcolm-hedgehog-e2e-iso-install.md +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -142,6 +142,8 @@ The [configuration and tuning](malcolm-config.md#ConfigAndTuning) wizard's quest - Docker runs all of its containers as the privileged `root` user by default. For better security, Malcolm immediately drops to non-privileged user accounts for executing internal processes wherever possible. The `PUID` (**p**rocess **u**ser **ID**) and `PGID` (**p**rocess **g**roup **ID**) environment variables allow Malcolm to map internal non-privileged user accounts to a corresponding [user account](https://en.wikipedia.org/wiki/User_identifier) on the host. * Should Malcolm use and maintain its own OpenSearch instance? - Malcolm's default standalone configuration is to use a local [OpenSearch](https://opensearch.org/) instance in a Docker container to index and search network traffic metadata. See [OpenSearch instances](opensearch-instances.md#OpenSearchInstance) for more information about using a remote OpenSearch cluster instead. +* Compress OpenSearch index snapshots? + - Choose whether OpenSearch [index snapshots](https://opensearch.org/docs/2.6/tuning-your-cluster/availability-and-recovery/snapshots/snapshot-management/) should be compressed or not, should you opt to configure them later in [OpenSearch index management](index-management.md#IndexManagement). * Forward Logstash logs to a secondary remote OpenSearch instance? - Whether the primary OpenSearch instance is a locally maintained single-node instance or is a remote cluster, Malcolm can be configured additionally forward logs to a secondary remote OpenSearch instance. See [OpenSearch instances](opensearch-instances.md#OpenSearchInstance) for more information about forwarding logs to another OpenSearch instance. * Setting 16g for OpenSearch and 3g for Logstash. Is this OK? @@ -168,16 +170,18 @@ The [configuration and tuning](malcolm-config.md#ConfigAndTuning) wizard's quest - When using LDAP authentication, this question allows you to configure [LDAP connection security](authsetup.md#AuthLDAPSecurity) * Store PCAP, log and index files locally under /home/user/Malcolm? - Malcolm generates a number of large file sets during normal operation: PCAP files, Zeek or Suricata logs, OpenSearch indices, etc. By default all of these are stored in subdirectories in the Malcolm installation directory. This question allows you to specify alternative storage location(s) (for example, a separate dedicated drive or RAID volume) for these artifacts. -* Compress OpenSearch index snapshots? - - Choose whether OpenSearch [index snapshots](https://opensearch.org/docs/2.6/tuning-your-cluster/availability-and-recovery/snapshots/snapshot-management/) should be compressed or not, should you opt to configure them later in [OpenSearch index management](index-management.md#IndexManagement). * Delete the oldest indices when the database exceeds a certain size? - Most of the configuration around OpenSearch [Index State Management](https://opensearch.org/docs/latest/im-plugin/ism/index/) and [Snapshot Management](https://opensearch.org/docs/latest/opensearch/snapshots/sm-dashboards/) can be done in OpenSearch Dashboards. In addition to (or instead of) the OpenSearch index state management operations, Malcolm can also be configured to delete the oldest network session metadata indices when the database exceeds a certain size to prevent filling up all available storage with OpenSearch indices. +* Should Arkime delete PCAP files based on available storage? + - Answering **Y** allows Arkime to prune (delete) old PCAP files based on available disk space (see https://arkime.com/faq#pcap-deletion). * Automatically analyze all PCAP files with Suricata? - This option is used to enable [Suricata](https://suricata.io/) (an IDS and threat detection engine) to analyze PCAP files uploaded to Malcolm via its upload web interface. * Download updated Suricata signatures periodically? - If your Malcolm instance has internet connectivity, answer **Y** to [enable automatic updates](https://suricata-update.readthedocs.io/en/latest/) of the Suricata rules used by Malcolm. * Automatically analyze all PCAP files with Zeek? - This option is used to enable [Zeek](https://www.zeek.org/index.html) (a network analysis framework and IDS) to analyze PCAP files uploaded to Malcolm via its upload web interface. +* Should Malcolm use "best guess" to identify potential OT/ICS traffic with Zeek? + - If you are using Malcolm in a control systems (OT/ICS) network, answer **Y** to enable ["Best Guess" Fingerprinting for ICS Protocols](ics-best-guess.md#ICSBestGuess). * Perform reverse DNS lookup locally for source and destination IP addresses in logs? - If enabled, this option will perform reverse [DNS lookups](https://www.elastic.co/guide/en/logstash/current/plugins-filters-dns.html) on IP addresses found in traffic and use the results to enrich network logs. Answer **Y** if your Malcolm instance has access to a DNS server to perform these lookups. * Perform hardware vendor OUI lookups for MAC addresses? @@ -190,6 +194,8 @@ The [configuration and tuning](malcolm-config.md#ConfigAndTuning) wizard's quest - Answer **Y** in order for Malcolm's firewall to allow connections from a remote log forwarder (such as Hedgehog Linux) to TCP port 5044 so that Zeek and Suricata logs can be ingested by Malcolm's Logstash instance. * Expose Filebeat TCP port to external hosts? - Answer **Y** in order for Malcolm's firewall to allow connections from a remote log forwarder (such as Hedgehog Linux for resource utilization metrics or other forwarders for other [third-Party logs](third-party-logs.md#ThirdPartyLogs)) to TCP port 5045. +* Use default field values for Filebeat TCP listener? + - Answer **Y** to use the defaults and skip the next five questions about the Filebeat TCP listener. * Select log format for messages sent to Filebeat TCP listener - Possible choices include `json` and `raw`; you probably want to choose `json`. * Source field to parse for messages sent to Filebeat TCP listener @@ -244,14 +250,10 @@ The [configuration and tuning](malcolm-config.md#ConfigAndTuning) wizard's quest - Answer **Y** for Malcolm to [capture network traffic](live-analysis.md#LocalPCAP) on the local network interface(s) indicated using [netsniff-ng](http://netsniff-ng.org/) (instead of tcpdump). These PCAP files are then periodically rotated into Arkime for analysis. netsniff-ng is Malcolm's preferred tool for capturing network traffic. * Capture packets using tcpdump? - Answer **Y** for Malcolm to [capture network traffic](live-analysis.md#LocalPCAP) on the local network interface(s) indicated using [tcpdump](https://www.tcpdump.org/) (instead of netsniff-ng). Do not answer **Y** for both `tcpdump` and `netsniff-ng`. -* Should Arkime delete PCAP files based on available storage? - - Answering **Y** allows Arkime to prune (delete) old PCAP files based on available disk space (see https://arkime.com/faq#pcap-deletion). * Should Malcolm analyze live network traffic with Suricata? - Answering **Y** will allow Malcolm itself to perform [live traffic analysis](live-analysis.md#LocalPCAP) using Suricata. If you are using Hedgehog Linux you probably want to answer **N** to this question. See the question above above about "captur[ing] live network traffic." * Should Malcolm analyze live network traffic with Zeek? - Answering **Y** will allow Malcolm itself to perform [live traffic analysis](live-analysis.md#LocalPCAP) using Zeek. If you are using Hedgehog Linux you probably want to answer **N** to this question. See the question above above about "captur[ing] live network traffic." -* Should Malcolm use "best guess" to identify potential OT/ICS traffic with Zeek? - - If you are using Malcolm in a control systems (OT/ICS) network, answer **Y** to enable ["Best Guess" Fingerprinting for ICS Protocols](ics-best-guess.md#ICSBestGuess). * Specify capture interface(s) (comma-separated) - Specify the network interface(s) for [live traffic analysis](live-analysis.md#LocalPCAP) if it is enabled for netsniff-ng, tcpdump, Suricata or Zeek as described above. For multiple interfaces, separate the interface names with a comma (e.g., `enp0s25` or `enp10s0,enp11s0`). * Capture filter (tcpdump-like filter expression; leave blank to capture all traffic) diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index 8b683a785..009feb698 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -102,6 +102,8 @@ Malcolm processes will run as UID 1000 and GID 1000. Is this OK? (Y/n): y Should Malcolm use and maintain its own OpenSearch instance? (Y/n): y +Compress OpenSearch index snapshots? (y/N): n + Forward Logstash logs to a secondary remote OpenSearch instance? (y/N): n Setting 10g for OpenSearch and 3g for Logstash. Is this OK? (Y/n): y @@ -128,16 +130,18 @@ Select authentication method (Basic): 1 Store PCAP, log and index files locally under /home/user/Malcolm? (Y/n): y -Compress OpenSearch index snapshots? (y/N): n - Delete the oldest indices when the database exceeds a certain size? (y/N): n +Should Arkime delete PCAP files based on available storage (see https://arkime.com/faq#pcap-deletion)? (y/N): y + Automatically analyze all PCAP files with Suricata? (Y/n): y Download updated Suricata signatures periodically? (y/N): y Automatically analyze all PCAP files with Zeek? (Y/n): y +Should Malcolm use "best guess" to identify potential OT/ICS traffic with Zeek? (y/N): n + Perform reverse DNS lookup locally for source and destination IP addresses in logs? (y/N): n Perform hardware vendor OUI lookups for MAC addresses? (Y/n): y @@ -149,17 +153,8 @@ Expose OpenSearch port to external hosts? (y/N): n Expose Logstash port to external hosts? (y/N): n Expose Filebeat TCP port to external hosts? (y/N): y -1: json -2: raw -Select log format for messages sent to Filebeat TCP listener (json): 1 -Source field to parse for messages sent to Filebeat TCP listener (message): message - -Target field under which to store decoded JSON fields for messages sent to Filebeat TCP listener (miscbeat): miscbeat - -Field to drop from events sent to Filebeat TCP listener (message): message - -Tag to apply to messages sent to Filebeat TCP listener (_malcolm_beats): _malcolm_beats +Use default field values for Filebeat TCP listener? (Y/n): y Expose SFTP server (for PCAP upload) to external hosts? (y/N): n @@ -195,14 +190,10 @@ Should Malcolm capture live network traffic to PCAP files for analysis with Arki Capture packets using netsniff-ng? (Y/n): y -Should Arkime delete PCAP files based on available storage (see https://arkime.com/faq#pcap-deletion)? (y/N): y - Should Malcolm analyze live network traffic with Suricata? (y/N): y Should Malcolm analyze live network traffic with Zeek? (y/N): y -Should Malcolm use "best guess" to identify potential OT/ICS traffic with Zeek? (y/N): n - Specify capture interface(s) (comma-separated): eth0 Capture filter (tcpdump-like filter expression; leave blank to capture all traffic) (): not port 5044 and not port 8005 and not port 9200 From 62fc4ab54fe93cf2cc1a27302a66364eb3c3bfb8 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Tue, 25 Apr 2023 17:48:39 -0600 Subject: [PATCH 215/239] point upstream for zeek packages --- shared/bin/zeek_install_plugins.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/bin/zeek_install_plugins.sh b/shared/bin/zeek_install_plugins.sh index d87fde9d5..735463b5c 100755 --- a/shared/bin/zeek_install_plugins.sh +++ b/shared/bin/zeek_install_plugins.sh @@ -73,6 +73,8 @@ ZKG_GITHUB_URLS=( "https://github.com/0xl3x1/zeek-EternalSafety" "https://github.com/0xxon/cve-2020-0601" "https://github.com/0xxon/cve-2020-13777" + "https://github.com/amzn/zeek-plugin-profinet|master" + "https://github.com/amzn/zeek-plugin-tds|master" "https://github.com/cisagov/icsnpp-bacnet" "https://github.com/cisagov/icsnpp-bsap" "https://github.com/cisagov/icsnpp-dnp3" @@ -107,14 +109,12 @@ ZKG_GITHUB_URLS=( "https://github.com/corelight/zerologon" "https://github.com/cybera/zeek-sniffpass" "https://github.com/mmguero-dev/bzar" - "https://github.com/mmguero-dev/ja3" - "https://github.com/mmguero-dev/zeek-plugin-profinet|master" - "https://github.com/mmguero-dev/zeek-plugin-tds|master" "https://github.com/ncsa/bro-is-darknet" "https://github.com/ncsa/bro-simple-scan" "https://github.com/precurse/zeek-httpattacks" "https://github.com/salesforce/GQUIC_Protocol_Analyzer" "https://github.com/salesforce/hassh" + "https://github.com/salesforce/ja3" "https://github.com/zeek/spicy-dhcp" "https://github.com/zeek/spicy-dns" "https://github.com/zeek/spicy-http" From 3c7a4ef5a04d37bb4111d0b99a31f0005b81acb4 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Wed, 26 Apr 2023 06:49:31 -0600 Subject: [PATCH 216/239] bump fluent bit to v2.1.2 (https://fluentbit.io/announcements/v2.1.2/) --- scripts/third-party-logs/fluent-bit-setup.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/third-party-logs/fluent-bit-setup.ps1 b/scripts/third-party-logs/fluent-bit-setup.ps1 index e5ba98b7e..96bf41aef 100644 --- a/scripts/third-party-logs/fluent-bit-setup.ps1 +++ b/scripts/third-party-logs/fluent-bit-setup.ps1 @@ -9,7 +9,7 @@ ############################################################################### $fluent_bit_version = '2.1' -$fluent_bit_full_version = '2.1.1' +$fluent_bit_full_version = '2.1.2' ############################################################################### # select an item from a menu provided in an array From 9a948dfc32bc068354c7723637c0519faf1d7a9b Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 26 Apr 2023 11:57:02 -0600 Subject: [PATCH 217/239] idaholab/Malcolm#190, just about everything for synchrophasor minus the dashboard --- Dockerfiles/zeek.Dockerfile | 2 + arkime/etc/config.ini | 117 ++++++++- arkime/wise/source.zeeklogs.js | 88 +++++++ config/zeek.env.example | 3 +- .../composable/component/zeek_ot.json | 90 ++++++- logstash/maps/zeek_log_ecs_categories.yaml | 7 + logstash/pipelines/zeek/11_zeek_parse.conf | 239 ++++++++++++++++++ logstash/pipelines/zeek/12_zeek_mutate.conf | 60 +++++ .../pipelines/zeek/13_zeek_normalize.conf | 6 + logstash/pipelines/zeek/14_zeek_convert.conf | 13 + .../usr/local/etc/zeek/local.zeek | 6 + .../interface/sensor_ctl/control_vars.conf | 1 + zeek/config/local.zeek | 6 + 13 files changed, 635 insertions(+), 3 deletions(-) diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index 9b2c9988b..a7294f7b0 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -255,6 +255,7 @@ ARG ZEEK_DISABLE_SPICY_STUN= ARG ZEEK_DISABLE_SPICY_TAILSCALE= ARG ZEEK_DISABLE_SPICY_TFTP= ARG ZEEK_DISABLE_SPICY_WIREGUARD= +ARG ZEEK_SYNCHROPHASOR_DETAILED= ENV ZEEK_DISABLE_HASH_ALL_FILES $ZEEK_DISABLE_HASH_ALL_FILES ENV ZEEK_DISABLE_LOG_PASSWORDS $ZEEK_DISABLE_LOG_PASSWORDS @@ -272,6 +273,7 @@ ENV ZEEK_DISABLE_SPICY_STUN $ZEEK_DISABLE_SPICY_STUN ENV ZEEK_DISABLE_SPICY_TAILSCALE $ZEEK_DISABLE_SPICY_TAILSCALE ENV ZEEK_DISABLE_SPICY_TFTP $ZEEK_DISABLE_SPICY_TFTP ENV ZEEK_DISABLE_SPICY_WIREGUARD $ZEEK_DISABLE_SPICY_WIREGUARD +ENV ZEEK_SYNCHROPHASOR_DETAILED $ZEEK_SYNCHROPHASOR_DETAILED ENV PUSER_CHOWN "$ZEEK_DIR" diff --git a/arkime/etc/config.ini b/arkime/etc/config.ini index 2a188cb4b..5a65d052d 100644 --- a/arkime/etc/config.ini +++ b/arkime/etc/config.ini @@ -1770,7 +1770,6 @@ tls.server.ja3s=db:tls.server.ja3s;group:zeek_ssl;kind:termfield;friendly:JA3 Se tls.client.ja3_description=db:tls.client.ja3_description;group:zeek_ssl;kind:termfield;friendly:JA3 Client Fingerprint Lookup;help:JA3 Client Fingerprint Lookup tls.server.ja3s_description=db:tls.server.ja3s_description;group:zeek_ssl;kind:termfield;friendly:JA3 Server Fingerprint Lookup;help:JA3 Server Fingerprint Lookup - # stun.log and stun_nat.log # https://github.com/corelight/zeek-spicy-stun/blob/master/analyzer/main.zeek zeek.stun.trans_id=db:zeek.stun.trans_id;group:zeek_stun;kind:termfield;friendly:Transaction ID;help:Transaction ID @@ -1782,6 +1781,115 @@ zeek.stun_nat.wan_addr=db:zeek.stun_nat.wan_addr;group:zeek_stun;kind:termfield; zeek.stun_nat.wan_port=db:zeek.stun_nat.wan_port;group:zeek_stun;kind:integer;friendly:Mapped Port;help:Mapped Port zeek.stun_nat.lan_addr=db:zeek.stun_nat.lan_addr;group:zeek_stun;kind:termfield;friendly:NAT LAN Address;help:NAT LAN Address +# synchrophasor_cmd.log +# https://github.com/cisagov/icsnpp-synchrophasor +zeek.synchrophasor_cmd.command=db:zeek.synchrophasor_cmd.command;group:zeek_synchrophasor;kind:termfield;friendly:command;help:command +zeek.synchrophasor_cmd.extframe=db:zeek.synchrophasor_cmd.extframe;group:zeek_synchrophasor;kind:integer;friendly:extframe;help:extframe + +# synchrophasor_cfg.log +# https://github.com/cisagov/icsnpp-synchrophasor +zeek.synchrophasor_cfg.cont_idx=db:zeek.synchrophasor_cfg.cont_idx;group:zeek_synchrophasor;kind:integer;friendly:cont_idx;help:cont_idx +zeek.synchrophasor_cfg.pmu_count_expected=db:zeek.synchrophasor_cfg.pmu_count_expected;group:zeek_synchrophasor;kind:integer;friendly:pmu_count_expected;help:pmu_count_expected +zeek.synchrophasor_cfg.pmu_count_actual=db:zeek.synchrophasor_cfg.pmu_count_actual;group:zeek_synchrophasor;kind:integer;friendly:pmu_count_actual;help:pmu_count_actual +zeek.synchrophasor_cfg.data_rate=db:zeek.synchrophasor_cfg.data_rate;group:zeek_synchrophasor;kind:integer;friendly:data_rate;help:data_rate +zeek.synchrophasor_cfg.cfg_frame_id=db:zeek.synchrophasor_cfg.cfg_frame_id;group:zeek_synchrophasor;kind:termfield;friendly:cfg_frame_id;help:cfg_frame_id + +# synchrophasor_cfg_detail.log +# https://github.com/cisagov/icsnpp-synchrophasor +zeek.synchrophasor_cfg_detail.cfg_frame_id=db:zeek.synchrophasor_cfg_detail.cfg_frame_id;group:zeek_synchrophasor;kind:termfield;friendly:cfg_frame_id;help:cfg_frame_id +zeek.synchrophasor_cfg_detail.pmu_idx=db:zeek.synchrophasor_cfg_detail.pmu_idx;group:zeek_synchrophasor;kind:integer;friendly:pmu_idx;help:pmu_idx +zeek.synchrophasor_cfg_detail.svc_class=db:zeek.synchrophasor_cfg_detail.svc_class;group:zeek_synchrophasor;kind:termfield;friendly:svc_class;help:svc_class +zeek.synchrophasor_cfg_detail.station_name=db:zeek.synchrophasor_cfg_detail.station_name;group:zeek_synchrophasor;kind:termfield;friendly:station_name;help:station_name +zeek.synchrophasor_cfg_detail.data_source_id=db:zeek.synchrophasor_cfg_detail.data_source_id;group:zeek_synchrophasor;kind:integer;friendly:data_source_id;help:data_source_id +zeek.synchrophasor_cfg_detail.global_pmuid=db:zeek.synchrophasor_cfg_detail.global_pmuid;group:zeek_synchrophasor;kind:termfield;friendly:global_pmuid;help:global_pmuid +zeek.synchrophasor_cfg_detail.phasor_shape=db:zeek.synchrophasor_cfg_detail.phasor_shape;group:zeek_synchrophasor;kind:termfield;friendly:phasor_shape;help:phasor_shape +zeek.synchrophasor_cfg_detail.phasor_format=db:zeek.synchrophasor_cfg_detail.phasor_format;group:zeek_synchrophasor;kind:termfield;friendly:phasor_format;help:phasor_format +zeek.synchrophasor_cfg_detail.analog_format=db:zeek.synchrophasor_cfg_detail.analog_format;group:zeek_synchrophasor;kind:termfield;friendly:analog_format;help:analog_format +zeek.synchrophasor_cfg_detail.freq_format=db:zeek.synchrophasor_cfg_detail.freq_format;group:zeek_synchrophasor;kind:termfield;friendly:freq_format;help:freq_format +zeek.synchrophasor_cfg_detail.phnmr=db:zeek.synchrophasor_cfg_detail.phnmr;group:zeek_synchrophasor;kind:integer;friendly:phnmr;help:phnmr +zeek.synchrophasor_cfg_detail.annmr=db:zeek.synchrophasor_cfg_detail.annmr;group:zeek_synchrophasor;kind:integer;friendly:annmr;help:annmr +zeek.synchrophasor_cfg_detail.dgnmr=db:zeek.synchrophasor_cfg_detail.dgnmr;group:zeek_synchrophasor;kind:integer;friendly:dgnmr;help:dgnmr +zeek.synchrophasor_cfg_detail.phnam=db:zeek.synchrophasor_cfg_detail.phnam;group:zeek_synchrophasor;kind:termfield;friendly:phnam;help:phnam +zeek.synchrophasor_cfg_detail.annam=db:zeek.synchrophasor_cfg_detail.annam;group:zeek_synchrophasor;kind:termfield;friendly:annam;help:annam +zeek.synchrophasor_cfg_detail.dgnam=db:zeek.synchrophasor_cfg_detail.dgnam;group:zeek_synchrophasor;kind:termfield;friendly:dgnam;help:dgnam +zeek.synchrophasor_cfg_detail.phasor_conv_phunit=db:zeek.synchrophasor_cfg_detail.phasor_conv_phunit;group:zeek_synchrophasor;kind:integer;friendly:phasor_conv_phunit;help:phasor_conv_phunit +zeek.synchrophasor_cfg_detail.phasor_conv_phvalue=db:zeek.synchrophasor_cfg_detail.phasor_conv_phvalue;group:zeek_synchrophasor;kind:integer;friendly:phasor_conv_phvalue;help:phasor_conv_phvalue +zeek.synchrophasor_cfg_detail.phasor_conv_upsampled_interpolation=db:zeek.synchrophasor_cfg_detail.phasor_conv_upsampled_interpolation;group:zeek_synchrophasor;kind:termfield;friendly:phasor_conv_upsampled_interpolation;help:phasor_conv_upsampled_interpolation +zeek.synchrophasor_cfg_detail.phasor_conv_upsampled_extrapolation=db:zeek.synchrophasor_cfg_detail.phasor_conv_upsampled_extrapolation;group:zeek_synchrophasor;kind:termfield;friendly:phasor_conv_upsampled_extrapolation;help:phasor_conv_upsampled_extrapolation +zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_reselection=db:zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_reselection;group:zeek_synchrophasor;kind:termfield;friendly:phasor_conv_downsampled_reselection;help:phasor_conv_downsampled_reselection +zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_fir_filter=db:zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_fir_filter;group:zeek_synchrophasor;kind:termfield;friendly:phasor_conv_downsampled_fir_filter;help:phasor_conv_downsampled_fir_filter +zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_no_fir_filter=db:zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_no_fir_filter;group:zeek_synchrophasor;kind:termfield;friendly:phasor_conv_downsampled_no_fir_filter;help:phasor_conv_downsampled_no_fir_filter +zeek.synchrophasor_cfg_detail.phasor_conv_filtered_without_changing_sampling=db:zeek.synchrophasor_cfg_detail.phasor_conv_filtered_without_changing_sampling;group:zeek_synchrophasor;kind:termfield;friendly:phasor_conv_filtered_without_changing_sampling;help:phasor_conv_filtered_without_changing_sampling +zeek.synchrophasor_cfg_detail.phasor_conv_calibration_mag_adj=db:zeek.synchrophasor_cfg_detail.phasor_conv_calibration_mag_adj;group:zeek_synchrophasor;kind:termfield;friendly:phasor_conv_calibration_mag_adj;help:phasor_conv_calibration_mag_adj +zeek.synchrophasor_cfg_detail.phasor_conv_calibration_phas_adj=db:zeek.synchrophasor_cfg_detail.phasor_conv_calibration_phas_adj;group:zeek_synchrophasor;kind:termfield;friendly:phasor_conv_calibration_phas_adj;help:phasor_conv_calibration_phas_adj +zeek.synchrophasor_cfg_detail.phasor_conv_rotation_phase_adj=db:zeek.synchrophasor_cfg_detail.phasor_conv_rotation_phase_adj;group:zeek_synchrophasor;kind:termfield;friendly:phasor_conv_rotation_phase_adj;help:phasor_conv_rotation_phase_adj +zeek.synchrophasor_cfg_detail.phasor_conv_pseudo_phasor_val=db:zeek.synchrophasor_cfg_detail.phasor_conv_pseudo_phasor_val;group:zeek_synchrophasor;kind:termfield;friendly:phasor_conv_pseudo_phasor_val;help:phasor_conv_pseudo_phasor_val +zeek.synchrophasor_cfg_detail.phasor_conv_mod_appl=db:zeek.synchrophasor_cfg_detail.phasor_conv_mod_appl;group:zeek_synchrophasor;kind:termfield;friendly:phasor_conv_mod_appl;help:phasor_conv_mod_appl +zeek.synchrophasor_cfg_detail.phasor_conv_phasor_component=db:zeek.synchrophasor_cfg_detail.phasor_conv_phasor_component;group:zeek_synchrophasor;kind:integer;friendly:phasor_conv_phasor_component;help:phasor_conv_phasor_component +zeek.synchrophasor_cfg_detail.phasor_conv_phasor_type=db:zeek.synchrophasor_cfg_detail.phasor_conv_phasor_type;group:zeek_synchrophasor;kind:termfield;friendly:phasor_conv_phasor_type;help:phasor_conv_phasor_type +zeek.synchrophasor_cfg_detail.phasor_conv_user_def=db:zeek.synchrophasor_cfg_detail.phasor_conv_user_def;group:zeek_synchrophasor;kind:integer;friendly:phasor_conv_user_def;help:phasor_conv_user_def +zeek.synchrophasor_cfg_detail.phasor_conv_scale_factor=db:zeek.synchrophasor_cfg_detail.phasor_conv_scale_factor;group:zeek_synchrophasor;kind:float;friendly:phasor_conv_scale_factor;help:phasor_conv_scale_factor +zeek.synchrophasor_cfg_detail.phasor_conv_angle_adj=db:zeek.synchrophasor_cfg_detail.phasor_conv_angle_adj;group:zeek_synchrophasor;kind:float;friendly:phasor_conv_angle_adj;help:phasor_conv_angle_adj +zeek.synchrophasor_cfg_detail.analog_conv_analog_flags=db:zeek.synchrophasor_cfg_detail.analog_conv_analog_flags;group:zeek_synchrophasor;kind:integer;friendly:analog_conv_analog_flags;help:analog_conv_analog_flags +zeek.synchrophasor_cfg_detail.analog_conv_user_defined_scaling=db:zeek.synchrophasor_cfg_detail.analog_conv_user_defined_scaling;group:zeek_synchrophasor;kind:integer;friendly:analog_conv_user_defined_scaling;help:analog_conv_user_defined_scaling +zeek.synchrophasor_cfg_detail.analog_conv_mag_scale=db:zeek.synchrophasor_cfg_detail.analog_conv_mag_scale;group:zeek_synchrophasor;kind:float;friendly:analog_conv_mag_scale;help:analog_conv_mag_scale +zeek.synchrophasor_cfg_detail.analog_conv_offset=db:zeek.synchrophasor_cfg_detail.analog_conv_offset;group:zeek_synchrophasor;kind:float;friendly:analog_conv_offset;help:analog_conv_offset +zeek.synchrophasor_cfg_detail.digital_conv_normal_status_mask=db:zeek.synchrophasor_cfg_detail.digital_conv_normal_status_mask;group:zeek_synchrophasor;kind:integer;friendly:digital_conv_normal_status_mask;help:digital_conv_normal_status_mask +zeek.synchrophasor_cfg_detail.digital_conv_valid_inputs_mask=db:zeek.synchrophasor_cfg_detail.digital_conv_valid_inputs_mask;group:zeek_synchrophasor;kind:integer;friendly:digital_conv_valid_inputs_mask;help:digital_conv_valid_inputs_mask +zeek.synchrophasor_cfg_detail.pmu_lat=db:zeek.synchrophasor_cfg_detail.pmu_lat;group:zeek_synchrophasor;kind:float;friendly:pmu_lat;help:pmu_lat +zeek.synchrophasor_cfg_detail.pmu_lon=db:zeek.synchrophasor_cfg_detail.pmu_lon;group:zeek_synchrophasor;kind:float;friendly:pmu_lon;help:pmu_lon +zeek.synchrophasor_cfg_detail.pmu_elev=db:zeek.synchrophasor_cfg_detail.pmu_elev;group:zeek_synchrophasor;kind:float;friendly:pmu_elev;help:pmu_elev +zeek.synchrophasor_cfg_detail.window=db:zeek.synchrophasor_cfg_detail.window;group:zeek_synchrophasor;kind:integer;friendly:window;help:window +zeek.synchrophasor_cfg_detail.group_delay=db:zeek.synchrophasor_cfg_detail.group_delay;group:zeek_synchrophasor;kind:integer;friendly:group_delay;help:group_delay +zeek.synchrophasor_cfg_detail.fnom=db:zeek.synchrophasor_cfg_detail.fnom;group:zeek_synchrophasor;kind:integer;friendly:fnom;help:fnom +zeek.synchrophasor_cfg_detail.cfgcnt=db:zeek.synchrophasor_cfg_detail.cfgcnt;group:zeek_synchrophasor;kind:integer;friendly:cfgcnt;help:cfgcnt + +# synchrophasor_data.log +# https://github.com/cisagov/icsnpp-synchrophasor +zeek.synchrophasor_data.pmu_count_expected=db:zeek.synchrophasor_data.pmu_count_expected;group:zeek_synchrophasor;kind:integer;friendly:pmu_count_expected;help:pmu_count_expected +zeek.synchrophasor_data.pmu_count_actual=db:zeek.synchrophasor_data.pmu_count_actual;group:zeek_synchrophasor;kind:integer;friendly:pmu_count_actual;help:pmu_count_actual +zeek.synchrophasor_data.data_frame_id=db:zeek.synchrophasor_data.data_frame_id;group:zeek_synchrophasor;kind:termfield;friendly:data_frame_id;help:data_frame_id + +# synchrophasor_data_detail.log +# https://github.com/cisagov/icsnpp-synchrophasor +zeek.synchrophasor_data_detail.data_frame_id=db:zeek.synchrophasor_data_detail.data_frame_id;group:zeek_synchrophasor;kind:termfield;friendly:data_frame_id;help:data_frame_id +zeek.synchrophasor_data_detail.pmu_idx=db:zeek.synchrophasor_data_detail.pmu_idx;group:zeek_synchrophasor;kind:integer;friendly:pmu_idx;help:pmu_idx +zeek.synchrophasor_data_detail.trigger_reason=db:zeek.synchrophasor_data_detail.trigger_reason;group:zeek_synchrophasor;kind:integer;friendly:trigger_reason;help:trigger_reason +zeek.synchrophasor_data_detail.unlocked_time=db:zeek.synchrophasor_data_detail.unlocked_time;group:zeek_synchrophasor;kind:integer;friendly:unlocked_time;help:unlocked_time +zeek.synchrophasor_data_detail.pmu_time_quality=db:zeek.synchrophasor_data_detail.pmu_time_quality;group:zeek_synchrophasor;kind:integer;friendly:pmu_time_quality;help:pmu_time_quality +zeek.synchrophasor_data_detail.data_modified=db:zeek.synchrophasor_data_detail.data_modified;group:zeek_synchrophasor;kind:termfield;friendly:data_modified;help:data_modified +zeek.synchrophasor_data_detail.config_change=db:zeek.synchrophasor_data_detail.config_change;group:zeek_synchrophasor;kind:termfield;friendly:config_change;help:config_change +zeek.synchrophasor_data_detail.pmu_trigger_pickup=db:zeek.synchrophasor_data_detail.pmu_trigger_pickup;group:zeek_synchrophasor;kind:termfield;friendly:pmu_trigger_pickup;help:pmu_trigger_pickup +zeek.synchrophasor_data_detail.data_sorting_type=db:zeek.synchrophasor_data_detail.data_sorting_type;group:zeek_synchrophasor;kind:termfield;friendly:data_sorting_type;help:data_sorting_type +zeek.synchrophasor_data_detail.pmu_sync_error=db:zeek.synchrophasor_data_detail.pmu_sync_error;group:zeek_synchrophasor;kind:termfield;friendly:pmu_sync_error;help:pmu_sync_error +zeek.synchrophasor_data_detail.data_error_indicator=db:zeek.synchrophasor_data_detail.data_error_indicator;group:zeek_synchrophasor;kind:integer;friendly:data_error_indicator;help:data_error_indicator +zeek.synchrophasor_data_detail.est_rectangular_real=db:zeek.synchrophasor_data_detail.est_rectangular_real;group:zeek_synchrophasor;kind:float;friendly:est_rectangular_real;help:est_rectangular_real +zeek.synchrophasor_data_detail.est_rectangular_imaginary=db:zeek.synchrophasor_data_detail.est_rectangular_imaginary;group:zeek_synchrophasor;kind:float;friendly:est_rectangular_imaginary;help:est_rectangular_imaginary +zeek.synchrophasor_data_detail.est_polar_magnitude=db:zeek.synchrophasor_data_detail.est_polar_magnitude;group:zeek_synchrophasor;kind:float;friendly:est_polar_magnitude;help:est_polar_magnitude +zeek.synchrophasor_data_detail.est_polar_angle=db:zeek.synchrophasor_data_detail.est_polar_angle;group:zeek_synchrophasor;kind:float;friendly:est_polar_angle;help:est_polar_angle +zeek.synchrophasor_data_detail.freq_dev_mhz=db:zeek.synchrophasor_data_detail.freq_dev_mhz;group:zeek_synchrophasor;kind:float;friendly:freq_dev_mhz;help:freq_dev_mhz +zeek.synchrophasor_data_detail.rocof=db:zeek.synchrophasor_data_detail.rocof;group:zeek_synchrophasor;kind:float;friendly:rocof;help:rocof +zeek.synchrophasor_data_detail.analog_data=db:zeek.synchrophasor_data_detail.analog_data;group:zeek_synchrophasor;kind:float;friendly:analog_data;help:analog_data +zeek.synchrophasor_data_detail.digital=db:zeek.synchrophasor_data_detail.digital;group:zeek_synchrophasor;kind:integer;friendly:digital;help:digital + +# synchrophasor_hdr.log +# https://github.com/cisagov/icsnpp-synchrophasor +zeek.synchrophasor_hdr.data=db:zeek.synchrophasor_hdr.data;group:zeek_synchrophasor;kind:termfield;friendly:data;help:data + +# synchrophasor.log +# https://github.com/cisagov/icsnpp-synchrophasor +zeek.synchrophasor.version=db:zeek.synchrophasor.version;group:zeek_synchrophasor;kind:termfield;friendly:version;help:version +zeek.synchrophasor.data_stream_id=db:zeek.synchrophasor.data_stream_id;group:zeek_synchrophasor;kind:termfield;friendly:data_stream_id;help:data_stream_id +zeek.synchrophasor.history=db:zeek.synchrophasor.history;group:zeek_synchrophasor;kind:termfield;friendly:history;help:history +zeek.synchrophasor.frame_size_min=db:zeek.synchrophasor.frame_size_min;group:zeek_synchrophasor;kind:integer;friendly:frame_size_min;help:frame_size_min +zeek.synchrophasor.frame_size_max=db:zeek.synchrophasor.frame_size_max;group:zeek_synchrophasor;kind:integer;friendly:frame_size_max;help:frame_size_max +zeek.synchrophasor.frame_size_tot=db:zeek.synchrophasor.frame_size_tot;group:zeek_synchrophasor;kind:integer;friendly:frame_size_tot;help:frame_size_tot +zeek.synchrophasor.data_frame_count=db:zeek.synchrophasor.data_frame_count;group:zeek_synchrophasor;kind:integer;friendly:data_frame_count;help:data_frame_count +zeek.synchrophasor.data_rate=db:zeek.synchrophasor.data_rate;group:zeek_synchrophasor;kind:termfield;friendly:data_rate;help:data_rate +zeek.synchrophasor.header_time_stamp=db:zeek.synchrophasor.header_time_stamp;group:zeek_synchrophasor;kind:termfield;friendly:header_time_stamp;help:header_time_stamp +zeek.synchrophasor.frame_type=db:zeek.synchrophasor.frame_type;group:zeek_synchrophasor;kind:termfield;friendly:frame_type;help:frame_type +zeek.synchrophasor.frame_size=db:zeek.synchrophasor.frame_size;group:zeek_synchrophasor;kind:integer;friendly:frame_size;help:frame_size + # syslog.log # https://docs.zeek.org/en/stable/scripts/base/protocols/syslog/main.zeek.html#type-Syslog::Info zeek.syslog.facility=db:zeek.syslog.facility;group:zeek_syslog;kind:termfield;friendly:Facility;help:Facility @@ -2524,6 +2632,13 @@ o_zeek_ssh=require:zeek.ssh;title:Zeek ssh.log;fields:zeek.ssh.version,zeek.ssh. o_zeek_ssl=require:zeek.ssl;title:Zeek ssl.log;fields:zeek.ssl.ssl_version,zeek.ssl.ssl_history,zeek.ssl.sni_matches_cert,zeek.ssl.cipher,zeek.ssl.curve,zeek.ssl.server_name,zeek.ssl.resumed,zeek.ssl.last_alert,zeek.ssl.next_protocol,zeek.ssl.established,tls.client.ja3,tls.client.ja3_description,tls.server.ja3s,tls.server.ja3s_description,zeek.ssl.cert_chain_fuids,zeek.ssl.cert_chain_fps,zeek.ssl.client_cert_chain_fuids,zeek.ssl.client_cert_chain_fps,zeek.ssl.subject.CN,zeek.ssl.subject.C,zeek.ssl.subject.O,zeek.ssl.subject.OU,zeek.ssl.subject.ST,zeek.ssl.subject.SN,zeek.ssl.subject.L,zeek.ssl.subject.GN,zeek.ssl.subject.pseudonym,zeek.ssl.subject.serialNumber,zeek.ssl.subject.title,zeek.ssl.subject.initials,zeek.ssl.subject.emailAddress,zeek.ssl.subject.description,zeek.ssl.subject.postalCode,zeek.ssl.subject.street,zeek.ssl.client_subject.CN,zeek.ssl.client_subject.C,zeek.ssl.client_subject.O,zeek.ssl.client_subject.OU,zeek.ssl.client_subject.ST,zeek.ssl.client_subject.SN,zeek.ssl.client_subject.L,zeek.ssl.client_subject.GN,zeek.ssl.client_subject.pseudonym,zeek.ssl.client_subject.serialNumber,zeek.ssl.client_subject.title,zeek.ssl.client_subject.initials,zeek.ssl.client_subject.emailAddress,zeek.ssl.issuer.CN,zeek.ssl.issuer.C,zeek.ssl.issuer.O,zeek.ssl.issuer.OU,zeek.ssl.issuer.ST,zeek.ssl.issuer.SN,zeek.ssl.issuer.L,zeek.ssl.issuer.DC,zeek.ssl.issuer.GN,zeek.ssl.issuer.pseudonym,zeek.ssl.issuer.serialNumber,zeek.ssl.issuer.title,zeek.ssl.issuer.initials,zeek.ssl.issuer.emailAddress,zeek.ssl.client_issuer.CN,zeek.ssl.client_issuer.C,zeek.ssl.client_issuer.O,zeek.ssl.client_issuer.OU,zeek.ssl.client_issuer.ST,zeek.ssl.client_issuer.SN,zeek.ssl.client_issuer.L,zeek.ssl.client_issuer.DC,zeek.ssl.client_issuer.GN,zeek.ssl.client_issuer.pseudonym,zeek.ssl.client_issuer.serialNumber,zeek.ssl.client_issuer.title,zeek.ssl.client_issuer.initials,zeek.ssl.client_issuer.emailAddress,zeek.ssl.validation_status o_zeek_stun=require:zeek.stun;title:Zeek stun.log;fields:zeek.stun.trans_id,zeek.stun.method,zeek.stun.class,zeek.stun.attr_type,zeek.stun.attr_val o_zeek_stun_nat=require:zeek.stun_nat;title:Zeek stun_nat.log;fields:zeek.stun_nat.wan_addr,zeek.stun_nat.wan_port,zeek.stun_nat.lan_addr +o_zeek_synchrophasor=require:zeek.synchrophasor;title:Zeek synchrophasor.log;fields:zeek.synchrophasor.version,zeek.synchrophasor.header_time_stamp,zeek.synchrophasor.data_stream_id,zeek.synchrophasor.history,zeek.synchrophasor.frame_type,zeek.synchrophasor.frame_size,zeek.synchrophasor.frame_size_min,zeek.synchrophasor.frame_size_max,zeek.synchrophasor.frame_size_tot,zeek.synchrophasor.data_frame_count,zeek.synchrophasor.data_rate +o_zeek_synchrophasor_cmd=require:zeek.synchrophasor_cmd;title:Zeek synchrophasor_cmd.log;fields:zeek.synchrophasor_cmd.command,zeek.synchrophasor_cmd.extframe +o_zeek_synchrophasor_cfg=require:zeek.synchrophasor_cfg;title:Zeek synchrophasor_cfg.log;fields:zeek.synchrophasor_cfg.cont_idx,zeek.synchrophasor_cfg.pmu_count_expected,zeek.synchrophasor_cfg.pmu_count_actual,zeek.synchrophasor_cfg.data_rate,zeek.synchrophasor_cfg.cfg_frame_id +o_zeek_synchrophasor_cfg_detail=require:zeek.synchrophasor_cfg_detail;title:Zeek synchrophasor_cfg_detail.log;fields:zeek.synchrophasor_cfg_detail.cfg_frame_id,zeek.synchrophasor_cfg_detail.pmu_idx,zeek.synchrophasor_cfg_detail.svc_class,zeek.synchrophasor_cfg_detail.station_name,zeek.synchrophasor_cfg_detail.data_source_id,zeek.synchrophasor_cfg_detail.global_pmuid,zeek.synchrophasor_cfg_detail.phasor_shape,zeek.synchrophasor_cfg_detail.phasor_format,zeek.synchrophasor_cfg_detail.analog_format,zeek.synchrophasor_cfg_detail.freq_format,zeek.synchrophasor_cfg_detail.phnmr,zeek.synchrophasor_cfg_detail.annmr,zeek.synchrophasor_cfg_detail.dgnmr,zeek.synchrophasor_cfg_detail.phnam,zeek.synchrophasor_cfg_detail.annam,zeek.synchrophasor_cfg_detail.dgnam,zeek.synchrophasor_cfg_detail.phasor_conv_phunit,zeek.synchrophasor_cfg_detail.phasor_conv_phvalue,zeek.synchrophasor_cfg_detail.phasor_conv_upsampled_interpolation,zeek.synchrophasor_cfg_detail.phasor_conv_upsampled_extrapolation,zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_reselection,zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_fir_filter,zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_no_fir_filter,zeek.synchrophasor_cfg_detail.phasor_conv_filtered_without_changing_sampling,zeek.synchrophasor_cfg_detail.phasor_conv_calibration_mag_adj,zeek.synchrophasor_cfg_detail.phasor_conv_calibration_phas_adj,zeek.synchrophasor_cfg_detail.phasor_conv_rotation_phase_adj,zeek.synchrophasor_cfg_detail.phasor_conv_pseudo_phasor_val,zeek.synchrophasor_cfg_detail.phasor_conv_mod_appl,zeek.synchrophasor_cfg_detail.phasor_conv_phasor_component,zeek.synchrophasor_cfg_detail.phasor_conv_phasor_type,zeek.synchrophasor_cfg_detail.phasor_conv_user_def,zeek.synchrophasor_cfg_detail.phasor_conv_scale_factor,zeek.synchrophasor_cfg_detail.phasor_conv_angle_adj,zeek.synchrophasor_cfg_detail.analog_conv_analog_flags,zeek.synchrophasor_cfg_detail.analog_conv_user_defined_scaling,zeek.synchrophasor_cfg_detail.analog_conv_mag_scale,zeek.synchrophasor_cfg_detail.analog_conv_offset,zeek.synchrophasor_cfg_detail.digital_conv_normal_status_mask,zeek.synchrophasor_cfg_detail.digital_conv_valid_inputs_mask,zeek.synchrophasor_cfg_detail.pmu_lat,zeek.synchrophasor_cfg_detail.pmu_lon,zeek.synchrophasor_cfg_detail.pmu_elev,zeek.synchrophasor_cfg_detail.window,zeek.synchrophasor_cfg_detail.group_delay,zeek.synchrophasor_cfg_detail.fnom,zeek.synchrophasor_cfg_detail.cfgcnt +o_zeek_synchrophasor_data=require:zeek.synchrophasor_data;title:Zeek synchrophasor_data.log;fields:zeek.synchrophasor_data.pmu_count_expected,zeek.synchrophasor_data.pmu_count_actual,zeek.synchrophasor_data.data_frame_id +o_zeek_synchrophasor_data_detail=require:zeek.synchrophasor_data_detail;title:Zeek synchrophasor_data_detail.log;fields:zeek.synchrophasor_data_detail.data_frame_id,zeek.synchrophasor_data_detail.pmu_idx,zeek.synchrophasor_data_detail.trigger_reason,zeek.synchrophasor_data_detail.unlocked_time,zeek.synchrophasor_data_detail.pmu_time_quality,zeek.synchrophasor_data_detail.data_modified,zeek.synchrophasor_data_detail.config_change,zeek.synchrophasor_data_detail.pmu_trigger_pickup,zeek.synchrophasor_data_detail.data_sorting_type,zeek.synchrophasor_data_detail.pmu_sync_error,zeek.synchrophasor_data_detail.data_error_indicator,zeek.synchrophasor_data_detail.est_rectangular_real,zeek.synchrophasor_data_detail.est_rectangular_imaginary,zeek.synchrophasor_data_detail.est_polar_magnitude,zeek.synchrophasor_data_detail.est_polar_angle,zeek.synchrophasor_data_detail.freq_dev_mhz,zeek.synchrophasor_data_detail.rocof,zeek.synchrophasor_data_detail.analog_data,zeek.synchrophasor_data_detail.digital +o_zeek_synchrophasor_hdr=require:zeek.synchrophasor_hdr;title:Zeek synchrophasor_hdr.log;fields:zeek.synchrophasor_hdr.data o_zeek_syslog=require:zeek.syslog;title:Zeek syslog.log;fields:zeek.syslog.facility,zeek.syslog.severity,zeek.syslog.message o_zeek_tds=require:zeek.tds;title:Zeek tds.log;fields:zeek.tds.command o_zeek_tds_rpc=require:zeek.tds_rpc;title:Zeek tds_rpc.log;fields:zeek.tds_rpc.procedure_name,zeek.tds_rpc.parameters diff --git a/arkime/wise/source.zeeklogs.js b/arkime/wise/source.zeeklogs.js index 5468be9f2..4805f75e8 100644 --- a/arkime/wise/source.zeeklogs.js +++ b/arkime/wise/source.zeeklogs.js @@ -1985,6 +1985,94 @@ class MalcolmSource extends WISESource { "zeek.stun_nat.lan_addr", "zeek.stun_nat.wan_addr", "zeek.stun_nat.wan_port", + "zeek.synchrophasor.data_frame_count", + "zeek.synchrophasor.data_rate", + "zeek.synchrophasor.data_stream_id", + "zeek.synchrophasor.frame_size", + "zeek.synchrophasor.frame_size_max", + "zeek.synchrophasor.frame_size_min", + "zeek.synchrophasor.frame_size_tot", + "zeek.synchrophasor.frame_type", + "zeek.synchrophasor.header_time_stamp", + "zeek.synchrophasor.history", + "zeek.synchrophasor.version", + "zeek.synchrophasor_cfg.cfg_frame_id", + "zeek.synchrophasor_cfg.cont_idx", + "zeek.synchrophasor_cfg.data_rate", + "zeek.synchrophasor_cfg.pmu_count_actual", + "zeek.synchrophasor_cfg.pmu_count_expected", + "zeek.synchrophasor_cfg_detail.analog_conv_analog_flags", + "zeek.synchrophasor_cfg_detail.analog_conv_mag_scale", + "zeek.synchrophasor_cfg_detail.analog_conv_offset", + "zeek.synchrophasor_cfg_detail.analog_conv_user_defined_scaling", + "zeek.synchrophasor_cfg_detail.analog_format", + "zeek.synchrophasor_cfg_detail.annam", + "zeek.synchrophasor_cfg_detail.annmr", + "zeek.synchrophasor_cfg_detail.cfg_frame_id", + "zeek.synchrophasor_cfg_detail.cfgcnt", + "zeek.synchrophasor_cfg_detail.data_source_id", + "zeek.synchrophasor_cfg_detail.dgnam", + "zeek.synchrophasor_cfg_detail.dgnmr", + "zeek.synchrophasor_cfg_detail.digital_conv_normal_status_mask", + "zeek.synchrophasor_cfg_detail.digital_conv_valid_inputs_mask", + "zeek.synchrophasor_cfg_detail.fnom", + "zeek.synchrophasor_cfg_detail.freq_format", + "zeek.synchrophasor_cfg_detail.global_pmuid", + "zeek.synchrophasor_cfg_detail.group_delay", + "zeek.synchrophasor_cfg_detail.phasor_conv_angle_adj", + "zeek.synchrophasor_cfg_detail.phasor_conv_calibration_mag_adj", + "zeek.synchrophasor_cfg_detail.phasor_conv_calibration_phas_adj", + "zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_fir_filter", + "zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_no_fir_filter", + "zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_reselection", + "zeek.synchrophasor_cfg_detail.phasor_conv_filtered_without_changing_sampling", + "zeek.synchrophasor_cfg_detail.phasor_conv_mod_appl", + "zeek.synchrophasor_cfg_detail.phasor_conv_phasor_component", + "zeek.synchrophasor_cfg_detail.phasor_conv_phasor_type", + "zeek.synchrophasor_cfg_detail.phasor_conv_phunit", + "zeek.synchrophasor_cfg_detail.phasor_conv_phvalue", + "zeek.synchrophasor_cfg_detail.phasor_conv_pseudo_phasor_val", + "zeek.synchrophasor_cfg_detail.phasor_conv_rotation_phase_adj", + "zeek.synchrophasor_cfg_detail.phasor_conv_scale_factor", + "zeek.synchrophasor_cfg_detail.phasor_conv_upsampled_extrapolation", + "zeek.synchrophasor_cfg_detail.phasor_conv_upsampled_interpolation", + "zeek.synchrophasor_cfg_detail.phasor_conv_user_def", + "zeek.synchrophasor_cfg_detail.phasor_format", + "zeek.synchrophasor_cfg_detail.phasor_shape", + "zeek.synchrophasor_cfg_detail.phnam", + "zeek.synchrophasor_cfg_detail.phnmr", + "zeek.synchrophasor_cfg_detail.pmu_elev", + "zeek.synchrophasor_cfg_detail.pmu_idx", + "zeek.synchrophasor_cfg_detail.pmu_lat", + "zeek.synchrophasor_cfg_detail.pmu_lon", + "zeek.synchrophasor_cfg_detail.station_name", + "zeek.synchrophasor_cfg_detail.svc_class", + "zeek.synchrophasor_cfg_detail.window", + "zeek.synchrophasor_cmd.command", + "zeek.synchrophasor_cmd.extframe", + "zeek.synchrophasor_data.data_frame_id", + "zeek.synchrophasor_data.pmu_count_actual", + "zeek.synchrophasor_data.pmu_count_expected", + "zeek.synchrophasor_data_detail.analog_data", + "zeek.synchrophasor_data_detail.config_change", + "zeek.synchrophasor_data_detail.data_error_indicator", + "zeek.synchrophasor_data_detail.data_frame_id", + "zeek.synchrophasor_data_detail.data_modified", + "zeek.synchrophasor_data_detail.data_sorting_type", + "zeek.synchrophasor_data_detail.digital", + "zeek.synchrophasor_data_detail.est_polar_angle", + "zeek.synchrophasor_data_detail.est_polar_magnitude", + "zeek.synchrophasor_data_detail.est_rectangular_imaginary", + "zeek.synchrophasor_data_detail.est_rectangular_real", + "zeek.synchrophasor_data_detail.freq_dev_mhz", + "zeek.synchrophasor_data_detail.pmu_idx", + "zeek.synchrophasor_data_detail.pmu_sync_error", + "zeek.synchrophasor_data_detail.pmu_time_quality", + "zeek.synchrophasor_data_detail.pmu_trigger_pickup", + "zeek.synchrophasor_data_detail.rocof", + "zeek.synchrophasor_data_detail.trigger_reason", + "zeek.synchrophasor_data_detail.unlocked_time", + "zeek.synchrophasor_hdr.data", "zeek.syslog.facility", "zeek.syslog.message", "zeek.syslog.severity", diff --git a/config/zeek.env.example b/config/zeek.env.example index ba3d5db6f..3563a08c3 100644 --- a/config/zeek.env.example +++ b/config/zeek.env.example @@ -64,4 +64,5 @@ ZEEK_DISABLE_SPICY_OPENVPN= ZEEK_DISABLE_SPICY_STUN= ZEEK_DISABLE_SPICY_TAILSCALE= ZEEK_DISABLE_SPICY_TFTP= -ZEEK_DISABLE_SPICY_WIREGUARD= \ No newline at end of file +ZEEK_DISABLE_SPICY_WIREGUARD= +ZEEK_SYNCHROPHASOR_DETAILED= \ No newline at end of file diff --git a/dashboards/templates/composable/component/zeek_ot.json b/dashboards/templates/composable/component/zeek_ot.json index c54b42ab3..5850093f6 100644 --- a/dashboards/templates/composable/component/zeek_ot.json +++ b/dashboards/templates/composable/component/zeek_ot.json @@ -709,7 +709,95 @@ "zeek.s7comm_upload_download.function_name": { "type": "keyword"}, "zeek.s7comm_upload_download.function_status": { "type": "keyword"}, "zeek.s7comm_upload_download.rosctr_name": { "type": "keyword"}, - "zeek.s7comm_upload_download.session_id": { "type": "long"} + "zeek.s7comm_upload_download.session_id": { "type": "long"}, + "zeek.synchrophasor.data_frame_count": { "type": "long" }, + "zeek.synchrophasor.data_rate": { "type": "keyword" }, + "zeek.synchrophasor.data_stream_id": { "type": "keyword" }, + "zeek.synchrophasor.frame_size_max": { "type": "long" }, + "zeek.synchrophasor.frame_size_min": { "type": "long" }, + "zeek.synchrophasor.frame_size_tot": { "type": "long" }, + "zeek.synchrophasor.header_time_stamp": { "type": "date" }, + "zeek.synchrophasor.frame_size": { "type": "long" }, + "zeek.synchrophasor.frame_type": { "type": "keyword" }, + "zeek.synchrophasor.history": { "type": "keyword" }, + "zeek.synchrophasor.version": { "type": "keyword" }, + "zeek.synchrophasor_cfg.cfg_frame_id": { "type": "keyword" }, + "zeek.synchrophasor_cfg.cont_idx": { "type": "long" }, + "zeek.synchrophasor_cfg.data_rate": { "type": "long" }, + "zeek.synchrophasor_cfg.pmu_count_actual": { "type": "long" }, + "zeek.synchrophasor_cfg.pmu_count_expected": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.analog_conv_analog_flags": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.analog_conv_mag_scale": { "type": "float" }, + "zeek.synchrophasor_cfg_detail.analog_conv_offset": { "type": "float" }, + "zeek.synchrophasor_cfg_detail.analog_conv_user_defined_scaling": { "type": "integer" }, + "zeek.synchrophasor_cfg_detail.analog_format": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.annam": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.annmr": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.cfg_frame_id": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.cfgcnt": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.data_source_id": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.dgnam": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.dgnmr": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.digital_conv_normal_status_mask": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.digital_conv_valid_inputs_mask": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.fnom": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.freq_format": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.global_pmuid": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.group_delay": { "type": "integer" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_angle_adj": { "type": "float" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_calibration_mag_adj": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_calibration_phas_adj": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_fir_filter": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_no_fir_filter": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_reselection": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_filtered_without_changing_sampling": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_mod_appl": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_phasor_component": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_phasor_type": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_phunit": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_phvalue": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_pseudo_phasor_val": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_rotation_phase_adj": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_scale_factor": { "type": "float" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_upsampled_extrapolation": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_upsampled_interpolation": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_conv_user_def": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.phasor_format": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phasor_shape": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phnam": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.phnmr": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.pmu_elev": { "type": "float" }, + "zeek.synchrophasor_cfg_detail.pmu_idx": { "type": "long" }, + "zeek.synchrophasor_cfg_detail.pmu_lat": { "type": "float" }, + "zeek.synchrophasor_cfg_detail.pmu_lon": { "type": "float" }, + "zeek.synchrophasor_cfg_detail.station_name": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.svc_class": { "type": "keyword" }, + "zeek.synchrophasor_cfg_detail.window": { "type": "integer" }, + "zeek.synchrophasor_cmd.command": { "type": "keyword" }, + "zeek.synchrophasor_cmd.extframe": { "type": "long" }, + "zeek.synchrophasor_data.data_frame_id": { "type": "keyword" }, + "zeek.synchrophasor_data.pmu_count_actual": { "type": "long" }, + "zeek.synchrophasor_data.pmu_count_expected": { "type": "long" }, + "zeek.synchrophasor_data_detail.analog_data": { "type": "float" }, + "zeek.synchrophasor_data_detail.config_change": { "type": "keyword" }, + "zeek.synchrophasor_data_detail.data_error_indicator": { "type": "long" }, + "zeek.synchrophasor_data_detail.data_frame_id": { "type": "keyword" }, + "zeek.synchrophasor_data_detail.data_modified": { "type": "keyword" }, + "zeek.synchrophasor_data_detail.data_sorting_type": { "type": "keyword" }, + "zeek.synchrophasor_data_detail.digital": { "type": "long" }, + "zeek.synchrophasor_data_detail.est_polar_angle": { "type": "float" }, + "zeek.synchrophasor_data_detail.est_polar_magnitude": { "type": "float" }, + "zeek.synchrophasor_data_detail.est_rectangular_imaginary": { "type": "float" }, + "zeek.synchrophasor_data_detail.est_rectangular_real": { "type": "float" }, + "zeek.synchrophasor_data_detail.freq_dev_mhz": { "type": "float" }, + "zeek.synchrophasor_data_detail.pmu_idx": { "type": "long" }, + "zeek.synchrophasor_data_detail.pmu_sync_error": { "type": "keyword" }, + "zeek.synchrophasor_data_detail.pmu_time_quality": { "type": "long" }, + "zeek.synchrophasor_data_detail.pmu_trigger_pickup": { "type": "keyword" }, + "zeek.synchrophasor_data_detail.rocof": { "type": "float" }, + "zeek.synchrophasor_data_detail.trigger_reason": { "type": "long" }, + "zeek.synchrophasor_data_detail.unlocked_time": { "type": "long" }, + "zeek.synchrophasor_hdr.data": { "type": "keyword" } } } } diff --git a/logstash/maps/zeek_log_ecs_categories.yaml b/logstash/maps/zeek_log_ecs_categories.yaml index 5c9cdcf55..f755ad5ab 100644 --- a/logstash/maps/zeek_log_ecs_categories.yaml +++ b/logstash/maps/zeek_log_ecs_categories.yaml @@ -81,6 +81,13 @@ "stun": ["network"] "stun_nat": ["network"] "syslog": ["network"] +"synchrophasor": ["ot", "network"] +"synchrophasor_cfg": ["ot", "network"] +"synchrophasor_cfg_detail": ["ot", "network"] +"synchrophasor_cmd": ["ot", "network"] +"synchrophasor_data": ["ot", "network"] +"synchrophasor_data_detail": ["ot", "network"] +"synchrophasor_hdr": ["ot", "network"] "tds": ["database", "network"] "tds_rpc": ["database", "network"] "tds_sql_batch": ["database", "network"] diff --git a/logstash/pipelines/zeek/11_zeek_parse.conf b/logstash/pipelines/zeek/11_zeek_parse.conf index 2c6f1203b..f1d530ef6 100644 --- a/logstash/pipelines/zeek/11_zeek_parse.conf +++ b/logstash/pipelines/zeek/11_zeek_parse.conf @@ -3218,6 +3218,245 @@ filter { } } + } else if ([log_source] == "synchrophasor") { + ############################################################################################################################# + # synchrophasor.log + # main.zeek (https://github.com/cisagov/icsnpp-synchrophasor) + + dissect { + id => "dissect_zeek_synchrophasor" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + mapping => { + "[message]" => "%{[zeek_cols][ts]} %{[zeek_cols][uid]} %{[zeek_cols][orig_h]} %{[zeek_cols][orig_p]} %{[zeek_cols][resp_h]} %{[zeek_cols][resp_p]} %{[zeek_cols][proto]} %{[zeek_cols][version]} %{[zeek_cols][data_stream_id]} %{[zeek_cols][history]} %{[zeek_cols][frame_size_min]} %{[zeek_cols][frame_size_max]} %{[zeek_cols][frame_size_tot]} %{[zeek_cols][data_frame_count]} %{[zeek_cols][data_rate]}" + } + } + + if ("_dissectfailure" in [tags]) { + mutate { + id => "mutate_split_zeek_synchrophasor" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + split => { "[message]" => " " } + } + ruby { + id => "ruby_zip_zeek_synchrophasor" + init => "$zeek_synchrophasor_field_names = [ 'ts', 'uid', 'orig_h', 'orig_p', 'resp_h', 'resp_p', 'proto', 'version', 'data_stream_id', 'history', 'frame_size_min', 'frame_size_max', 'frame_size_tot', 'data_frame_count', 'data_rate' ]" + code => "event.set('[zeek_cols]', $zeek_synchrophasor_field_names.zip(event.get('[message]')).to_h)" + } + } + + mutate { + id => "mutate_add_fields_zeek_synchrophasor" + add_field => { + "[zeek_cols][service]" => "synchrophasor" + } + add_tag => [ "ics" ] + } + + + } else if ([log_source] == "synchrophasor_cmd") { + ############################################################################################################################# + # synchrophasor_cmd.log + # main.zeek (https://github.com/cisagov/icsnpp-synchrophasor) + + dissect { + id => "dissect_zeek_synchrophasor_cmd" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + mapping => { + "[message]" => "%{[zeek_cols][ts]} %{[zeek_cols][uid]} %{[zeek_cols][orig_h]} %{[zeek_cols][orig_p]} %{[zeek_cols][resp_h]} %{[zeek_cols][resp_p]} %{[zeek_cols][proto]} %{[zeek_cols][frame_type]} %{[zeek_cols][frame_size]} %{[zeek_cols][header_time_stamp]} %{[zeek_cols][command]} %{[zeek_cols][extframe]}" + } + } + + if ("_dissectfailure" in [tags]) { + mutate { + id => "mutate_split_zeek_synchrophasor_cmd" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + split => { "[message]" => " " } + } + ruby { + id => "ruby_zip_zeek_synchrophasor_cmd" + init => "$zeek_synchrophasor_cmd_field_names = [ 'ts', 'uid', 'orig_h', 'orig_p', 'resp_h', 'resp_p', 'proto', 'frame_type', 'frame_size', 'header_time_stamp', 'command', 'extframe' ]" + code => "event.set('[zeek_cols]', $zeek_synchrophasor_cmd_field_names.zip(event.get('[message]')).to_h)" + } + } + + mutate { + id => "mutate_add_fields_zeek_synchrophasor_cmd" + add_field => { + "[zeek_cols][service]" => "synchrophasor" + } + add_tag => [ "ics" ] + } + + } else if ([log_source] == "synchrophasor_cfg") { + ############################################################################################################################# + # synchrophasor_cfg.log + # main.zeek (https://github.com/cisagov/icsnpp-synchrophasor) + + dissect { + id => "dissect_zeek_synchrophasor_cfg" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + mapping => { + "[message]" => "%{[zeek_cols][ts]} %{[zeek_cols][uid]} %{[zeek_cols][orig_h]} %{[zeek_cols][orig_p]} %{[zeek_cols][resp_h]} %{[zeek_cols][resp_p]} %{[zeek_cols][proto]} %{[zeek_cols][frame_type]} %{[zeek_cols][frame_size]} %{[zeek_cols][header_time_stamp]} %{[zeek_cols][cont_idx]} %{[zeek_cols][pmu_count_expected]} %{[zeek_cols][pmu_count_actual]} %{[zeek_cols][data_rate]} %{[zeek_cols][cfg_frame_id]}" + } + } + + if ("_dissectfailure" in [tags]) { + mutate { + id => "mutate_split_zeek_synchrophasor_cfg" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + split => { "[message]" => " " } + } + ruby { + id => "ruby_zip_zeek_synchrophasor_cfg" + init => "$zeek_synchrophasor_cfg_field_names = [ 'ts', 'uid', 'orig_h', 'orig_p', 'resp_h', 'resp_p', 'proto', 'frame_type', 'frame_size', 'header_time_stamp', 'cont_idx', 'pmu_count_expected', 'pmu_count_actual', 'data_rate', 'cfg_frame_id' ]" + code => "event.set('[zeek_cols]', $zeek_synchrophasor_cfg_field_names.zip(event.get('[message]')).to_h)" + } + } + + mutate { + id => "mutate_add_fields_zeek_synchrophasor_cfg" + add_field => { + "[zeek_cols][service]" => "synchrophasor" + } + add_tag => [ "ics" ] + } + + } else if ([log_source] == "synchrophasor_cfg_detail") { + ############################################################################################################################# + # synchrophasor_cfg_detail.log + # main.zeek (https://github.com/cisagov/icsnpp-synchrophasor) + + dissect { + id => "dissect_zeek_synchrophasor_cfg_detail" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + mapping => { + "[message]" => "%{[zeek_cols][ts]} %{[zeek_cols][uid]} %{[zeek_cols][orig_h]} %{[zeek_cols][orig_p]} %{[zeek_cols][resp_h]} %{[zeek_cols][resp_p]} %{[zeek_cols][proto]} %{[zeek_cols][frame_type]} %{[zeek_cols][header_time_stamp]} %{[zeek_cols][cfg_frame_id]} %{[zeek_cols][pmu_idx]} %{[zeek_cols][svc_class]} %{[zeek_cols][station_name]} %{[zeek_cols][data_source_id]} %{[zeek_cols][global_pmuid]} %{[zeek_cols][phasor_shape]} %{[zeek_cols][phasor_format]} %{[zeek_cols][analog_format]} %{[zeek_cols][freq_format]} %{[zeek_cols][phnmr]} %{[zeek_cols][annmr]} %{[zeek_cols][dgnmr]} %{[zeek_cols][phnam]} %{[zeek_cols][annam]} %{[zeek_cols][dgnam]} %{[zeek_cols][phasor_conv_phunit]} %{[zeek_cols][phasor_conv_phvalue]} %{[zeek_cols][phasor_conv_upsampled_interpolation]} %{[zeek_cols][phasor_conv_upsampled_extrapolation]} %{[zeek_cols][phasor_conv_downsampled_reselection]} %{[zeek_cols][phasor_conv_downsampled_fir_filter]} %{[zeek_cols][phasor_conv_downsampled_no_fir_filter]} %{[zeek_cols][phasor_conv_filtered_without_changing_sampling]} %{[zeek_cols][phasor_conv_calibration_mag_adj]} %{[zeek_cols][phasor_conv_calibration_phas_adj]} %{[zeek_cols][phasor_conv_rotation_phase_adj]} %{[zeek_cols][phasor_conv_pseudo_phasor_val]} %{[zeek_cols][phasor_conv_mod_appl]} %{[zeek_cols][phasor_conv_phasor_component]} %{[zeek_cols][phasor_conv_phasor_type]} %{[zeek_cols][phasor_conv_user_def]} %{[zeek_cols][phasor_conv_scale_factor]} %{[zeek_cols][phasor_conv_angle_adj]} %{[zeek_cols][analog_conv_analog_flags]} %{[zeek_cols][analog_conv_user_defined_scaling]} %{[zeek_cols][analog_conv_mag_scale]} %{[zeek_cols][analog_conv_offset]} %{[zeek_cols][digital_conv_normal_status_mask]} %{[zeek_cols][digital_conv_valid_inputs_mask]} %{[zeek_cols][pmu_lat]} %{[zeek_cols][pmu_lon]} %{[zeek_cols][pmu_elev]} %{[zeek_cols][window]} %{[zeek_cols][group_delay]} %{[zeek_cols][fnom]} %{[zeek_cols][cfgcnt]}" + } + } + + if ("_dissectfailure" in [tags]) { + mutate { + id => "mutate_split_zeek_synchrophasor_cfg_detail" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + split => { "[message]" => " " } + } + ruby { + id => "ruby_zip_zeek_synchrophasor_cfg_detail" + init => "$zeek_synchrophasor_cfg_detail_field_names = [ 'ts', 'uid', 'orig_h', 'orig_p', 'resp_h', 'resp_p', 'proto', 'frame_type', 'header_time_stamp', 'cfg_frame_id', 'pmu_idx', 'svc_class', 'station_name', 'data_source_id', 'global_pmuid', 'phasor_shape', 'phasor_format', 'analog_format', 'freq_format', 'phnmr', 'annmr', 'dgnmr', 'phnam', 'annam', 'dgnam', 'phasor_conv_phunit', 'phasor_conv_phvalue', 'phasor_conv_upsampled_interpolation', 'phasor_conv_upsampled_extrapolation', 'phasor_conv_downsampled_reselection', 'phasor_conv_downsampled_fir_filter', 'phasor_conv_downsampled_no_fir_filter', 'phasor_conv_filtered_without_changing_sampling', 'phasor_conv_calibration_mag_adj', 'phasor_conv_calibration_phas_adj', 'phasor_conv_rotation_phase_adj', 'phasor_conv_pseudo_phasor_val', 'phasor_conv_mod_appl', 'phasor_conv_phasor_component', 'phasor_conv_phasor_type', 'phasor_conv_user_def', 'phasor_conv_scale_factor', 'phasor_conv_angle_adj', 'analog_conv_analog_flags', 'analog_conv_user_defined_scaling', 'analog_conv_mag_scale', 'analog_conv_offset', 'digital_conv_normal_status_mask', 'digital_conv_valid_inputs_mask', 'pmu_lat', 'pmu_lon', 'pmu_elev', 'window', 'group_delay', 'fnom', 'cfgcnt' ]" + code => "event.set('[zeek_cols]', $zeek_synchrophasor_cfg_detail_field_names.zip(event.get('[message]')).to_h)" + } + } + + mutate { + id => "mutate_add_fields_zeek_synchrophasor_cfg_detail" + add_field => { + "[zeek_cols][service]" => "synchrophasor" + } + add_tag => [ "ics" ] + } + + } else if ([log_source] == "synchrophasor_data") { + ############################################################################################################################# + # synchrophasor_data.log + # main.zeek (https://github.com/cisagov/icsnpp-synchrophasor) + + dissect { + id => "dissect_zeek_synchrophasor_data" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + mapping => { + "[message]" => "%{[zeek_cols][ts]} %{[zeek_cols][uid]} %{[zeek_cols][orig_h]} %{[zeek_cols][orig_p]} %{[zeek_cols][resp_h]} %{[zeek_cols][resp_p]} %{[zeek_cols][proto]} %{[zeek_cols][frame_type]} %{[zeek_cols][frame_size]} %{[zeek_cols][header_time_stamp]} %{[zeek_cols][pmu_count_expected]} %{[zeek_cols][pmu_count_actual]} %{[zeek_cols][data_frame_id]}" + } + } + + if ("_dissectfailure" in [tags]) { + mutate { + id => "mutate_split_zeek_synchrophasor_data" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + split => { "[message]" => " " } + } + ruby { + id => "ruby_zip_zeek_synchrophasor_data" + init => "$zeek_synchrophasor_data_field_names = [ 'ts', 'uid', 'orig_h', 'orig_p', 'resp_h', 'resp_p', 'proto', 'frame_type', 'frame_size', 'header_time_stamp', 'pmu_count_expected', 'pmu_count_actual', 'data_frame_id' ]" + code => "event.set('[zeek_cols]', $zeek_synchrophasor_data_field_names.zip(event.get('[message]')).to_h)" + } + } + + mutate { + id => "mutate_add_fields_zeek_synchrophasor_data" + add_field => { + "[zeek_cols][service]" => "synchrophasor" + } + add_tag => [ "ics" ] + } + + } else if ([log_source] == "synchrophasor_data_detail") { + ############################################################################################################################# + # synchrophasor_data_detail.log + # main.zeek (https://github.com/cisagov/icsnpp-synchrophasor) + + dissect { + id => "dissect_zeek_synchrophasor_data_detail" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + mapping => { + "[message]" => "%{[zeek_cols][ts]} %{[zeek_cols][uid]} %{[zeek_cols][orig_h]} %{[zeek_cols][orig_p]} %{[zeek_cols][resp_h]} %{[zeek_cols][resp_p]} %{[zeek_cols][proto]} %{[zeek_cols][frame_type]} %{[zeek_cols][header_time_stamp]} %{[zeek_cols][data_frame_id]} %{[zeek_cols][pmu_idx]} %{[zeek_cols][trigger_reason]} %{[zeek_cols][unlocked_time]} %{[zeek_cols][pmu_time_quality]} %{[zeek_cols][data_modified]} %{[zeek_cols][config_change]} %{[zeek_cols][pmu_trigger_pickup]} %{[zeek_cols][data_sorting_type]} %{[zeek_cols][pmu_sync_error]} %{[zeek_cols][data_error_indicator]} %{[zeek_cols][est_rectangular_real]} %{[zeek_cols][est_rectangular_imaginary]} %{[zeek_cols][est_polar_magnitude]} %{[zeek_cols][est_polar_angle]} %{[zeek_cols][freq_dev_mhz]} %{[zeek_cols][rocof]} %{[zeek_cols][analog_data]} %{[zeek_cols][digital]}" + } + } + + if ("_dissectfailure" in [tags]) { + mutate { + id => "mutate_split_zeek_synchrophasor_data_detail" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + split => { "[message]" => " " } + } + ruby { + id => "ruby_zip_zeek_synchrophasor_data_detail" + init => "$zeek_synchrophasor_data_detail_field_names = [ 'ts', 'uid', 'orig_h', 'orig_p', 'resp_h', 'resp_p', 'proto', 'frame_type', 'header_time_stamp', 'data_frame_id', 'pmu_idx', 'trigger_reason', 'unlocked_time', 'pmu_time_quality', 'data_modified', 'config_change', 'pmu_trigger_pickup', 'data_sorting_type', 'pmu_sync_error', 'data_error_indicator', 'est_rectangular_real', 'est_rectangular_imaginary', 'est_polar_magnitude', 'est_polar_angle', 'freq_dev_mhz', 'rocof', 'analog_data', 'digital' ]" + code => "event.set('[zeek_cols]', $zeek_synchrophasor_data_detail_field_names.zip(event.get('[message]')).to_h)" + } + } + + mutate { + id => "mutate_add_fields_zeek_synchrophasor_data_detail" + add_field => { + "[zeek_cols][service]" => "synchrophasor" + } + add_tag => [ "ics" ] + } + + } else if ([log_source] == "synchrophasor_hdr") { + ############################################################################################################################# + # synchrophasor_hdr.log + # main.zeek (https://github.com/cisagov/icsnpp-synchrophasor) + + dissect { + id => "dissect_zeek_synchrophasor_hdr" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + mapping => { + "[message]" => "%{[zeek_cols][ts]} %{[zeek_cols][uid]} %{[zeek_cols][orig_h]} %{[zeek_cols][orig_p]} %{[zeek_cols][resp_h]} %{[zeek_cols][resp_p]} %{[zeek_cols][proto]} %{[zeek_cols][frame_type]} %{[zeek_cols][frame_size]} %{[zeek_cols][header_time_stamp]} %{[zeek_cols][data]}" + } + } + + if ("_dissectfailure" in [tags]) { + mutate { + id => "mutate_split_zeek_synchrophasor_hdr" + # zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP + split => { "[message]" => " " } + } + ruby { + id => "ruby_zip_zeek_synchrophasor_hdr" + init => "$zeek_synchrophasor_hdr_field_names = [ 'ts', 'uid', 'orig_h', 'orig_p', 'resp_h', 'resp_p', 'proto', 'frame_type', 'frame_size', 'header_time_stamp', 'data' ]" + code => "event.set('[zeek_cols]', $zeek_synchrophasor_hdr_field_names.zip(event.get('[message]')).to_h)" + } + } + + mutate { + id => "mutate_add_fields_zeek_synchrophasor_hdr" + add_field => { + "[zeek_cols][service]" => "synchrophasor" + } + add_tag => [ "ics" ] + } + } else if ([log_source] == "syslog") { ############################################################################################################################# # syslog.log diff --git a/logstash/pipelines/zeek/12_zeek_mutate.conf b/logstash/pipelines/zeek/12_zeek_mutate.conf index 54a454ce8..63aa8171e 100644 --- a/logstash/pipelines/zeek/12_zeek_mutate.conf +++ b/logstash/pipelines/zeek/12_zeek_mutate.conf @@ -2153,6 +2153,66 @@ filter { if ([zeek][stun_nat][wan_port]) { mutate { id => "mutate_add_field_ecs_zeek_stun_nat_wan_port" add_field => { "[source][nat][port]" => "%{[zeek][stun_nat][wan_port]}" } } } + } else if ([log_source] =~ /^synchrophasor/) { + + mutate { + id => "mutate_rename_synchrophasor_fields" + rename => { "[zeek][synchrophasor_cfg][frame_size]" => "[zeek][synchrophasor][frame_size]" } + rename => { "[zeek][synchrophasor_cfg][frame_type]" => "[zeek][synchrophasor][frame_type]" } + rename => { "[zeek][synchrophasor_cfg][header_time_stamp]" => "[zeek][synchrophasor][header_time_stamp]" } + rename => { "[zeek][synchrophasor_cfg_detail][frame_type]" => "[zeek][synchrophasor][frame_type]" } + rename => { "[zeek][synchrophasor_cfg_detail][header_time_stamp]" => "[zeek][synchrophasor][header_time_stamp]" } + rename => { "[zeek][synchrophasor_cmd][frame_size]" => "[zeek][synchrophasor][frame_size]" } + rename => { "[zeek][synchrophasor_cmd][frame_type]" => "[zeek][synchrophasor][frame_type]" } + rename => { "[zeek][synchrophasor_cmd][header_time_stamp]" => "[zeek][synchrophasor][header_time_stamp]" } + rename => { "[zeek][synchrophasor_data][frame_size]" => "[zeek][synchrophasor][frame_size]" } + rename => { "[zeek][synchrophasor_data][frame_type]" => "[zeek][synchrophasor][frame_type]" } + rename => { "[zeek][synchrophasor_data][header_time_stamp]" => "[zeek][synchrophasor][header_time_stamp]" } + rename => { "[zeek][synchrophasor_data_detail][frame_type]" => "[zeek][synchrophasor][frame_type]" } + rename => { "[zeek][synchrophasor_data_detail][header_time_stamp]" => "[zeek][synchrophasor][header_time_stamp]" } + rename => { "[zeek][synchrophasor_hdr][frame_size]" => "[zeek][synchrophasor][frame_size]" } + rename => { "[zeek][synchrophasor_hdr][frame_type]" => "[zeek][synchrophasor][frame_type]" } + rename => { "[zeek][synchrophasor_hdr][header_time_stamp]" => "[zeek][synchrophasor][header_time_stamp]" } + } + + mutate { + id => "mutate_split_synchrophasor_fields" + split => { "[zeek][synchrophasor_cfg_detail][analog_conv_analog_flags]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][analog_conv_mag_scale]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][analog_conv_offset]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][analog_conv_user_defined_scaling]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][annam]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][dgnam]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][digital_conv_normal_status_mask]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][digital_conv_valid_inputs_mask]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_angle_adj]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_calibration_mag_adj]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_calibration_phas_adj]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_downsampled_fir_filter]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_downsampled_no_fir_filter]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_downsampled_reselection]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_filtered_without_changing_sampling]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_mod_appl]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_phasor_component]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_phasor_type]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_phunit]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_phvalue]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_pseudo_phasor_val]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_rotation_phase_adj]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_scale_factor]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_upsampled_extrapolation]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_upsampled_interpolation]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phasor_conv_user_def]" => "," } + split => { "[zeek][synchrophasor_cfg_detail][phnam]" => "," } + split => { "[zeek][synchrophasor_cmd][extframe]" => "," } + split => { "[zeek][synchrophasor_data_detail][analog_data]" => "," } + split => { "[zeek][synchrophasor_data_detail][digital]" => "," } + split => { "[zeek][synchrophasor_data_detail][est_polar_angle]" => "," } + split => { "[zeek][synchrophasor_data_detail][est_polar_magnitude]" => "," } + split => { "[zeek][synchrophasor_data_detail][est_rectangular_imaginary]" => "," } + split => { "[zeek][synchrophasor_data_detail][est_rectangular_real]" => "," } + } + } else if ([log_source] == "tds_rpc") { ############################################################################################################################# # tds_rpc.log specific logic diff --git a/logstash/pipelines/zeek/13_zeek_normalize.conf b/logstash/pipelines/zeek/13_zeek_normalize.conf index f5d55ed3e..f06162037 100644 --- a/logstash/pipelines/zeek/13_zeek_normalize.conf +++ b/logstash/pipelines/zeek/13_zeek_normalize.conf @@ -155,6 +155,9 @@ filter { if ([zeek][ssl][ssl_version]) { mutate { id => "mutate_merge_normalize_zeek_ssl_ssl_version" merge => { "[network][protocol_version]" => "[zeek][ssl][ssl_version]" } } } + if ([zeek][synchrophasor][version]) { mutate { id => "mutate_merge_normalize_zeek_synchrophasor_version" + merge => { "[network][protocol_version]" => "[zeek][synchrophasor][version]" } } } + # Action ############################################################################################################ # collect all actions/operations/commands under the parent [event][action] array @@ -600,6 +603,9 @@ filter { if ([zeek][stun][method]) { mutate { id => "mutate_merge_normalize_zeek_stun_method" merge => { "[event][action]" => "[zeek][stun][method]" } } } + if ([zeek][synchrophasor_cmd][command]) { mutate { id => "mutate_merge_normalize_zeek_synchrophasor_cmd_command" + merge => { "[event][action]" => "[zeek][synchrophasor_cmd][command]" } } } + if ([zeek][tds][command]) { mutate { id => "mutate_merge_normalize_zeek_tds_command" merge => { "[event][action]" => "[zeek][tds][command]" } } } diff --git a/logstash/pipelines/zeek/14_zeek_convert.conf b/logstash/pipelines/zeek/14_zeek_convert.conf index c460184b0..51e9b2dfb 100644 --- a/logstash/pipelines/zeek/14_zeek_convert.conf +++ b/logstash/pipelines/zeek/14_zeek_convert.conf @@ -373,6 +373,19 @@ filter { } } + if ([zeek][synchrophasor][header_time_stamp]) { + if ([zeek][synchrophasor][header_time_stamp] == "0.000000") { + mutate { id => "mutate_remove_field_zeek_synchrophasor_header_time_stamp" + remove_field => [ "[zeek][synchrophasor][header_time_stamp]" ] } + } else { + date { + id => "date_zeek_synchrophasor_header_time_stamp" + match => [ "[zeek][synchrophasor][header_time_stamp]", "UNIX" ] + target => "[zeek][synchrophasor][header_time_stamp]" + } + } + } + if ([zeek][x509][certificate_not_valid_after]) { if ([zeek][x509][certificate_not_valid_after] == "0.000000") { mutate { id => "mutate_remove_field_zeek_x509_certificate_not_valid_after_zero" diff --git a/sensor-iso/config/includes.chroot/usr/local/etc/zeek/local.zeek b/sensor-iso/config/includes.chroot/usr/local/etc/zeek/local.zeek index 917c55401..0dcef400b 100644 --- a/sensor-iso/config/includes.chroot/usr/local/etc/zeek/local.zeek +++ b/sensor-iso/config/includes.chroot/usr/local/etc/zeek/local.zeek @@ -9,6 +9,7 @@ global disable_log_passwords = (getenv("ZEEK_DISABLE_LOG_PASSWORDS") == "") ? F global disable_ssl_validate_certs = (getenv("ZEEK_DISABLE_SSL_VALIDATE_CERTS") == "") ? F : T; global disable_track_all_assets = (getenv("ZEEK_DISABLE_TRACK_ALL_ASSETS") == "") ? F : T; global disable_best_guess_ics = (getenv("ZEEK_DISABLE_BEST_GUESS_ICS") == "") ? F : T; +global synchrophasor_detailed = (getenv("ZEEK_SYNCHROPHASOR_DETAILED") == "") ? F : T; global disable_spicy_dhcp = (getenv("ZEEK_DISABLE_SPICY_DHCP") == "") ? F : T; global disable_spicy_dns = (getenv("ZEEK_DISABLE_SPICY_DNS") == "") ? F : T; @@ -127,3 +128,8 @@ event zeek_init() &priority=-5 { redef LDAP::default_log_search_attributes = F; redef SNIFFPASS::notice_log_enable = F; redef CVE_2021_44228::log = F; +@if (synchrophasor_detailed) + redef SYNCHROPHASOR::log_data_frame = T; + redef SYNCHROPHASOR::log_data_detail = T; + redef SYNCHROPHASOR::log_cfg_detail = T; +@endif diff --git a/sensor-iso/interface/sensor_ctl/control_vars.conf b/sensor-iso/interface/sensor_ctl/control_vars.conf index e5b81c7b5..f185799a0 100644 --- a/sensor-iso/interface/sensor_ctl/control_vars.conf +++ b/sensor-iso/interface/sensor_ctl/control_vars.conf @@ -65,6 +65,7 @@ export ZEEK_DISABLE_SPICY_STUN= export ZEEK_DISABLE_SPICY_TAILSCALE= export ZEEK_DISABLE_SPICY_TFTP= export ZEEK_DISABLE_SPICY_WIREGUARD= +export ZEEK_SYNCHROPHASOR_DETAILED= # Suricata export SURICATA_CUSTOM_RULES_ONLY=false diff --git a/zeek/config/local.zeek b/zeek/config/local.zeek index 42df7ff2e..48ae031cf 100644 --- a/zeek/config/local.zeek +++ b/zeek/config/local.zeek @@ -9,6 +9,7 @@ global disable_log_passwords = (getenv("ZEEK_DISABLE_LOG_PASSWORDS") == "") ? F global disable_ssl_validate_certs = (getenv("ZEEK_DISABLE_SSL_VALIDATE_CERTS") == "") ? F : T; global disable_track_all_assets = (getenv("ZEEK_DISABLE_TRACK_ALL_ASSETS") == "") ? F : T; global disable_best_guess_ics = (getenv("ZEEK_DISABLE_BEST_GUESS_ICS") == "") ? F : T; +global synchrophasor_detailed = (getenv("ZEEK_SYNCHROPHASOR_DETAILED") == "") ? F : T; global disable_spicy_dhcp = (getenv("ZEEK_DISABLE_SPICY_DHCP") == "") ? F : T; global disable_spicy_dns = (getenv("ZEEK_DISABLE_SPICY_DNS") == "") ? F : T; @@ -127,3 +128,8 @@ event zeek_init() &priority=-5 { redef LDAP::default_log_search_attributes = F; redef SNIFFPASS::notice_log_enable = F; redef CVE_2021_44228::log = F; +@if (synchrophasor_detailed) + redef SYNCHROPHASOR::log_data_frame = T; + redef SYNCHROPHASOR::log_data_detail = T; + redef SYNCHROPHASOR::log_cfg_detail = T; +@endif From efcc7f131d766bc550052743877eced2b5414d65 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 26 Apr 2023 12:44:36 -0600 Subject: [PATCH 218/239] idaholab/Malcolm#190, a little normalization and dashboards --- .../024062a6-48d6-498f-a91a-3bf2da3a3cd3.json | 2 +- .../03207c00-d07e-11ec-b4a7-d1b4003706b7.json | 2 +- .../05e3e000-f118-11e9-acda-83a8e29e1a24.json | 2 +- .../078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b.json | 2 +- .../0a490422-0ce9-44bf-9a2d-19329ddde8c3.json | 2 +- .../0ad3d7c2-3441-485e-9dfe-dbb22e84e576.json | 2 +- .../0aed0e23-c8ac-4f2b-9f68-d04b6e7666b0.json | 2 +- .../0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa.json | 2 +- .../11be6381-beef-40a7-bdce-88c5398392fc.json | 2 +- .../11ddd980-e388-11e9-b568-cf17de8e860c.json | 2 +- .../12e3a130-d83b-11eb-a0b0-f328ce09b0b7.json | 2 +- .../152f29dc-51a2-4f53-93e9-6e92765567b8.json | 2 +- .../1cc01ff0-5205-11ec-a62c-7bc80e88f3f0.json | 2 +- .../1ce42250-3f99-11e9-a58e-8bdedb0915e8.json | 2 +- .../1fff49f6-0199-4a0f-820b-721aff9ff1f1.json | 2 +- .../29a1b290-eb98-11e9-a384-0fcf32210194.json | 2 +- .../2bec1490-eb94-11e9-a384-0fcf32210194.json | 2 +- .../2cc56240-e460-11ed-a9d5-9f591c284cb4.json | 361 ++++++++++++++++++ .../2cf94cd0-ecab-40a5-95a7-8419f3a39cd9.json | 2 +- .../2d98bb8e-214c-4374-837b-20e1bcd63a5e.json | 2 +- .../32587740-ef88-11e9-b38a-2db3ee640e88.json | 2 +- .../36ed695f-edcc-47c1-b0ec-50d20c93ce0f.json | 2 +- .../37041ee1-79c0-4684-a436-3173b0e89876.json | 2 +- .../39abfe30-3f99-11e9-a58e-8bdedb0915e8.json | 2 +- .../42e831b9-41a9-4f35-8b7d-e1566d368773.json | 2 +- .../432af556-c5c0-4cc3-8166-b274b4e3a406.json | 2 +- .../4a073440-b286-11eb-a4d4-09fa12a6ebd4.json | 2 +- .../4a4bde20-4760-11ea-949c-bbb5a9feecbf.json | 2 +- .../4e5f106e-c60a-4226-8f64-d534abb912ab.json | 2 +- .../50ced171-1b10-4c3f-8b67-2db9635661a6.json | 2 +- .../543118a9-02d7-43fe-b669-b8652177fc37.json | 2 +- .../55e332d0-3f99-11e9-a58e-8bdedb0915e8.json | 2 +- .../5694ca60-cbdf-11ec-a50a-5fedd672f5c5.json | 2 +- .../60d78fbd-471c-4f59-a9e3-189b33a13644.json | 2 +- .../665d1610-523d-11e9-a30e-e3576242f3ed.json | 2 +- .../677ee170-809e-11ed-8d5b-07069f823b6f.json | 2 +- .../76f2f912-80da-44cd-ab66-6a73c8344cc3.json | 2 +- .../77fc9960-3f99-11e9-a58e-8bdedb0915e8.json | 2 +- .../7f41913f-cba8-43f5-82a8-241b7ead03e0.json | 2 +- .../7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb.json | 2 +- .../82da3101-2a9c-4ae2-bb61-d447a3fbe673.json | 2 +- .../870a5862-6c26-4a08-99fd-0c06cda85ba3.json | 2 +- .../87a32f90-ef58-11e9-974e-9d600036d105.json | 2 +- .../87d990cc-9e0b-41e5-b8fe-b10ae1da0c85.json | 2 +- .../89d1cc50-974c-11ed-bb6b-3fb06c879b11.json | 2 +- .../92985909-dc29-4533-9e80-d3182a0ecf1d.json | 2 +- .../95479950-41f2-11ea-88fa-7151df485405.json | 2 +- .../9ee51f94-3316-4fc5-bd89-93a52af69714.json | 2 +- .../a16110b0-3f99-11e9-a58e-8bdedb0915e8.json | 2 +- .../a33e0a50-afcd-11ea-993f-b7d8522a8bed.json | 2 +- .../a7514350-eba6-11e9-a384-0fcf32210194.json | 2 +- .../abdd7550-2c7c-40dc-947e-f6d186a158c4.json | 2 +- .../ae79b7d1-4281-4095-b2f6-fa7eafda9970.json | 2 +- .../af5df620-eeb6-11e9-bdef-65a192b7f586.json | 2 +- .../b50c8d17-6ed3-4de6-aed4-5181032810b2.json | 2 +- .../b9f247c0-3f99-11e9-a58e-8bdedb0915e8.json | 2 +- .../bb827f8e-639e-468c-93c8-9f5bc132eb8f.json | 2 +- .../bed185a0-ef82-11e9-b38a-2db3ee640e88.json | 2 +- .../bf5efbb0-60f1-11eb-9d60-dbf0411cfc48.json | 2 +- .../c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2.json | 2 +- .../ca5799a0-56b5-11eb-b749-576de068f8ad.json | 2 +- .../caef3ade-d289-4d05-a511-149f3e97f238.json | 2 +- .../d2dd0180-06b1-11ec-8c6b-353266ade330.json | 2 +- .../d41fe630-3f98-11e9-a58e-8bdedb0915e8.json | 2 +- .../d4fd6afd-15cb-42bf-8a25-03dd8e59b327.json | 2 +- .../dd87edd0-796a-11ec-9ce6-b395c1ff58f4.json | 2 +- .../e09a4b86-29b5-4256-bb3b-802ac9f90404.json | 2 +- .../e76d05c0-eb9f-11e9-a384-0fcf32210194.json | 2 +- .../ed8a6640-3f98-11e9-a58e-8bdedb0915e8.json | 2 +- .../f1f09567-fc7f-450b-a341-19d2f2bb468b.json | 2 +- .../f394057d-1b16-4174-b994-7045f423a416.json | 2 +- .../f77bf097-18a8-465c-b634-eb2acc7a4f26.json | 2 +- .../fa141950-ef89-11e9-b38a-2db3ee640e88.json | 2 +- .../fa477130-2b8a-11ec-a9f2-3911c8571bfd.json | 2 +- logstash/pipelines/zeek/12_zeek_mutate.conf | 9 + 75 files changed, 443 insertions(+), 73 deletions(-) create mode 100644 dashboards/dashboards/2cc56240-e460-11ed-a9d5-9f591c284cb4.json diff --git a/dashboards/dashboards/024062a6-48d6-498f-a91a-3bf2da3a3cd3.json b/dashboards/dashboards/024062a6-48d6-498f-a91a-3bf2da3a3cd3.json index b43c3c475..168a63b2d 100644 --- a/dashboards/dashboards/024062a6-48d6-498f-a91a-3bf2da3a3cd3.json +++ b/dashboards/dashboards/024062a6-48d6-498f-a91a-3bf2da3a3cd3.json @@ -112,7 +112,7 @@ "version": "Wzc0MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/03207c00-d07e-11ec-b4a7-d1b4003706b7.json b/dashboards/dashboards/03207c00-d07e-11ec-b4a7-d1b4003706b7.json index 4cd8be739..113bae5ca 100644 --- a/dashboards/dashboards/03207c00-d07e-11ec-b4a7-d1b4003706b7.json +++ b/dashboards/dashboards/03207c00-d07e-11ec-b4a7-d1b4003706b7.json @@ -87,7 +87,7 @@ "version": "Wzc5NSwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/05e3e000-f118-11e9-acda-83a8e29e1a24.json b/dashboards/dashboards/05e3e000-f118-11e9-acda-83a8e29e1a24.json index 400c4a68a..9794d1f77 100644 --- a/dashboards/dashboards/05e3e000-f118-11e9-acda-83a8e29e1a24.json +++ b/dashboards/dashboards/05e3e000-f118-11e9-acda-83a8e29e1a24.json @@ -92,7 +92,7 @@ "version": "Wzg3OSwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b.json b/dashboards/dashboards/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b.json index 8a087ca3d..010a1fa65 100644 --- a/dashboards/dashboards/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b.json +++ b/dashboards/dashboards/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b.json @@ -87,7 +87,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/0a490422-0ce9-44bf-9a2d-19329ddde8c3.json b/dashboards/dashboards/0a490422-0ce9-44bf-9a2d-19329ddde8c3.json index 88152f6a0..e648c3164 100644 --- a/dashboards/dashboards/0a490422-0ce9-44bf-9a2d-19329ddde8c3.json +++ b/dashboards/dashboards/0a490422-0ce9-44bf-9a2d-19329ddde8c3.json @@ -87,7 +87,7 @@ "version": "WzkzNiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/0ad3d7c2-3441-485e-9dfe-dbb22e84e576.json b/dashboards/dashboards/0ad3d7c2-3441-485e-9dfe-dbb22e84e576.json index 64e92c1b7..aa545fa32 100644 --- a/dashboards/dashboards/0ad3d7c2-3441-485e-9dfe-dbb22e84e576.json +++ b/dashboards/dashboards/0ad3d7c2-3441-485e-9dfe-dbb22e84e576.json @@ -87,7 +87,7 @@ "version": "Wzc5NSwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/0aed0e23-c8ac-4f2b-9f68-d04b6e7666b0.json b/dashboards/dashboards/0aed0e23-c8ac-4f2b-9f68-d04b6e7666b0.json index 0d4e47fc8..aa52e0c61 100644 --- a/dashboards/dashboards/0aed0e23-c8ac-4f2b-9f68-d04b6e7666b0.json +++ b/dashboards/dashboards/0aed0e23-c8ac-4f2b-9f68-d04b6e7666b0.json @@ -87,7 +87,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa.json b/dashboards/dashboards/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa.json index 2fbcb1e8b..46e49a6b9 100644 --- a/dashboards/dashboards/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa.json +++ b/dashboards/dashboards/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa.json @@ -107,7 +107,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/11be6381-beef-40a7-bdce-88c5398392fc.json b/dashboards/dashboards/11be6381-beef-40a7-bdce-88c5398392fc.json index 742195812..14bf63445 100644 --- a/dashboards/dashboards/11be6381-beef-40a7-bdce-88c5398392fc.json +++ b/dashboards/dashboards/11be6381-beef-40a7-bdce-88c5398392fc.json @@ -82,7 +82,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/11ddd980-e388-11e9-b568-cf17de8e860c.json b/dashboards/dashboards/11ddd980-e388-11e9-b568-cf17de8e860c.json index 95211d9d5..d38c0c5aa 100644 --- a/dashboards/dashboards/11ddd980-e388-11e9-b568-cf17de8e860c.json +++ b/dashboards/dashboards/11ddd980-e388-11e9-b568-cf17de8e860c.json @@ -87,7 +87,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/12e3a130-d83b-11eb-a0b0-f328ce09b0b7.json b/dashboards/dashboards/12e3a130-d83b-11eb-a0b0-f328ce09b0b7.json index 2bc42ed26..58b3af7bb 100644 --- a/dashboards/dashboards/12e3a130-d83b-11eb-a0b0-f328ce09b0b7.json +++ b/dashboards/dashboards/12e3a130-d83b-11eb-a0b0-f328ce09b0b7.json @@ -82,7 +82,7 @@ "version": "Wzc1NSwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/152f29dc-51a2-4f53-93e9-6e92765567b8.json b/dashboards/dashboards/152f29dc-51a2-4f53-93e9-6e92765567b8.json index 013f4b224..f99101751 100644 --- a/dashboards/dashboards/152f29dc-51a2-4f53-93e9-6e92765567b8.json +++ b/dashboards/dashboards/152f29dc-51a2-4f53-93e9-6e92765567b8.json @@ -117,7 +117,7 @@ "version": "WzgxOSwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0.json b/dashboards/dashboards/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0.json index 6dc85898b..d37b1f509 100644 --- a/dashboards/dashboards/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0.json +++ b/dashboards/dashboards/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0.json @@ -92,7 +92,7 @@ "version": "WzkzNiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/1ce42250-3f99-11e9-a58e-8bdedb0915e8.json b/dashboards/dashboards/1ce42250-3f99-11e9-a58e-8bdedb0915e8.json index ecd237ca6..34e61f1aa 100644 --- a/dashboards/dashboards/1ce42250-3f99-11e9-a58e-8bdedb0915e8.json +++ b/dashboards/dashboards/1ce42250-3f99-11e9-a58e-8bdedb0915e8.json @@ -57,7 +57,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/1fff49f6-0199-4a0f-820b-721aff9ff1f1.json b/dashboards/dashboards/1fff49f6-0199-4a0f-820b-721aff9ff1f1.json index 3fd5118b6..4a8c8e603 100644 --- a/dashboards/dashboards/1fff49f6-0199-4a0f-820b-721aff9ff1f1.json +++ b/dashboards/dashboards/1fff49f6-0199-4a0f-820b-721aff9ff1f1.json @@ -72,7 +72,7 @@ "version": "Wzc4NCwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/29a1b290-eb98-11e9-a384-0fcf32210194.json b/dashboards/dashboards/29a1b290-eb98-11e9-a384-0fcf32210194.json index 22211085c..965fadb4e 100644 --- a/dashboards/dashboards/29a1b290-eb98-11e9-a384-0fcf32210194.json +++ b/dashboards/dashboards/29a1b290-eb98-11e9-a384-0fcf32210194.json @@ -102,7 +102,7 @@ "version": "Wzc4NSwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/2bec1490-eb94-11e9-a384-0fcf32210194.json b/dashboards/dashboards/2bec1490-eb94-11e9-a384-0fcf32210194.json index f4955b831..4045deae4 100644 --- a/dashboards/dashboards/2bec1490-eb94-11e9-a384-0fcf32210194.json +++ b/dashboards/dashboards/2bec1490-eb94-11e9-a384-0fcf32210194.json @@ -107,7 +107,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/2cc56240-e460-11ed-a9d5-9f591c284cb4.json b/dashboards/dashboards/2cc56240-e460-11ed-a9d5-9f591c284cb4.json new file mode 100644 index 000000000..2bd4e698f --- /dev/null +++ b/dashboards/dashboards/2cc56240-e460-11ed-a9d5-9f591c284cb4.json @@ -0,0 +1,361 @@ +{ + "version": "2.6.0", + "objects": [ + { + "id": "2cc56240-e460-11ed-a9d5-9f591c284cb4", + "type": "dashboard", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T18:41:21.569Z", + "version": "Wzk3MSwxXQ==", + "attributes": { + "title": "Synchrophasor", + "hits": 0, + "description": "Dashboard for the DNP3 Protocol", + "panelsJSON": "[{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":8,\"h\":28,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{},\"panelRefName\":\"panel_0\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":8,\"y\":0,\"w\":13,\"h\":18,\"i\":\"38d18152-c0f9-436b-a19d-ac76cfbc1bac\"},\"panelIndex\":\"38d18152-c0f9-436b-a19d-ac76cfbc1bac\",\"embeddableConfig\":{},\"panelRefName\":\"panel_1\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":21,\"y\":0,\"w\":27,\"h\":18,\"i\":\"b3c290e3-6d8f-4616-beaa-f5183a0d5208\"},\"panelIndex\":\"b3c290e3-6d8f-4616-beaa-f5183a0d5208\",\"embeddableConfig\":{},\"panelRefName\":\"panel_2\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":8,\"y\":18,\"w\":8,\"h\":19,\"i\":\"faf91692-201a-47c1-9439-62cb71e577de\"},\"panelIndex\":\"faf91692-201a-47c1-9439-62cb71e577de\",\"embeddableConfig\":{},\"panelRefName\":\"panel_3\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":16,\"y\":18,\"w\":10,\"h\":19,\"i\":\"3a54cfa0-dc19-49ad-b63f-8e570227587d\"},\"panelIndex\":\"3a54cfa0-dc19-49ad-b63f-8e570227587d\",\"embeddableConfig\":{},\"panelRefName\":\"panel_4\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":26,\"y\":18,\"w\":22,\"h\":19,\"i\":\"118707f0-37e5-4838-b58f-a9c75b37f021\"},\"panelIndex\":\"118707f0-37e5-4838-b58f-a9c75b37f021\",\"embeddableConfig\":{},\"panelRefName\":\"panel_5\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":28,\"w\":8,\"h\":9,\"i\":\"3033af99-af03-4def-b524-55bf5706e9e7\"},\"panelIndex\":\"3033af99-af03-4def-b524-55bf5706e9e7\",\"embeddableConfig\":{},\"panelRefName\":\"panel_6\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":37,\"w\":48,\"h\":24,\"i\":\"8afdbe78-d879-4285-b589-769e1f006ebf\"},\"panelIndex\":\"8afdbe78-d879-4285-b589-769e1f006ebf\",\"embeddableConfig\":{},\"panelRefName\":\"panel_7\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":61,\"w\":48,\"h\":22,\"i\":\"dba8956a-61cf-4d3a-a691-07a97072b51c\"},\"panelIndex\":\"dba8956a-61cf-4d3a-a691-07a97072b51c\",\"embeddableConfig\":{},\"panelRefName\":\"panel_8\"}]", + "optionsJSON": "{\"useMargins\":true}", + "version": 1, + "timeRestore": false, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"highlightAll\":false,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}" + } + }, + "references": [ + { + "name": "panel_0", + "type": "visualization", + "id": "df9e399b-efa5-4e33-b0ac-a7668a8ac2b3" + }, + { + "name": "panel_1", + "type": "visualization", + "id": "a4acba10-e460-11ed-a9d5-9f591c284cb4" + }, + { + "name": "panel_2", + "type": "visualization", + "id": "f3dffed0-e460-11ed-a9d5-9f591c284cb4" + }, + { + "name": "panel_3", + "type": "visualization", + "id": "2928e520-e461-11ed-a9d5-9f591c284cb4" + }, + { + "name": "panel_4", + "type": "visualization", + "id": "606cff30-e461-11ed-a9d5-9f591c284cb4" + }, + { + "name": "panel_5", + "type": "visualization", + "id": "e286d040-e461-11ed-a9d5-9f591c284cb4" + }, + { + "name": "panel_6", + "type": "visualization", + "id": "9ba20280-e461-11ed-a9d5-9f591c284cb4" + }, + { + "name": "panel_7", + "type": "search", + "id": "031207a0-e460-11ed-a9d5-9f591c284cb4" + }, + { + "name": "panel_8", + "type": "search", + "id": "53729e40-e45f-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "dashboard": "7.9.3" + } + }, + { + "id": "df9e399b-efa5-4e33-b0ac-a7668a8ac2b3", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T17:45:20.809Z", + "version": "WzgzOCwxXQ==", + "attributes": { + "title": "Network Logs", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" + } + }, + "references": [], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "a4acba10-e460-11ed-a9d5-9f591c284cb4", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T18:32:30.574Z", + "version": "Wzk2MiwxXQ==", + "attributes": { + "title": "Synchrophasor - Log Count", + "visState": "{\"title\":\"Synchrophasor - Log Count\",\"type\":\"metric\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"event.dataset\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Log Type\"},\"schema\":\"group\"}],\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":36}}}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "031207a0-e460-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "f3dffed0-e460-11ed-a9d5-9f591c284cb4", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T18:34:17.788Z", + "version": "Wzk2MywxXQ==", + "attributes": { + "title": "Synchrophasor - Log Count Over Time", + "visState": "{\"title\":\"Synchrophasor - Log Count Over Time\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"params\":{\"field\":\"firstPacket\",\"timeRange\":{\"from\":\"now-15y\",\"to\":\"now\"},\"useNormalizedOpenSearchInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{},\"customLabel\":\"\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"square root\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"}}}", + "uiStateJSON": "{\"vis\":{\"legendOpen\":false}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "53729e40-e45f-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "2928e520-e461-11ed-a9d5-9f591c284cb4", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T18:37:39.936Z", + "version": "Wzk2NywxXQ==", + "attributes": { + "title": "Synchrophasor - Source", + "visState": "{\"title\":\"Synchrophasor - Source\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"source.ip\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":250,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Source IP\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "53729e40-e45f-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "606cff30-e461-11ed-a9d5-9f591c284cb4", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T18:37:19.904Z", + "version": "Wzk2NiwxXQ==", + "attributes": { + "title": "Synchrophasor - Destination", + "visState": "{\"title\":\"Synchrophasor - Destination\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"destination.ip\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":250,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Destination IP\"},\"schema\":\"bucket\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"destination.port\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Destination Port\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "53729e40-e45f-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "e286d040-e461-11ed-a9d5-9f591c284cb4", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T18:40:58.179Z", + "version": "Wzk3MCwxXQ==", + "attributes": { + "title": "Synchrophasor - Action", + "visState": "{\"title\":\"Synchrophasor - Action\",\"type\":\"horizontal_bar\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"event.action\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Action\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":200},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":75,\"filter\":true,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"}}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "031207a0-e460-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "9ba20280-e461-11ed-a9d5-9f591c284cb4", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T18:38:59.240Z", + "version": "Wzk2OCwxXQ==", + "attributes": { + "title": "Synchrophasor - Transport", + "visState": "{\"title\":\"Synchrophasor - Transport\",\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"network.transport\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"Unknown\",\"customLabel\":\"Transport\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":true,\"values\":true,\"last_level\":true,\"truncate\":100}}}", + "uiStateJSON": "{\"vis\":{\"legendOpen\":false}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "53729e40-e45f-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "031207a0-e460-11ed-a9d5-9f591c284cb4", + "type": "search", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T18:27:33.784Z", + "version": "Wzk1OCwxXQ==", + "attributes": { + "title": "Synchrophasor and Related - Logs", + "description": "", + "hits": 0, + "columns": [ + "event.dataset", + "network.transport", + "source.ip", + "destination.ip", + "destination.port", + "zeek.synchrophasor.frame_type", + "event.action", + "totDataBytes", + "zeek.uid" + ], + "sort": [], + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"highlightAll\":false,\"version\":true,\"query\":{\"query\":\"event.provider:zeek AND event.dataset:synchrophasor*\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + } + }, + "references": [ + { + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern", + "id": "arkime_sessions3-*" + } + ], + "migrationVersion": { + "search": "7.9.3" + } + }, + { + "id": "53729e40-e45f-11ed-a9d5-9f591c284cb4", + "type": "search", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T18:22:39.138Z", + "version": "Wzk0NCwxXQ==", + "attributes": { + "title": "Synchrophasor - Logs", + "description": "", + "hits": 0, + "columns": [ + "network.transport", + "network.protocol_version", + "source.ip", + "destination.ip", + "destination.port", + "zeek.synchrophasor.data_stream_id", + "zeek.synchrophasor.history", + "zeek.synchrophasor.data_rate", + "zeek.synchrophasor.data_frame_count", + "zeek.synchrophasor.frame_size_tot", + "zeek.uid" + ], + "sort": [], + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"highlightAll\":false,\"version\":true,\"query\":{\"query\":\"event.provider:zeek AND event.dataset:synchrophasor\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + } + }, + "references": [ + { + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern", + "id": "arkime_sessions3-*" + } + ], + "migrationVersion": { + "search": "7.9.3" + } + } + ] +} \ No newline at end of file diff --git a/dashboards/dashboards/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9.json b/dashboards/dashboards/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9.json index 54b3c598e..3cde1eb35 100644 --- a/dashboards/dashboards/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9.json +++ b/dashboards/dashboards/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9.json @@ -107,7 +107,7 @@ "version": "Wzg3OSwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/2d98bb8e-214c-4374-837b-20e1bcd63a5e.json b/dashboards/dashboards/2d98bb8e-214c-4374-837b-20e1bcd63a5e.json index 015df968f..290af940b 100644 --- a/dashboards/dashboards/2d98bb8e-214c-4374-837b-20e1bcd63a5e.json +++ b/dashboards/dashboards/2d98bb8e-214c-4374-837b-20e1bcd63a5e.json @@ -117,7 +117,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/32587740-ef88-11e9-b38a-2db3ee640e88.json b/dashboards/dashboards/32587740-ef88-11e9-b38a-2db3ee640e88.json index 3b77db2d0..d40c44f5a 100644 --- a/dashboards/dashboards/32587740-ef88-11e9-b38a-2db3ee640e88.json +++ b/dashboards/dashboards/32587740-ef88-11e9-b38a-2db3ee640e88.json @@ -72,7 +72,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/36ed695f-edcc-47c1-b0ec-50d20c93ce0f.json b/dashboards/dashboards/36ed695f-edcc-47c1-b0ec-50d20c93ce0f.json index 1bfb6dd9d..ff1f72d69 100644 --- a/dashboards/dashboards/36ed695f-edcc-47c1-b0ec-50d20c93ce0f.json +++ b/dashboards/dashboards/36ed695f-edcc-47c1-b0ec-50d20c93ce0f.json @@ -97,7 +97,7 @@ "version": "Wzc2OSwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/37041ee1-79c0-4684-a436-3173b0e89876.json b/dashboards/dashboards/37041ee1-79c0-4684-a436-3173b0e89876.json index ffcd9030c..590725d93 100644 --- a/dashboards/dashboards/37041ee1-79c0-4684-a436-3173b0e89876.json +++ b/dashboards/dashboards/37041ee1-79c0-4684-a436-3173b0e89876.json @@ -127,7 +127,7 @@ "version": "WzgwNSwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/39abfe30-3f99-11e9-a58e-8bdedb0915e8.json b/dashboards/dashboards/39abfe30-3f99-11e9-a58e-8bdedb0915e8.json index c75a75741..8850daf59 100644 --- a/dashboards/dashboards/39abfe30-3f99-11e9-a58e-8bdedb0915e8.json +++ b/dashboards/dashboards/39abfe30-3f99-11e9-a58e-8bdedb0915e8.json @@ -57,7 +57,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/42e831b9-41a9-4f35-8b7d-e1566d368773.json b/dashboards/dashboards/42e831b9-41a9-4f35-8b7d-e1566d368773.json index f71809251..ad9446029 100644 --- a/dashboards/dashboards/42e831b9-41a9-4f35-8b7d-e1566d368773.json +++ b/dashboards/dashboards/42e831b9-41a9-4f35-8b7d-e1566d368773.json @@ -102,7 +102,7 @@ "version": "WzkzNywxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/432af556-c5c0-4cc3-8166-b274b4e3a406.json b/dashboards/dashboards/432af556-c5c0-4cc3-8166-b274b4e3a406.json index 78dd9730f..9a0748b3b 100644 --- a/dashboards/dashboards/432af556-c5c0-4cc3-8166-b274b4e3a406.json +++ b/dashboards/dashboards/432af556-c5c0-4cc3-8166-b274b4e3a406.json @@ -97,7 +97,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/4a073440-b286-11eb-a4d4-09fa12a6ebd4.json b/dashboards/dashboards/4a073440-b286-11eb-a4d4-09fa12a6ebd4.json index 3716a5d5f..2888504dd 100644 --- a/dashboards/dashboards/4a073440-b286-11eb-a4d4-09fa12a6ebd4.json +++ b/dashboards/dashboards/4a073440-b286-11eb-a4d4-09fa12a6ebd4.json @@ -82,7 +82,7 @@ "version": "Wzg4MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/4a4bde20-4760-11ea-949c-bbb5a9feecbf.json b/dashboards/dashboards/4a4bde20-4760-11ea-949c-bbb5a9feecbf.json index 64358c88b..829073e75 100644 --- a/dashboards/dashboards/4a4bde20-4760-11ea-949c-bbb5a9feecbf.json +++ b/dashboards/dashboards/4a4bde20-4760-11ea-949c-bbb5a9feecbf.json @@ -92,7 +92,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/4e5f106e-c60a-4226-8f64-d534abb912ab.json b/dashboards/dashboards/4e5f106e-c60a-4226-8f64-d534abb912ab.json index ad9aa8a11..01ac8d835 100644 --- a/dashboards/dashboards/4e5f106e-c60a-4226-8f64-d534abb912ab.json +++ b/dashboards/dashboards/4e5f106e-c60a-4226-8f64-d534abb912ab.json @@ -87,7 +87,7 @@ "version": "Wzg3NCwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/50ced171-1b10-4c3f-8b67-2db9635661a6.json b/dashboards/dashboards/50ced171-1b10-4c3f-8b67-2db9635661a6.json index 02fec7b95..68dc7db3e 100644 --- a/dashboards/dashboards/50ced171-1b10-4c3f-8b67-2db9635661a6.json +++ b/dashboards/dashboards/50ced171-1b10-4c3f-8b67-2db9635661a6.json @@ -97,7 +97,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/543118a9-02d7-43fe-b669-b8652177fc37.json b/dashboards/dashboards/543118a9-02d7-43fe-b669-b8652177fc37.json index 01742676e..87df8a899 100644 --- a/dashboards/dashboards/543118a9-02d7-43fe-b669-b8652177fc37.json +++ b/dashboards/dashboards/543118a9-02d7-43fe-b669-b8652177fc37.json @@ -97,7 +97,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/55e332d0-3f99-11e9-a58e-8bdedb0915e8.json b/dashboards/dashboards/55e332d0-3f99-11e9-a58e-8bdedb0915e8.json index 7308b1224..c28f5a03c 100644 --- a/dashboards/dashboards/55e332d0-3f99-11e9-a58e-8bdedb0915e8.json +++ b/dashboards/dashboards/55e332d0-3f99-11e9-a58e-8bdedb0915e8.json @@ -47,7 +47,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/5694ca60-cbdf-11ec-a50a-5fedd672f5c5.json b/dashboards/dashboards/5694ca60-cbdf-11ec-a50a-5fedd672f5c5.json index 2f8f4e0c5..f8058b80d 100644 --- a/dashboards/dashboards/5694ca60-cbdf-11ec-a50a-5fedd672f5c5.json +++ b/dashboards/dashboards/5694ca60-cbdf-11ec-a50a-5fedd672f5c5.json @@ -97,7 +97,7 @@ "version": "Wzc4NCwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/60d78fbd-471c-4f59-a9e3-189b33a13644.json b/dashboards/dashboards/60d78fbd-471c-4f59-a9e3-189b33a13644.json index a7b88339d..eb3b6090a 100644 --- a/dashboards/dashboards/60d78fbd-471c-4f59-a9e3-189b33a13644.json +++ b/dashboards/dashboards/60d78fbd-471c-4f59-a9e3-189b33a13644.json @@ -87,7 +87,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/665d1610-523d-11e9-a30e-e3576242f3ed.json b/dashboards/dashboards/665d1610-523d-11e9-a30e-e3576242f3ed.json index b650c0ca8..937af33d4 100644 --- a/dashboards/dashboards/665d1610-523d-11e9-a30e-e3576242f3ed.json +++ b/dashboards/dashboards/665d1610-523d-11e9-a30e-e3576242f3ed.json @@ -77,7 +77,7 @@ "version": "Wzc4NCwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/677ee170-809e-11ed-8d5b-07069f823b6f.json b/dashboards/dashboards/677ee170-809e-11ed-8d5b-07069f823b6f.json index accf76c10..7eabc838c 100644 --- a/dashboards/dashboards/677ee170-809e-11ed-8d5b-07069f823b6f.json +++ b/dashboards/dashboards/677ee170-809e-11ed-8d5b-07069f823b6f.json @@ -117,7 +117,7 @@ "version": "WzgzNywxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/76f2f912-80da-44cd-ab66-6a73c8344cc3.json b/dashboards/dashboards/76f2f912-80da-44cd-ab66-6a73c8344cc3.json index 43d238541..c1c097be1 100644 --- a/dashboards/dashboards/76f2f912-80da-44cd-ab66-6a73c8344cc3.json +++ b/dashboards/dashboards/76f2f912-80da-44cd-ab66-6a73c8344cc3.json @@ -82,7 +82,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/77fc9960-3f99-11e9-a58e-8bdedb0915e8.json b/dashboards/dashboards/77fc9960-3f99-11e9-a58e-8bdedb0915e8.json index 0be49def4..659228b43 100644 --- a/dashboards/dashboards/77fc9960-3f99-11e9-a58e-8bdedb0915e8.json +++ b/dashboards/dashboards/77fc9960-3f99-11e9-a58e-8bdedb0915e8.json @@ -57,7 +57,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/7f41913f-cba8-43f5-82a8-241b7ead03e0.json b/dashboards/dashboards/7f41913f-cba8-43f5-82a8-241b7ead03e0.json index 0c54cce9b..f16700c2e 100644 --- a/dashboards/dashboards/7f41913f-cba8-43f5-82a8-241b7ead03e0.json +++ b/dashboards/dashboards/7f41913f-cba8-43f5-82a8-241b7ead03e0.json @@ -92,7 +92,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb.json b/dashboards/dashboards/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb.json index 247b8ac11..00bea6ecc 100644 --- a/dashboards/dashboards/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb.json +++ b/dashboards/dashboards/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb.json @@ -127,7 +127,7 @@ "version": "WzcyNiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/82da3101-2a9c-4ae2-bb61-d447a3fbe673.json b/dashboards/dashboards/82da3101-2a9c-4ae2-bb61-d447a3fbe673.json index 47b0b7650..7976e3d04 100644 --- a/dashboards/dashboards/82da3101-2a9c-4ae2-bb61-d447a3fbe673.json +++ b/dashboards/dashboards/82da3101-2a9c-4ae2-bb61-d447a3fbe673.json @@ -107,7 +107,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/870a5862-6c26-4a08-99fd-0c06cda85ba3.json b/dashboards/dashboards/870a5862-6c26-4a08-99fd-0c06cda85ba3.json index 1842a16d4..24ffe312f 100644 --- a/dashboards/dashboards/870a5862-6c26-4a08-99fd-0c06cda85ba3.json +++ b/dashboards/dashboards/870a5862-6c26-4a08-99fd-0c06cda85ba3.json @@ -102,7 +102,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/87a32f90-ef58-11e9-974e-9d600036d105.json b/dashboards/dashboards/87a32f90-ef58-11e9-974e-9d600036d105.json index 508a8945e..cfdabab35 100644 --- a/dashboards/dashboards/87a32f90-ef58-11e9-974e-9d600036d105.json +++ b/dashboards/dashboards/87a32f90-ef58-11e9-974e-9d600036d105.json @@ -92,7 +92,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85.json b/dashboards/dashboards/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85.json index e2c6301c3..53e4df327 100644 --- a/dashboards/dashboards/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85.json +++ b/dashboards/dashboards/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85.json @@ -62,7 +62,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/89d1cc50-974c-11ed-bb6b-3fb06c879b11.json b/dashboards/dashboards/89d1cc50-974c-11ed-bb6b-3fb06c879b11.json index ff6fbaccc..85ecd6ffc 100644 --- a/dashboards/dashboards/89d1cc50-974c-11ed-bb6b-3fb06c879b11.json +++ b/dashboards/dashboards/89d1cc50-974c-11ed-bb6b-3fb06c879b11.json @@ -102,7 +102,7 @@ "version": "WzgzNywxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/92985909-dc29-4533-9e80-d3182a0ecf1d.json b/dashboards/dashboards/92985909-dc29-4533-9e80-d3182a0ecf1d.json index 134065b96..1f4086dc7 100644 --- a/dashboards/dashboards/92985909-dc29-4533-9e80-d3182a0ecf1d.json +++ b/dashboards/dashboards/92985909-dc29-4533-9e80-d3182a0ecf1d.json @@ -87,7 +87,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/95479950-41f2-11ea-88fa-7151df485405.json b/dashboards/dashboards/95479950-41f2-11ea-88fa-7151df485405.json index 9ea454bc4..50ce2f5c9 100644 --- a/dashboards/dashboards/95479950-41f2-11ea-88fa-7151df485405.json +++ b/dashboards/dashboards/95479950-41f2-11ea-88fa-7151df485405.json @@ -102,7 +102,7 @@ "version": "WzgwNiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/9ee51f94-3316-4fc5-bd89-93a52af69714.json b/dashboards/dashboards/9ee51f94-3316-4fc5-bd89-93a52af69714.json index e2e677d8e..2cfabcde2 100644 --- a/dashboards/dashboards/9ee51f94-3316-4fc5-bd89-93a52af69714.json +++ b/dashboards/dashboards/9ee51f94-3316-4fc5-bd89-93a52af69714.json @@ -117,7 +117,7 @@ "version": "Wzc4NiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/a16110b0-3f99-11e9-a58e-8bdedb0915e8.json b/dashboards/dashboards/a16110b0-3f99-11e9-a58e-8bdedb0915e8.json index 35206c3cc..6342cc776 100644 --- a/dashboards/dashboards/a16110b0-3f99-11e9-a58e-8bdedb0915e8.json +++ b/dashboards/dashboards/a16110b0-3f99-11e9-a58e-8bdedb0915e8.json @@ -57,7 +57,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/a33e0a50-afcd-11ea-993f-b7d8522a8bed.json b/dashboards/dashboards/a33e0a50-afcd-11ea-993f-b7d8522a8bed.json index e828cc95b..3e3552135 100644 --- a/dashboards/dashboards/a33e0a50-afcd-11ea-993f-b7d8522a8bed.json +++ b/dashboards/dashboards/a33e0a50-afcd-11ea-993f-b7d8522a8bed.json @@ -77,7 +77,7 @@ "version": "Wzc4NSwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/a7514350-eba6-11e9-a384-0fcf32210194.json b/dashboards/dashboards/a7514350-eba6-11e9-a384-0fcf32210194.json index 628181b1c..d4e93a7ef 100644 --- a/dashboards/dashboards/a7514350-eba6-11e9-a384-0fcf32210194.json +++ b/dashboards/dashboards/a7514350-eba6-11e9-a384-0fcf32210194.json @@ -87,7 +87,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/abdd7550-2c7c-40dc-947e-f6d186a158c4.json b/dashboards/dashboards/abdd7550-2c7c-40dc-947e-f6d186a158c4.json index df96e4aa2..220fd99fa 100644 --- a/dashboards/dashboards/abdd7550-2c7c-40dc-947e-f6d186a158c4.json +++ b/dashboards/dashboards/abdd7550-2c7c-40dc-947e-f6d186a158c4.json @@ -167,7 +167,7 @@ "version": "Wzc4NiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/ae79b7d1-4281-4095-b2f6-fa7eafda9970.json b/dashboards/dashboards/ae79b7d1-4281-4095-b2f6-fa7eafda9970.json index 706b375ae..46d6f027b 100644 --- a/dashboards/dashboards/ae79b7d1-4281-4095-b2f6-fa7eafda9970.json +++ b/dashboards/dashboards/ae79b7d1-4281-4095-b2f6-fa7eafda9970.json @@ -87,7 +87,7 @@ "version": "WzkzNywxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/af5df620-eeb6-11e9-bdef-65a192b7f586.json b/dashboards/dashboards/af5df620-eeb6-11e9-bdef-65a192b7f586.json index 012c754aa..e6d9794ed 100644 --- a/dashboards/dashboards/af5df620-eeb6-11e9-bdef-65a192b7f586.json +++ b/dashboards/dashboards/af5df620-eeb6-11e9-bdef-65a192b7f586.json @@ -87,7 +87,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/b50c8d17-6ed3-4de6-aed4-5181032810b2.json b/dashboards/dashboards/b50c8d17-6ed3-4de6-aed4-5181032810b2.json index 520143c9d..26c32eade 100644 --- a/dashboards/dashboards/b50c8d17-6ed3-4de6-aed4-5181032810b2.json +++ b/dashboards/dashboards/b50c8d17-6ed3-4de6-aed4-5181032810b2.json @@ -57,7 +57,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/b9f247c0-3f99-11e9-a58e-8bdedb0915e8.json b/dashboards/dashboards/b9f247c0-3f99-11e9-a58e-8bdedb0915e8.json index be97b67ec..714dc2877 100644 --- a/dashboards/dashboards/b9f247c0-3f99-11e9-a58e-8bdedb0915e8.json +++ b/dashboards/dashboards/b9f247c0-3f99-11e9-a58e-8bdedb0915e8.json @@ -57,7 +57,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/bb827f8e-639e-468c-93c8-9f5bc132eb8f.json b/dashboards/dashboards/bb827f8e-639e-468c-93c8-9f5bc132eb8f.json index 574055151..6a3653cb7 100644 --- a/dashboards/dashboards/bb827f8e-639e-468c-93c8-9f5bc132eb8f.json +++ b/dashboards/dashboards/bb827f8e-639e-468c-93c8-9f5bc132eb8f.json @@ -107,7 +107,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/bed185a0-ef82-11e9-b38a-2db3ee640e88.json b/dashboards/dashboards/bed185a0-ef82-11e9-b38a-2db3ee640e88.json index dd5769ddf..63deb6d9e 100644 --- a/dashboards/dashboards/bed185a0-ef82-11e9-b38a-2db3ee640e88.json +++ b/dashboards/dashboards/bed185a0-ef82-11e9-b38a-2db3ee640e88.json @@ -72,7 +72,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48.json b/dashboards/dashboards/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48.json index 8e5959eb6..42d728be7 100644 --- a/dashboards/dashboards/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48.json +++ b/dashboards/dashboards/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48.json @@ -82,7 +82,7 @@ "version": "WzY5MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2.json b/dashboards/dashboards/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2.json index 2f57ade45..3b00555c6 100644 --- a/dashboards/dashboards/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2.json +++ b/dashboards/dashboards/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2.json @@ -77,7 +77,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/ca5799a0-56b5-11eb-b749-576de068f8ad.json b/dashboards/dashboards/ca5799a0-56b5-11eb-b749-576de068f8ad.json index 325f2f766..81c7de66f 100644 --- a/dashboards/dashboards/ca5799a0-56b5-11eb-b749-576de068f8ad.json +++ b/dashboards/dashboards/ca5799a0-56b5-11eb-b749-576de068f8ad.json @@ -92,7 +92,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/caef3ade-d289-4d05-a511-149f3e97f238.json b/dashboards/dashboards/caef3ade-d289-4d05-a511-149f3e97f238.json index 893de0e74..d31063c44 100644 --- a/dashboards/dashboards/caef3ade-d289-4d05-a511-149f3e97f238.json +++ b/dashboards/dashboards/caef3ade-d289-4d05-a511-149f3e97f238.json @@ -102,7 +102,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/d2dd0180-06b1-11ec-8c6b-353266ade330.json b/dashboards/dashboards/d2dd0180-06b1-11ec-8c6b-353266ade330.json index ebd3730a2..093afa901 100644 --- a/dashboards/dashboards/d2dd0180-06b1-11ec-8c6b-353266ade330.json +++ b/dashboards/dashboards/d2dd0180-06b1-11ec-8c6b-353266ade330.json @@ -112,7 +112,7 @@ "version": "WzczOSwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/d41fe630-3f98-11e9-a58e-8bdedb0915e8.json b/dashboards/dashboards/d41fe630-3f98-11e9-a58e-8bdedb0915e8.json index e7cea00e1..4ab0a7d62 100644 --- a/dashboards/dashboards/d41fe630-3f98-11e9-a58e-8bdedb0915e8.json +++ b/dashboards/dashboards/d41fe630-3f98-11e9-a58e-8bdedb0915e8.json @@ -57,7 +57,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/d4fd6afd-15cb-42bf-8a25-03dd8e59b327.json b/dashboards/dashboards/d4fd6afd-15cb-42bf-8a25-03dd8e59b327.json index 9dd79bbf8..b70dd3203 100644 --- a/dashboards/dashboards/d4fd6afd-15cb-42bf-8a25-03dd8e59b327.json +++ b/dashboards/dashboards/d4fd6afd-15cb-42bf-8a25-03dd8e59b327.json @@ -57,7 +57,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/dd87edd0-796a-11ec-9ce6-b395c1ff58f4.json b/dashboards/dashboards/dd87edd0-796a-11ec-9ce6-b395c1ff58f4.json index fa06a8a2b..233689446 100644 --- a/dashboards/dashboards/dd87edd0-796a-11ec-9ce6-b395c1ff58f4.json +++ b/dashboards/dashboards/dd87edd0-796a-11ec-9ce6-b395c1ff58f4.json @@ -107,7 +107,7 @@ "version": "WzgzOCwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/e09a4b86-29b5-4256-bb3b-802ac9f90404.json b/dashboards/dashboards/e09a4b86-29b5-4256-bb3b-802ac9f90404.json index 7c5b69a92..b90546042 100644 --- a/dashboards/dashboards/e09a4b86-29b5-4256-bb3b-802ac9f90404.json +++ b/dashboards/dashboards/e09a4b86-29b5-4256-bb3b-802ac9f90404.json @@ -57,7 +57,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/e76d05c0-eb9f-11e9-a384-0fcf32210194.json b/dashboards/dashboards/e76d05c0-eb9f-11e9-a384-0fcf32210194.json index 217fef8cb..8e9e8ec31 100644 --- a/dashboards/dashboards/e76d05c0-eb9f-11e9-a384-0fcf32210194.json +++ b/dashboards/dashboards/e76d05c0-eb9f-11e9-a384-0fcf32210194.json @@ -87,7 +87,7 @@ "version": "WzgwMSwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/ed8a6640-3f98-11e9-a58e-8bdedb0915e8.json b/dashboards/dashboards/ed8a6640-3f98-11e9-a58e-8bdedb0915e8.json index 97b0c5cbe..5aa84af44 100644 --- a/dashboards/dashboards/ed8a6640-3f98-11e9-a58e-8bdedb0915e8.json +++ b/dashboards/dashboards/ed8a6640-3f98-11e9-a58e-8bdedb0915e8.json @@ -47,7 +47,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/f1f09567-fc7f-450b-a341-19d2f2bb468b.json b/dashboards/dashboards/f1f09567-fc7f-450b-a341-19d2f2bb468b.json index 57ff8d1c0..d2eb7eb48 100644 --- a/dashboards/dashboards/f1f09567-fc7f-450b-a341-19d2f2bb468b.json +++ b/dashboards/dashboards/f1f09567-fc7f-450b-a341-19d2f2bb468b.json @@ -137,7 +137,7 @@ "version": "Wzc4NCwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/f394057d-1b16-4174-b994-7045f423a416.json b/dashboards/dashboards/f394057d-1b16-4174-b994-7045f423a416.json index 6f551e215..109b52b49 100644 --- a/dashboards/dashboards/f394057d-1b16-4174-b994-7045f423a416.json +++ b/dashboards/dashboards/f394057d-1b16-4174-b994-7045f423a416.json @@ -57,7 +57,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/f77bf097-18a8-465c-b634-eb2acc7a4f26.json b/dashboards/dashboards/f77bf097-18a8-465c-b634-eb2acc7a4f26.json index 253051813..d426b9281 100644 --- a/dashboards/dashboards/f77bf097-18a8-465c-b634-eb2acc7a4f26.json +++ b/dashboards/dashboards/f77bf097-18a8-465c-b634-eb2acc7a4f26.json @@ -102,7 +102,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/fa141950-ef89-11e9-b38a-2db3ee640e88.json b/dashboards/dashboards/fa141950-ef89-11e9-b38a-2db3ee640e88.json index 87a04672e..e02b32e9a 100644 --- a/dashboards/dashboards/fa141950-ef89-11e9-b38a-2db3ee640e88.json +++ b/dashboards/dashboards/fa141950-ef89-11e9-b38a-2db3ee640e88.json @@ -77,7 +77,7 @@ "version": "Wzg3MiwxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/fa477130-2b8a-11ec-a9f2-3911c8571bfd.json b/dashboards/dashboards/fa477130-2b8a-11ec-a9f2-3911c8571bfd.json index 607dac9e4..d4db6934a 100644 --- a/dashboards/dashboards/fa477130-2b8a-11ec-a9f2-3911c8571bfd.json +++ b/dashboards/dashboards/fa477130-2b8a-11ec-a9f2-3911c8571bfd.json @@ -102,7 +102,7 @@ "version": "WzkzNywxXQ==", "attributes": { "title": "Network Logs", - "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", + "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/logstash/pipelines/zeek/12_zeek_mutate.conf b/logstash/pipelines/zeek/12_zeek_mutate.conf index 63aa8171e..de4ed41e0 100644 --- a/logstash/pipelines/zeek/12_zeek_mutate.conf +++ b/logstash/pipelines/zeek/12_zeek_mutate.conf @@ -2213,6 +2213,15 @@ filter { split => { "[zeek][synchrophasor_data_detail][est_rectangular_real]" => "," } } + if ([zeek][synchrophasor][frame_size]) { + mutate { id => "mutate_add_field_totDataBytes_zeek_synchrophasor_frame_size" + add_field => { "[totDataBytes]" => "%{[zeek][synchrophasor][frame_size]}" } } + } + if ([zeek][synchrophasor][frame_size_tot]) { + mutate { id => "mutate_add_field_totDataBytes_zeek_synchrophasor_frame_size_tot" + add_field => { "[totDataBytes]" => "%{[zeek][synchrophasor][frame_size_tot]}" } } + } + } else if ([log_source] == "tds_rpc") { ############################################################################################################################# # tds_rpc.log specific logic From 25dbbb1b6ea982145e90edb407a21fc9ceb99792 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 26 Apr 2023 13:48:43 -0600 Subject: [PATCH 219/239] idaholab/Malcolm#190, a little normalization and dashboards --- arkime/etc/config.ini | 3 +- arkime/wise/source.zeeklogs.js | 1 - .../2cc56240-e460-11ed-a9d5-9f591c284cb4.json | 328 ++++++++++++++++-- .../composable/component/zeek_ot.json | 1 - logstash/pipelines/zeek/12_zeek_mutate.conf | 1 + 5 files changed, 306 insertions(+), 28 deletions(-) diff --git a/arkime/etc/config.ini b/arkime/etc/config.ini index 5a65d052d..cd480d289 100644 --- a/arkime/etc/config.ini +++ b/arkime/etc/config.ini @@ -1791,7 +1791,6 @@ zeek.synchrophasor_cmd.extframe=db:zeek.synchrophasor_cmd.extframe;group:zeek_sy zeek.synchrophasor_cfg.cont_idx=db:zeek.synchrophasor_cfg.cont_idx;group:zeek_synchrophasor;kind:integer;friendly:cont_idx;help:cont_idx zeek.synchrophasor_cfg.pmu_count_expected=db:zeek.synchrophasor_cfg.pmu_count_expected;group:zeek_synchrophasor;kind:integer;friendly:pmu_count_expected;help:pmu_count_expected zeek.synchrophasor_cfg.pmu_count_actual=db:zeek.synchrophasor_cfg.pmu_count_actual;group:zeek_synchrophasor;kind:integer;friendly:pmu_count_actual;help:pmu_count_actual -zeek.synchrophasor_cfg.data_rate=db:zeek.synchrophasor_cfg.data_rate;group:zeek_synchrophasor;kind:integer;friendly:data_rate;help:data_rate zeek.synchrophasor_cfg.cfg_frame_id=db:zeek.synchrophasor_cfg.cfg_frame_id;group:zeek_synchrophasor;kind:termfield;friendly:cfg_frame_id;help:cfg_frame_id # synchrophasor_cfg_detail.log @@ -2634,7 +2633,7 @@ o_zeek_stun=require:zeek.stun;title:Zeek stun.log;fields:zeek.stun.trans_id,zeek o_zeek_stun_nat=require:zeek.stun_nat;title:Zeek stun_nat.log;fields:zeek.stun_nat.wan_addr,zeek.stun_nat.wan_port,zeek.stun_nat.lan_addr o_zeek_synchrophasor=require:zeek.synchrophasor;title:Zeek synchrophasor.log;fields:zeek.synchrophasor.version,zeek.synchrophasor.header_time_stamp,zeek.synchrophasor.data_stream_id,zeek.synchrophasor.history,zeek.synchrophasor.frame_type,zeek.synchrophasor.frame_size,zeek.synchrophasor.frame_size_min,zeek.synchrophasor.frame_size_max,zeek.synchrophasor.frame_size_tot,zeek.synchrophasor.data_frame_count,zeek.synchrophasor.data_rate o_zeek_synchrophasor_cmd=require:zeek.synchrophasor_cmd;title:Zeek synchrophasor_cmd.log;fields:zeek.synchrophasor_cmd.command,zeek.synchrophasor_cmd.extframe -o_zeek_synchrophasor_cfg=require:zeek.synchrophasor_cfg;title:Zeek synchrophasor_cfg.log;fields:zeek.synchrophasor_cfg.cont_idx,zeek.synchrophasor_cfg.pmu_count_expected,zeek.synchrophasor_cfg.pmu_count_actual,zeek.synchrophasor_cfg.data_rate,zeek.synchrophasor_cfg.cfg_frame_id +o_zeek_synchrophasor_cfg=require:zeek.synchrophasor_cfg;title:Zeek synchrophasor_cfg.log;fields:zeek.synchrophasor_cfg.cont_idx,zeek.synchrophasor_cfg.pmu_count_expected,zeek.synchrophasor_cfg.pmu_count_actual,zeek.synchrophasor_cfg.cfg_frame_id o_zeek_synchrophasor_cfg_detail=require:zeek.synchrophasor_cfg_detail;title:Zeek synchrophasor_cfg_detail.log;fields:zeek.synchrophasor_cfg_detail.cfg_frame_id,zeek.synchrophasor_cfg_detail.pmu_idx,zeek.synchrophasor_cfg_detail.svc_class,zeek.synchrophasor_cfg_detail.station_name,zeek.synchrophasor_cfg_detail.data_source_id,zeek.synchrophasor_cfg_detail.global_pmuid,zeek.synchrophasor_cfg_detail.phasor_shape,zeek.synchrophasor_cfg_detail.phasor_format,zeek.synchrophasor_cfg_detail.analog_format,zeek.synchrophasor_cfg_detail.freq_format,zeek.synchrophasor_cfg_detail.phnmr,zeek.synchrophasor_cfg_detail.annmr,zeek.synchrophasor_cfg_detail.dgnmr,zeek.synchrophasor_cfg_detail.phnam,zeek.synchrophasor_cfg_detail.annam,zeek.synchrophasor_cfg_detail.dgnam,zeek.synchrophasor_cfg_detail.phasor_conv_phunit,zeek.synchrophasor_cfg_detail.phasor_conv_phvalue,zeek.synchrophasor_cfg_detail.phasor_conv_upsampled_interpolation,zeek.synchrophasor_cfg_detail.phasor_conv_upsampled_extrapolation,zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_reselection,zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_fir_filter,zeek.synchrophasor_cfg_detail.phasor_conv_downsampled_no_fir_filter,zeek.synchrophasor_cfg_detail.phasor_conv_filtered_without_changing_sampling,zeek.synchrophasor_cfg_detail.phasor_conv_calibration_mag_adj,zeek.synchrophasor_cfg_detail.phasor_conv_calibration_phas_adj,zeek.synchrophasor_cfg_detail.phasor_conv_rotation_phase_adj,zeek.synchrophasor_cfg_detail.phasor_conv_pseudo_phasor_val,zeek.synchrophasor_cfg_detail.phasor_conv_mod_appl,zeek.synchrophasor_cfg_detail.phasor_conv_phasor_component,zeek.synchrophasor_cfg_detail.phasor_conv_phasor_type,zeek.synchrophasor_cfg_detail.phasor_conv_user_def,zeek.synchrophasor_cfg_detail.phasor_conv_scale_factor,zeek.synchrophasor_cfg_detail.phasor_conv_angle_adj,zeek.synchrophasor_cfg_detail.analog_conv_analog_flags,zeek.synchrophasor_cfg_detail.analog_conv_user_defined_scaling,zeek.synchrophasor_cfg_detail.analog_conv_mag_scale,zeek.synchrophasor_cfg_detail.analog_conv_offset,zeek.synchrophasor_cfg_detail.digital_conv_normal_status_mask,zeek.synchrophasor_cfg_detail.digital_conv_valid_inputs_mask,zeek.synchrophasor_cfg_detail.pmu_lat,zeek.synchrophasor_cfg_detail.pmu_lon,zeek.synchrophasor_cfg_detail.pmu_elev,zeek.synchrophasor_cfg_detail.window,zeek.synchrophasor_cfg_detail.group_delay,zeek.synchrophasor_cfg_detail.fnom,zeek.synchrophasor_cfg_detail.cfgcnt o_zeek_synchrophasor_data=require:zeek.synchrophasor_data;title:Zeek synchrophasor_data.log;fields:zeek.synchrophasor_data.pmu_count_expected,zeek.synchrophasor_data.pmu_count_actual,zeek.synchrophasor_data.data_frame_id o_zeek_synchrophasor_data_detail=require:zeek.synchrophasor_data_detail;title:Zeek synchrophasor_data_detail.log;fields:zeek.synchrophasor_data_detail.data_frame_id,zeek.synchrophasor_data_detail.pmu_idx,zeek.synchrophasor_data_detail.trigger_reason,zeek.synchrophasor_data_detail.unlocked_time,zeek.synchrophasor_data_detail.pmu_time_quality,zeek.synchrophasor_data_detail.data_modified,zeek.synchrophasor_data_detail.config_change,zeek.synchrophasor_data_detail.pmu_trigger_pickup,zeek.synchrophasor_data_detail.data_sorting_type,zeek.synchrophasor_data_detail.pmu_sync_error,zeek.synchrophasor_data_detail.data_error_indicator,zeek.synchrophasor_data_detail.est_rectangular_real,zeek.synchrophasor_data_detail.est_rectangular_imaginary,zeek.synchrophasor_data_detail.est_polar_magnitude,zeek.synchrophasor_data_detail.est_polar_angle,zeek.synchrophasor_data_detail.freq_dev_mhz,zeek.synchrophasor_data_detail.rocof,zeek.synchrophasor_data_detail.analog_data,zeek.synchrophasor_data_detail.digital diff --git a/arkime/wise/source.zeeklogs.js b/arkime/wise/source.zeeklogs.js index 4805f75e8..14d0ac046 100644 --- a/arkime/wise/source.zeeklogs.js +++ b/arkime/wise/source.zeeklogs.js @@ -1998,7 +1998,6 @@ class MalcolmSource extends WISESource { "zeek.synchrophasor.version", "zeek.synchrophasor_cfg.cfg_frame_id", "zeek.synchrophasor_cfg.cont_idx", - "zeek.synchrophasor_cfg.data_rate", "zeek.synchrophasor_cfg.pmu_count_actual", "zeek.synchrophasor_cfg.pmu_count_expected", "zeek.synchrophasor_cfg_detail.analog_conv_analog_flags", diff --git a/dashboards/dashboards/2cc56240-e460-11ed-a9d5-9f591c284cb4.json b/dashboards/dashboards/2cc56240-e460-11ed-a9d5-9f591c284cb4.json index 2bd4e698f..121a4f698 100644 --- a/dashboards/dashboards/2cc56240-e460-11ed-a9d5-9f591c284cb4.json +++ b/dashboards/dashboards/2cc56240-e460-11ed-a9d5-9f591c284cb4.json @@ -7,13 +7,13 @@ "namespaces": [ "default" ], - "updated_at": "2023-04-26T18:41:21.569Z", - "version": "Wzk3MSwxXQ==", + "updated_at": "2023-04-26T19:48:24.081Z", + "version": "Wzk1MSwxXQ==", "attributes": { "title": "Synchrophasor", "hits": 0, "description": "Dashboard for the DNP3 Protocol", - "panelsJSON": "[{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":8,\"h\":28,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{},\"panelRefName\":\"panel_0\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":8,\"y\":0,\"w\":13,\"h\":18,\"i\":\"38d18152-c0f9-436b-a19d-ac76cfbc1bac\"},\"panelIndex\":\"38d18152-c0f9-436b-a19d-ac76cfbc1bac\",\"embeddableConfig\":{},\"panelRefName\":\"panel_1\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":21,\"y\":0,\"w\":27,\"h\":18,\"i\":\"b3c290e3-6d8f-4616-beaa-f5183a0d5208\"},\"panelIndex\":\"b3c290e3-6d8f-4616-beaa-f5183a0d5208\",\"embeddableConfig\":{},\"panelRefName\":\"panel_2\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":8,\"y\":18,\"w\":8,\"h\":19,\"i\":\"faf91692-201a-47c1-9439-62cb71e577de\"},\"panelIndex\":\"faf91692-201a-47c1-9439-62cb71e577de\",\"embeddableConfig\":{},\"panelRefName\":\"panel_3\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":16,\"y\":18,\"w\":10,\"h\":19,\"i\":\"3a54cfa0-dc19-49ad-b63f-8e570227587d\"},\"panelIndex\":\"3a54cfa0-dc19-49ad-b63f-8e570227587d\",\"embeddableConfig\":{},\"panelRefName\":\"panel_4\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":26,\"y\":18,\"w\":22,\"h\":19,\"i\":\"118707f0-37e5-4838-b58f-a9c75b37f021\"},\"panelIndex\":\"118707f0-37e5-4838-b58f-a9c75b37f021\",\"embeddableConfig\":{},\"panelRefName\":\"panel_5\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":28,\"w\":8,\"h\":9,\"i\":\"3033af99-af03-4def-b524-55bf5706e9e7\"},\"panelIndex\":\"3033af99-af03-4def-b524-55bf5706e9e7\",\"embeddableConfig\":{},\"panelRefName\":\"panel_6\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":37,\"w\":48,\"h\":24,\"i\":\"8afdbe78-d879-4285-b589-769e1f006ebf\"},\"panelIndex\":\"8afdbe78-d879-4285-b589-769e1f006ebf\",\"embeddableConfig\":{},\"panelRefName\":\"panel_7\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":61,\"w\":48,\"h\":22,\"i\":\"dba8956a-61cf-4d3a-a691-07a97072b51c\"},\"panelIndex\":\"dba8956a-61cf-4d3a-a691-07a97072b51c\",\"embeddableConfig\":{},\"panelRefName\":\"panel_8\"}]", + "panelsJSON": "[{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":8,\"h\":30,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{},\"panelRefName\":\"panel_0\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":8,\"y\":0,\"w\":13,\"h\":18,\"i\":\"38d18152-c0f9-436b-a19d-ac76cfbc1bac\"},\"panelIndex\":\"38d18152-c0f9-436b-a19d-ac76cfbc1bac\",\"embeddableConfig\":{},\"panelRefName\":\"panel_1\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":21,\"y\":0,\"w\":27,\"h\":18,\"i\":\"b3c290e3-6d8f-4616-beaa-f5183a0d5208\"},\"panelIndex\":\"b3c290e3-6d8f-4616-beaa-f5183a0d5208\",\"embeddableConfig\":{},\"panelRefName\":\"panel_2\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":8,\"y\":18,\"w\":8,\"h\":19,\"i\":\"faf91692-201a-47c1-9439-62cb71e577de\"},\"panelIndex\":\"faf91692-201a-47c1-9439-62cb71e577de\",\"embeddableConfig\":{},\"panelRefName\":\"panel_3\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":16,\"y\":18,\"w\":10,\"h\":19,\"i\":\"3a54cfa0-dc19-49ad-b63f-8e570227587d\"},\"panelIndex\":\"3a54cfa0-dc19-49ad-b63f-8e570227587d\",\"embeddableConfig\":{},\"panelRefName\":\"panel_4\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":26,\"y\":18,\"w\":10,\"h\":19,\"i\":\"118707f0-37e5-4838-b58f-a9c75b37f021\"},\"panelIndex\":\"118707f0-37e5-4838-b58f-a9c75b37f021\",\"embeddableConfig\":{\"vis\":{\"legendOpen\":false}},\"panelRefName\":\"panel_5\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":36,\"y\":18,\"w\":12,\"h\":19,\"i\":\"3a76fd84-afcf-4c18-b378-c2434c1c2ce3\"},\"panelIndex\":\"3a76fd84-afcf-4c18-b378-c2434c1c2ce3\",\"embeddableConfig\":{},\"panelRefName\":\"panel_6\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":30,\"w\":8,\"h\":13,\"i\":\"3033af99-af03-4def-b524-55bf5706e9e7\"},\"panelIndex\":\"3033af99-af03-4def-b524-55bf5706e9e7\",\"embeddableConfig\":{},\"panelRefName\":\"panel_7\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":8,\"y\":37,\"w\":10,\"h\":19,\"i\":\"063f5459-fca9-4f55-b0a8-5fc15d59e1e2\"},\"panelIndex\":\"063f5459-fca9-4f55-b0a8-5fc15d59e1e2\",\"embeddableConfig\":{},\"panelRefName\":\"panel_8\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":18,\"y\":37,\"w\":10,\"h\":19,\"i\":\"668fcd2b-fcb1-468e-89af-d10a89e66992\"},\"panelIndex\":\"668fcd2b-fcb1-468e-89af-d10a89e66992\",\"embeddableConfig\":{},\"panelRefName\":\"panel_9\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":28,\"y\":37,\"w\":10,\"h\":19,\"i\":\"7ad5b38e-aa33-45b2-83b5-650ea45778f5\"},\"panelIndex\":\"7ad5b38e-aa33-45b2-83b5-650ea45778f5\",\"embeddableConfig\":{},\"panelRefName\":\"panel_10\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":38,\"y\":37,\"w\":10,\"h\":19,\"i\":\"b94ddaeb-f509-401c-8f14-ae0d4c363dc8\"},\"panelIndex\":\"b94ddaeb-f509-401c-8f14-ae0d4c363dc8\",\"embeddableConfig\":{},\"panelRefName\":\"panel_11\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":43,\"w\":8,\"h\":13,\"i\":\"8fd25887-38e9-4cb7-8c85-f659f2337b65\"},\"panelIndex\":\"8fd25887-38e9-4cb7-8c85-f659f2337b65\",\"embeddableConfig\":{},\"panelRefName\":\"panel_12\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":56,\"w\":48,\"h\":18,\"i\":\"2e030d08-7422-4420-9c32-515ea641f5ec\"},\"panelIndex\":\"2e030d08-7422-4420-9c32-515ea641f5ec\",\"embeddableConfig\":{},\"panelRefName\":\"panel_13\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":74,\"w\":48,\"h\":18,\"i\":\"44849c88-bbe0-4907-94a6-4a930b3bf61c\"},\"panelIndex\":\"44849c88-bbe0-4907-94a6-4a930b3bf61c\",\"embeddableConfig\":{},\"panelRefName\":\"panel_14\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":92,\"w\":48,\"h\":24,\"i\":\"8afdbe78-d879-4285-b589-769e1f006ebf\"},\"panelIndex\":\"8afdbe78-d879-4285-b589-769e1f006ebf\",\"embeddableConfig\":{},\"panelRefName\":\"panel_15\"},{\"version\":\"2.6.0\",\"gridData\":{\"x\":0,\"y\":116,\"w\":48,\"h\":22,\"i\":\"dba8956a-61cf-4d3a-a691-07a97072b51c\"},\"panelIndex\":\"dba8956a-61cf-4d3a-a691-07a97072b51c\",\"embeddableConfig\":{},\"panelRefName\":\"panel_16\"}]", "optionsJSON": "{\"useMargins\":true}", "version": 1, "timeRestore": false, @@ -55,15 +55,55 @@ { "name": "panel_6", "type": "visualization", - "id": "9ba20280-e461-11ed-a9d5-9f591c284cb4" + "id": "28c384e0-e467-11ed-be6f-077da9a43874" }, { "name": "panel_7", + "type": "visualization", + "id": "9ba20280-e461-11ed-a9d5-9f591c284cb4" + }, + { + "name": "panel_8", + "type": "visualization", + "id": "b2f24660-e467-11ed-be6f-077da9a43874" + }, + { + "name": "panel_9", + "type": "visualization", + "id": "6fe5f7e0-e467-11ed-be6f-077da9a43874" + }, + { + "name": "panel_10", + "type": "visualization", + "id": "8d694670-e465-11ed-be6f-077da9a43874" + }, + { + "name": "panel_11", + "type": "visualization", + "id": "fa71b860-e465-11ed-be6f-077da9a43874" + }, + { + "name": "panel_12", + "type": "visualization", + "id": "a7f2ae90-e466-11ed-be6f-077da9a43874" + }, + { + "name": "panel_13", + "type": "visualization", + "id": "9af335d0-e46a-11ed-be6f-077da9a43874" + }, + { + "name": "panel_14", + "type": "visualization", + "id": "6ed03030-e469-11ed-be6f-077da9a43874" + }, + { + "name": "panel_15", "type": "search", "id": "031207a0-e460-11ed-a9d5-9f591c284cb4" }, { - "name": "panel_8", + "name": "panel_16", "type": "search", "id": "53729e40-e45f-11ed-a9d5-9f591c284cb4" } @@ -78,8 +118,8 @@ "namespaces": [ "default" ], - "updated_at": "2023-04-26T17:45:20.809Z", - "version": "WzgzOCwxXQ==", + "updated_at": "2023-04-26T18:57:19.151Z", + "version": "Wzg0OSwxXQ==", "attributes": { "title": "Network Logs", "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Known Summary](/dashboards/app/dashboards#/view/89d1cc50-974c-11ed-bb6b-3fb06c879b11) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[Asset Interaction Analysis](/dashboards/app/dashboards#/view/677ee170-809e-11ed-8d5b-07069f823b6f) \\n[↪ NetBox](/netbox/) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Synchrophasor](/dashboards/app/dashboards#/view/2cc56240-e460-11ed-a9d5-9f591c284cb4) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", @@ -101,8 +141,8 @@ "namespaces": [ "default" ], - "updated_at": "2023-04-26T18:32:30.574Z", - "version": "Wzk2MiwxXQ==", + "updated_at": "2023-04-26T18:56:23.870Z", + "version": "WzIwOSwxXQ==", "attributes": { "title": "Synchrophasor - Log Count", "visState": "{\"title\":\"Synchrophasor - Log Count\",\"type\":\"metric\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"event.dataset\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Log Type\"},\"schema\":\"group\"}],\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":36}}}}", @@ -131,8 +171,8 @@ "namespaces": [ "default" ], - "updated_at": "2023-04-26T18:34:17.788Z", - "version": "Wzk2MywxXQ==", + "updated_at": "2023-04-26T18:56:23.870Z", + "version": "WzIxMCwxXQ==", "attributes": { "title": "Synchrophasor - Log Count Over Time", "visState": "{\"title\":\"Synchrophasor - Log Count Over Time\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"params\":{\"field\":\"firstPacket\",\"timeRange\":{\"from\":\"now-15y\",\"to\":\"now\"},\"useNormalizedOpenSearchInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{},\"customLabel\":\"\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"square root\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"}}}", @@ -161,8 +201,8 @@ "namespaces": [ "default" ], - "updated_at": "2023-04-26T18:37:39.936Z", - "version": "Wzk2NywxXQ==", + "updated_at": "2023-04-26T18:56:23.870Z", + "version": "WzIxMSwxXQ==", "attributes": { "title": "Synchrophasor - Source", "visState": "{\"title\":\"Synchrophasor - Source\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"source.ip\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":250,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Source IP\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", @@ -191,8 +231,8 @@ "namespaces": [ "default" ], - "updated_at": "2023-04-26T18:37:19.904Z", - "version": "Wzk2NiwxXQ==", + "updated_at": "2023-04-26T18:56:23.870Z", + "version": "WzIxMiwxXQ==", "attributes": { "title": "Synchrophasor - Destination", "visState": "{\"title\":\"Synchrophasor - Destination\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"destination.ip\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":250,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Destination IP\"},\"schema\":\"bucket\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"destination.port\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Destination Port\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", @@ -221,12 +261,42 @@ "namespaces": [ "default" ], - "updated_at": "2023-04-26T18:40:58.179Z", - "version": "Wzk3MCwxXQ==", + "updated_at": "2023-04-26T19:08:46.643Z", + "version": "Wzk0MCwxXQ==", "attributes": { "title": "Synchrophasor - Action", "visState": "{\"title\":\"Synchrophasor - Action\",\"type\":\"horizontal_bar\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"event.action\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Action\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":200},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":75,\"filter\":true,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"}}}", - "uiStateJSON": "{}", + "uiStateJSON": "{\"vis\":{\"legendOpen\":false}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "031207a0-e460-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "28c384e0-e467-11ed-be6f-077da9a43874", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T19:18:43.502Z", + "version": "Wzk0NCwxXQ==", + "attributes": { + "title": "Synchrophasor - Frame Types", + "visState": "{\"title\":\"Synchrophasor - Frame Types\",\"type\":\"horizontal_bar\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"zeek.synchrophasor.frame_type\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":10,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Frame Type\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":200},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"square root\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":75,\"filter\":true,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"}}}", + "uiStateJSON": "{\"vis\":{\"legendOpen\":false}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { @@ -251,8 +321,8 @@ "namespaces": [ "default" ], - "updated_at": "2023-04-26T18:38:59.240Z", - "version": "Wzk2OCwxXQ==", + "updated_at": "2023-04-26T18:56:23.870Z", + "version": "WzIxNCwxXQ==", "attributes": { "title": "Synchrophasor - Transport", "visState": "{\"title\":\"Synchrophasor - Transport\",\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"network.transport\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"Unknown\",\"customLabel\":\"Transport\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":true,\"values\":true,\"last_level\":true,\"truncate\":100}}}", @@ -275,14 +345,224 @@ "visualization": "7.10.0" } }, + { + "id": "b2f24660-e467-11ed-be6f-077da9a43874", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T19:22:35.332Z", + "version": "Wzk0NiwxXQ==", + "attributes": { + "title": "Synchrophasor - Stations", + "visState": "{\"title\":\"Synchrophasor - Stations\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"zeek.synchrophasor_cfg_detail.station_name\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":250,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Station\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "031207a0-e460-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "6fe5f7e0-e467-11ed-be6f-077da9a43874", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T19:20:42.844Z", + "version": "Wzk0NSwxXQ==", + "attributes": { + "title": "Synchrophasor - Phasors", + "visState": "{\"title\":\"Synchrophasor - Phasors\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"zeek.synchrophasor_cfg_detail.phnam\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":250,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Phasor\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "031207a0-e460-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "8d694670-e465-11ed-be6f-077da9a43874", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T19:07:13.365Z", + "version": "WzkzNywxXQ==", + "attributes": { + "title": "Synchrophasor - Analog Channels", + "visState": "{\"title\":\"Synchrophasor - Analog Channels\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"zeek.synchrophasor_cfg_detail.annam\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":250,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Analog Channel\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "031207a0-e460-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "fa71b860-e465-11ed-be6f-077da9a43874", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T19:10:16.291Z", + "version": "Wzk0MSwxXQ==", + "attributes": { + "title": "Synchrophasor - Digital Channels", + "visState": "{\"title\":\"Synchrophasor - Digital Channels\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"zeek.synchrophasor_cfg_detail.dgnam\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":250,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Digital Channel\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "031207a0-e460-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "a7f2ae90-e466-11ed-be6f-077da9a43874", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T19:15:07.383Z", + "version": "Wzk0MiwxXQ==", + "attributes": { + "title": "Synchrophasor - Data Modified", + "visState": "{\"title\":\"Synchrophasor - Data Modified\",\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"zeek.synchrophasor_data_detail.data_modified\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Data Modified\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":true,\"values\":true,\"last_level\":true,\"truncate\":100}}}", + "uiStateJSON": "{\"vis\":{\"legendOpen\":false}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "031207a0-e460-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "9af335d0-e46a-11ed-be6f-077da9a43874", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T19:43:23.563Z", + "version": "Wzk1MCwxXQ==", + "attributes": { + "title": "Synchrophasor - Rate of Change of Frequency Over Time", + "visState": "{\"title\":\"Synchrophasor - Rate of Change of Frequency Over Time\",\"type\":\"line\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"params\":{\"field\":\"zeek.synchrophasor_data_detail.rocof\",\"customLabel\":\"ROCOF (Hz/sec * 100)\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"params\":{\"field\":\"firstPacket\",\"timeRange\":{\"from\":\"now-15y\",\"to\":\"now\"},\"useNormalizedOpenSearchInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}},\"schema\":\"segment\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"count\",\"params\":{\"customLabel\":\"Count\"},\"schema\":\"radius\"}],\"params\":{\"type\":\"line\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"square root\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"ROCOF (Hz/sec * 100)\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"line\",\"mode\":\"stacked\",\"data\":{\"label\":\"ROCOF (Hz/sec * 100)\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":false,\"lineWidth\":2,\"interpolate\":\"linear\",\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"radiusRatio\":25}}", + "uiStateJSON": "{\"vis\":{\"legendOpen\":false}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "031207a0-e460-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, + { + "id": "6ed03030-e469-11ed-be6f-077da9a43874", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2023-04-26T19:35:00.017Z", + "version": "Wzk0OCwxXQ==", + "attributes": { + "title": "Synchrophasor - Frequency Deviation From Nominal Over Time", + "visState": "{\"title\":\"Synchrophasor - Frequency Deviation From Nominal Over Time\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"params\":{\"field\":\"zeek.synchrophasor_data_detail.freq_dev_mhz\",\"customLabel\":\"Maximum frequency deviation from nominal (mHz)\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"params\":{\"field\":\"firstPacket\",\"timeRange\":{\"from\":\"now-15y\",\"to\":\"now\"},\"useNormalizedOpenSearchInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"log\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Maximum frequency deviation from nominal (mHz)\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Maximum frequency deviation from nominal (mHz)\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"}}}", + "uiStateJSON": "{\"vis\":{\"legendOpen\":false}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "031207a0-e460-11ed-a9d5-9f591c284cb4" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, { "id": "031207a0-e460-11ed-a9d5-9f591c284cb4", "type": "search", "namespaces": [ "default" ], - "updated_at": "2023-04-26T18:27:33.784Z", - "version": "Wzk1OCwxXQ==", + "updated_at": "2023-04-26T18:56:23.870Z", + "version": "WzIxNSwxXQ==", "attributes": { "title": "Synchrophasor and Related - Logs", "description": "", @@ -321,8 +601,8 @@ "namespaces": [ "default" ], - "updated_at": "2023-04-26T18:22:39.138Z", - "version": "Wzk0NCwxXQ==", + "updated_at": "2023-04-26T18:56:23.870Z", + "version": "WzIxNiwxXQ==", "attributes": { "title": "Synchrophasor - Logs", "description": "", diff --git a/dashboards/templates/composable/component/zeek_ot.json b/dashboards/templates/composable/component/zeek_ot.json index 5850093f6..255037c43 100644 --- a/dashboards/templates/composable/component/zeek_ot.json +++ b/dashboards/templates/composable/component/zeek_ot.json @@ -723,7 +723,6 @@ "zeek.synchrophasor.version": { "type": "keyword" }, "zeek.synchrophasor_cfg.cfg_frame_id": { "type": "keyword" }, "zeek.synchrophasor_cfg.cont_idx": { "type": "long" }, - "zeek.synchrophasor_cfg.data_rate": { "type": "long" }, "zeek.synchrophasor_cfg.pmu_count_actual": { "type": "long" }, "zeek.synchrophasor_cfg.pmu_count_expected": { "type": "long" }, "zeek.synchrophasor_cfg_detail.analog_conv_analog_flags": { "type": "long" }, diff --git a/logstash/pipelines/zeek/12_zeek_mutate.conf b/logstash/pipelines/zeek/12_zeek_mutate.conf index de4ed41e0..7e56a04e7 100644 --- a/logstash/pipelines/zeek/12_zeek_mutate.conf +++ b/logstash/pipelines/zeek/12_zeek_mutate.conf @@ -2157,6 +2157,7 @@ filter { mutate { id => "mutate_rename_synchrophasor_fields" + rename => { "[zeek][synchrophasor_cfg][data_rate]" => "[zeek][synchrophasor][data_rate]" } rename => { "[zeek][synchrophasor_cfg][frame_size]" => "[zeek][synchrophasor][frame_size]" } rename => { "[zeek][synchrophasor_cfg][frame_type]" => "[zeek][synchrophasor][frame_type]" } rename => { "[zeek][synchrophasor_cfg][header_time_stamp]" => "[zeek][synchrophasor][header_time_stamp]" } From c0a13cfa7d0da5236fbdc4ffa898de2e90db104c Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 26 Apr 2023 15:02:01 -0600 Subject: [PATCH 220/239] idaholab/Malcolm#190, allow overriding synchrophasor ports --- .../usr/local/etc/zeek/local.zeek | 27 ++++++++++++++++++- zeek/config/local.zeek | 27 ++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/sensor-iso/config/includes.chroot/usr/local/etc/zeek/local.zeek b/sensor-iso/config/includes.chroot/usr/local/etc/zeek/local.zeek index 0dcef400b..67b969077 100644 --- a/sensor-iso/config/includes.chroot/usr/local/etc/zeek/local.zeek +++ b/sensor-iso/config/includes.chroot/usr/local/etc/zeek/local.zeek @@ -10,6 +10,7 @@ global disable_ssl_validate_certs = (getenv("ZEEK_DISABLE_SSL_VALIDATE_CERTS") = global disable_track_all_assets = (getenv("ZEEK_DISABLE_TRACK_ALL_ASSETS") == "") ? F : T; global disable_best_guess_ics = (getenv("ZEEK_DISABLE_BEST_GUESS_ICS") == "") ? F : T; global synchrophasor_detailed = (getenv("ZEEK_SYNCHROPHASOR_DETAILED") == "") ? F : T; +global synchrophasor_ports_str = getenv("ZEEK_SYNCHROPHASOR_PORTS"); global disable_spicy_dhcp = (getenv("ZEEK_DISABLE_SPICY_DHCP") == "") ? F : T; global disable_spicy_dns = (getenv("ZEEK_DISABLE_SPICY_DNS") == "") ? F : T; @@ -63,7 +64,6 @@ redef ignore_checksums = T; @load policy/protocols/conn/vlan-logging @load policy/protocols/conn/mac-logging @load policy/protocols/modbus/known-masters-slaves -@load policy/protocols/mqtt @load ./login.zeek @if (!disable_best_guess_ics) @@ -116,6 +116,31 @@ event zeek_init() &priority=-5 { if (disable_spicy_wireguard) { Spicy::disable_protocol_analyzer(Analyzer::ANALYZER_SPICY_WIREGUARD); } + + # register additional ports for Analyzer::ANALYZER_SPICY_SYNCHROPHASOR_... + if (synchrophasor_ports_str != "") { + local synchrophasor_ports = split_string(synchrophasor_ports_str, /,/); + if (|synchrophasor_ports| > 0) { + local synch_ports_tcp: set[port] = {}; + local synch_ports_udp: set[port] = {}; + for (synch_port_idx in synchrophasor_ports) { + local synch_port = to_port(synchrophasor_ports[synch_port_idx]); + local synch_prot = get_port_transport_proto(synch_port); + if (synch_prot == tcp) { + add synch_ports_tcp[synch_port]; + } else if (synch_prot == udp) { + add synch_ports_udp[synch_port]; + } + } + if (|synch_ports_tcp| > 0) { + Analyzer::register_for_ports(Analyzer::ANALYZER_SPICY_SYNCHROPHASOR_TCP, synch_ports_tcp); + } + if (|synch_ports_udp| > 0) { + Analyzer::register_for_ports(Analyzer::ANALYZER_SPICY_SYNCHROPHASOR_UDP, synch_ports_udp); + } + } + } + } @if (!disable_log_passwords) diff --git a/zeek/config/local.zeek b/zeek/config/local.zeek index 48ae031cf..d5d6f49c2 100644 --- a/zeek/config/local.zeek +++ b/zeek/config/local.zeek @@ -10,6 +10,7 @@ global disable_ssl_validate_certs = (getenv("ZEEK_DISABLE_SSL_VALIDATE_CERTS") = global disable_track_all_assets = (getenv("ZEEK_DISABLE_TRACK_ALL_ASSETS") == "") ? F : T; global disable_best_guess_ics = (getenv("ZEEK_DISABLE_BEST_GUESS_ICS") == "") ? F : T; global synchrophasor_detailed = (getenv("ZEEK_SYNCHROPHASOR_DETAILED") == "") ? F : T; +global synchrophasor_ports_str = getenv("ZEEK_SYNCHROPHASOR_PORTS"); global disable_spicy_dhcp = (getenv("ZEEK_DISABLE_SPICY_DHCP") == "") ? F : T; global disable_spicy_dns = (getenv("ZEEK_DISABLE_SPICY_DNS") == "") ? F : T; @@ -63,7 +64,6 @@ redef ignore_checksums = T; @load policy/protocols/conn/vlan-logging @load policy/protocols/conn/mac-logging @load policy/protocols/modbus/known-masters-slaves -@load policy/protocols/mqtt @load ./login.zeek @if (!disable_best_guess_ics) @@ -116,6 +116,31 @@ event zeek_init() &priority=-5 { if (disable_spicy_wireguard) { Spicy::disable_protocol_analyzer(Analyzer::ANALYZER_SPICY_WIREGUARD); } + + # register additional ports for Analyzer::ANALYZER_SPICY_SYNCHROPHASOR_... + if (synchrophasor_ports_str != "") { + local synchrophasor_ports = split_string(synchrophasor_ports_str, /,/); + if (|synchrophasor_ports| > 0) { + local synch_ports_tcp: set[port] = {}; + local synch_ports_udp: set[port] = {}; + for (synch_port_idx in synchrophasor_ports) { + local synch_port = to_port(synchrophasor_ports[synch_port_idx]); + local synch_prot = get_port_transport_proto(synch_port); + if (synch_prot == tcp) { + add synch_ports_tcp[synch_port]; + } else if (synch_prot == udp) { + add synch_ports_udp[synch_port]; + } + } + if (|synch_ports_tcp| > 0) { + Analyzer::register_for_ports(Analyzer::ANALYZER_SPICY_SYNCHROPHASOR_TCP, synch_ports_tcp); + } + if (|synch_ports_udp| > 0) { + Analyzer::register_for_ports(Analyzer::ANALYZER_SPICY_SYNCHROPHASOR_UDP, synch_ports_udp); + } + } + } + } @if (!disable_log_passwords) From b789f0fad4bf2325151a213efbba4c808ead6694 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 26 Apr 2023 15:23:55 -0600 Subject: [PATCH 221/239] fix version in kubernetes manifests --- kubernetes/02-opensearch.yml | 2 +- kubernetes/03-dashboards.yml | 2 +- kubernetes/04-upload.yml | 2 +- kubernetes/05-pcap-monitor.yml | 2 +- kubernetes/06-arkime.yml | 2 +- kubernetes/07-api.yml | 2 +- kubernetes/08-dashboards-helper.yml | 2 +- kubernetes/09-zeek.yml | 2 +- kubernetes/10-suricata.yml | 2 +- kubernetes/11-file-monitor.yml | 2 +- kubernetes/12-filebeat.yml | 2 +- kubernetes/13-logstash.yml | 2 +- kubernetes/15-netbox-redis.yml | 2 +- kubernetes/16-netbox-redis-cache.yml | 2 +- kubernetes/17-netbox-postgres.yml | 2 +- kubernetes/18-netbox.yml | 2 +- kubernetes/19-htadmin.yml | 2 +- kubernetes/20-pcap-capture.yml | 2 +- kubernetes/21-zeek-live.yml | 2 +- kubernetes/22-suricata-live.yml | 2 +- kubernetes/23-freq.yml | 2 +- kubernetes/99-nginx-proxy.yml | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index 85d7db527..16899ed91 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: opensearch-container - image: ghcr.io/idaholab/malcolm/opensearch:kubernetes + image: ghcr.io/idaholab/malcolm/opensearch:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/03-dashboards.yml b/kubernetes/03-dashboards.yml index bc513a9ee..4f62d5fc7 100644 --- a/kubernetes/03-dashboards.yml +++ b/kubernetes/03-dashboards.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: dashboards-container - image: ghcr.io/idaholab/malcolm/dashboards:kubernetes + image: ghcr.io/idaholab/malcolm/dashboards:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index a1297cb04..1c15e0d3c 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -34,7 +34,7 @@ spec: spec: containers: - name: upload-container - image: ghcr.io/idaholab/malcolm/file-upload:kubernetes + image: ghcr.io/idaholab/malcolm/file-upload:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml index 4ff27b76d..e19938fc4 100644 --- a/kubernetes/05-pcap-monitor.yml +++ b/kubernetes/05-pcap-monitor.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: pcap-monitor-container - image: ghcr.io/idaholab/malcolm/pcap-monitor:kubernetes + image: ghcr.io/idaholab/malcolm/pcap-monitor:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index d5e7ac59c..cc373ff35 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: arkime-container - image: ghcr.io/idaholab/malcolm/arkime:kubernetes + image: ghcr.io/idaholab/malcolm/arkime:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/07-api.yml b/kubernetes/07-api.yml index d29939989..42cf3c3e1 100644 --- a/kubernetes/07-api.yml +++ b/kubernetes/07-api.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: api-container - image: ghcr.io/idaholab/malcolm/api:kubernetes + image: ghcr.io/idaholab/malcolm/api:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/08-dashboards-helper.yml b/kubernetes/08-dashboards-helper.yml index 44832b52d..4ce938b93 100644 --- a/kubernetes/08-dashboards-helper.yml +++ b/kubernetes/08-dashboards-helper.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: dashboards-helper-container - image: ghcr.io/idaholab/malcolm/dashboards-helper:kubernetes + image: ghcr.io/idaholab/malcolm/dashboards-helper:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/09-zeek.yml b/kubernetes/09-zeek.yml index 094e299b5..c99d3e21b 100644 --- a/kubernetes/09-zeek.yml +++ b/kubernetes/09-zeek.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: zeek-offline-container - image: ghcr.io/idaholab/malcolm/zeek:kubernetes + image: ghcr.io/idaholab/malcolm/zeek:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/10-suricata.yml b/kubernetes/10-suricata.yml index e5846a3a0..40024e812 100644 --- a/kubernetes/10-suricata.yml +++ b/kubernetes/10-suricata.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: suricata-offline-container - image: ghcr.io/idaholab/malcolm/suricata:kubernetes + image: ghcr.io/idaholab/malcolm/suricata:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/11-file-monitor.yml b/kubernetes/11-file-monitor.yml index d6d225241..9606cc927 100644 --- a/kubernetes/11-file-monitor.yml +++ b/kubernetes/11-file-monitor.yml @@ -33,7 +33,7 @@ spec: spec: containers: - name: file-monitor-container - image: ghcr.io/idaholab/malcolm/file-monitor:kubernetes + image: ghcr.io/idaholab/malcolm/file-monitor:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml index 31c6c9e9c..74447c240 100644 --- a/kubernetes/12-filebeat.yml +++ b/kubernetes/12-filebeat.yml @@ -31,7 +31,7 @@ spec: spec: containers: - name: filebeat-container - image: ghcr.io/idaholab/malcolm/filebeat-oss:kubernetes + image: ghcr.io/idaholab/malcolm/filebeat-oss:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index 36636094e..255eb4d78 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -47,7 +47,7 @@ spec: # topologyKey: "kubernetes.io/hostname" containers: - name: logstash-container - image: ghcr.io/idaholab/malcolm/logstash-oss:kubernetes + image: ghcr.io/idaholab/malcolm/logstash-oss:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index afc71ade3..173005739 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: netbox-redis-container - image: ghcr.io/idaholab/malcolm/redis:kubernetes + image: ghcr.io/idaholab/malcolm/redis:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index 48dabb21e..31e5b21b3 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: netbox-redis-cache-container - image: ghcr.io/idaholab/malcolm/redis:kubernetes + image: ghcr.io/idaholab/malcolm/redis:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index 1eb499c81..e719d1f5a 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: netbox-postgres-container - image: ghcr.io/idaholab/malcolm/postgresql:kubernetes + image: ghcr.io/idaholab/malcolm/postgresql:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index 908617354..67abd4a09 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -36,7 +36,7 @@ spec: spec: containers: - name: netbox-container - image: ghcr.io/idaholab/malcolm/netbox:kubernetes + image: ghcr.io/idaholab/malcolm/netbox:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml index b877ad463..0a87e3872 100644 --- a/kubernetes/19-htadmin.yml +++ b/kubernetes/19-htadmin.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: htadmin-container - image: ghcr.io/idaholab/malcolm/htadmin:kubernetes + image: ghcr.io/idaholab/malcolm/htadmin:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/20-pcap-capture.yml b/kubernetes/20-pcap-capture.yml index fd4905e20..53a9f971e 100644 --- a/kubernetes/20-pcap-capture.yml +++ b/kubernetes/20-pcap-capture.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: pcap-capture-container - image: ghcr.io/idaholab/malcolm/pcap-capture:kubernetes + image: ghcr.io/idaholab/malcolm/pcap-capture:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/21-zeek-live.yml b/kubernetes/21-zeek-live.yml index 5d11dd5a1..2f8c6b03f 100644 --- a/kubernetes/21-zeek-live.yml +++ b/kubernetes/21-zeek-live.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: zeek-live-container - image: ghcr.io/idaholab/malcolm/zeek:kubernetes + image: ghcr.io/idaholab/malcolm/zeek:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/22-suricata-live.yml b/kubernetes/22-suricata-live.yml index 9c2de26aa..73fb72532 100644 --- a/kubernetes/22-suricata-live.yml +++ b/kubernetes/22-suricata-live.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: suricata-live-container - image: ghcr.io/idaholab/malcolm/suricata:kubernetes + image: ghcr.io/idaholab/malcolm/suricata:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/23-freq.yml b/kubernetes/23-freq.yml index 4c71155da..a57617d05 100644 --- a/kubernetes/23-freq.yml +++ b/kubernetes/23-freq.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: freq-container - image: ghcr.io/idaholab/malcolm/freq:kubernetes + image: ghcr.io/idaholab/malcolm/freq:23.04.1 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index e193bc757..72ebc014f 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -37,7 +37,7 @@ spec: spec: containers: - name: nginx-proxy-container - image: ghcr.io/idaholab/malcolm/nginx-proxy:kubernetes + image: ghcr.io/idaholab/malcolm/nginx-proxy:23.04.1 imagePullPolicy: Always stdin: false tty: true From ec7e518e54142c4134ce1a29ddcf46ad9243f7ee Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 27 Apr 2023 08:18:24 -0600 Subject: [PATCH 222/239] bump arkime to v4.3.0 (https://github.com/arkime/arkime/blob/fa0db2415bdc109be7a4dd8ee2c2838673980b5f/CHANGELOG#L33-L72) --- Dockerfiles/arkime.Dockerfile | 2 +- sensor-iso/arkime/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index 1b35074c2..99a1fd88f 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -4,7 +4,7 @@ FROM debian:11-slim AS build ENV DEBIAN_FRONTEND noninteractive -ENV ARKIME_VERSION "v4.2.0" +ENV ARKIME_VERSION "v4.3.0" ENV ARKIME_DIR "/opt/arkime" ENV ARKIME_URL "https://github.com/arkime/arkime.git" ENV ARKIME_LOCALELASTICSEARCH no diff --git a/sensor-iso/arkime/Dockerfile b/sensor-iso/arkime/Dockerfile index 32b8f1bb8..eaec223e1 100644 --- a/sensor-iso/arkime/Dockerfile +++ b/sensor-iso/arkime/Dockerfile @@ -6,7 +6,7 @@ LABEL maintainer="malcolm@inl.gov" ENV DEBIAN_FRONTEND noninteractive -ENV ARKIME_VERSION "4.2.0" +ENV ARKIME_VERSION "4.3.0" ENV ARKIME_DIR "/opt/arkime" RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.list && \ From 5dfbf68535fd4042a28e13d296078e75fa97aff1 Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 27 Apr 2023 08:26:39 -0600 Subject: [PATCH 223/239] minor arkime ini tweaks --- arkime/etc/config.ini | 3 +-- sensor-iso/interface/sensor_ctl/arkime/config.ini | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/arkime/etc/config.ini b/arkime/etc/config.ini index cd480d289..494779a1d 100644 --- a/arkime/etc/config.ini +++ b/arkime/etc/config.ini @@ -42,7 +42,7 @@ parseSMB=true parseQSValue=false supportSha256=false maxReqBody=64 -config.reqBodyOnlyUtf8=true +reqBodyOnlyUtf8=true smtpIpHeaders=X-Originating-IP:;X-Barracuda-Apparent-Source-IP: parsersDir=/opt/arkime/parsers pluginsDir=/opt/arkime/plugins @@ -54,7 +54,6 @@ pcapWriteMethod=simple pcapWriteSize=262143 simpleCompression=zstd simpleZstdLevel=3 -dbBulkSize=300000 compressES=false maxESConns=30 maxESRequests=500 diff --git a/sensor-iso/interface/sensor_ctl/arkime/config.ini b/sensor-iso/interface/sensor_ctl/arkime/config.ini index 63337b703..3214dbec3 100644 --- a/sensor-iso/interface/sensor_ctl/arkime/config.ini +++ b/sensor-iso/interface/sensor_ctl/arkime/config.ini @@ -29,7 +29,7 @@ parseSMB=true parseQSValue=false supportSha256=false maxReqBody=64 -config.reqBodyOnlyUtf8=true +reqBodyOnlyUtf8=true smtpIpHeaders=X-Originating-IP:;X-Barracuda-Apparent-Source-IP: parsersDir=/dummy/parsers pluginsDir=/dummy/plugins From 2abc485b2fa56d8083e5934151642b92ce7134db Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 27 Apr 2023 11:19:23 -0600 Subject: [PATCH 224/239] for idaholab/Malcolm#189, have a special secretmap directory that is treated like the configmap directory on container entrypoint --- shared/bin/docker-uid-gid-setup.sh | 69 ++++++++++++++++-------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/shared/bin/docker-uid-gid-setup.sh b/shared/bin/docker-uid-gid-setup.sh index 9b8ec8b28..dbca5a505 100755 --- a/shared/bin/docker-uid-gid-setup.sh +++ b/shared/bin/docker-uid-gid-setup.sh @@ -27,43 +27,46 @@ groupmod --non-unique --gid ${PGID:-${DEFAULT_GID}} ${PGROUP} # copied is made read-only, doesn't play nicely if you're using it for configuration # files which exist in a directory which may need to do read-write operations on other files. # This works for nested subdirectories, but don't nest CONFIG_MAP_DIR directories -# inside of other CONFIG_MAP_DIR directories. +# inside of other CONFIG_MAP_DIR directories. More than one CONFIG_MAP_DIR can be specified +# in this variable, separated by ';' (for example, "CONFIG_MAP_DIR=configmap;secretmap"). # # TODO: else with cpio, tar, cp? +CONFIG_MAP_FIND_PRUNE_ARGS=() if [[ -n ${CONFIG_MAP_DIR} ]] && command -v rsync >/dev/null 2>&1; then - find / -type d -name "${CONFIG_MAP_DIR}" -print -o -path /sys -prune -o -path /proc -prune 2>/dev/null | \ - awk '{print gsub("/","/"), $0}' | sort -n | cut -d' ' -f2- | \ - while read CMDIR; do - - rsync --recursive --copy-links \ - "--usermap=*:${PUID:-${DEFAULT_UID}}" \ - "--groupmap=*:${PGID:-${DEFAULT_GID}}" \ - --exclude='..*' --exclude="${CONFIG_MAP_DIR}"/ --exclude=.dockerignore --exclude=.gitignore \ - "${CMDIR}"/ "${CMDIR}"/../ - - # TODO - regarding ownership and permissions: - # - # I *think* what we want to do here is change the ownership of - # these configmap-copied files to be owned by the user specified by PUID - # (falling back to DEFAULT_UID) and PGID (falling back to DEFAULT_GID). - # The other option would be to preserve the ownership of the source - # fine with --owner --group, but I don't think that's what we want, as - # if we were doing this with a docker bind mount they'd likely have the - # permissions of the original user on the host, anyway, which is - # supposed to match up to PUID/PGID. - # - # For permissions, rsync says that "existing files retain their existing permissions" - # and "new files get their normal permission bits set to the source file's - # permissions masked with the receiving directory's default permissions" - # (either via umask or ACL) which I think is what we want. The other alternative - # would be to do something like --chmod=D2755,F644 - - done # loop over found CONFIG_MAP_DIR directories - CONFIG_MAP_FIND_PRUNE_ARGS=(-o -name "${CONFIG_MAP_DIR}" -prune) - -else - CONFIG_MAP_FIND_PRUNE_ARGS=() + while read MAP_DIR; do + + find / -type d -name "${MAP_DIR}" -print -o -path /sys -prune -o -path /proc -prune 2>/dev/null | \ + awk '{print gsub("/","/"), $0}' | sort -n | cut -d' ' -f2- | \ + while read CMDIR; do + + rsync --recursive --copy-links \ + "--usermap=*:${PUID:-${DEFAULT_UID}}" \ + "--groupmap=*:${PGID:-${DEFAULT_GID}}" \ + --exclude='..*' --exclude="${MAP_DIR}"/ --exclude=.dockerignore --exclude=.gitignore \ + "${CMDIR}"/ "${CMDIR}"/../ + + # TODO - regarding ownership and permissions: + # + # I *think* what we want to do here is change the ownership of + # these configmap-copied files to be owned by the user specified by PUID + # (falling back to DEFAULT_UID) and PGID (falling back to DEFAULT_GID). + # The other option would be to preserve the ownership of the source + # fine with --owner --group, but I don't think that's what we want, as + # if we were doing this with a docker bind mount they'd likely have the + # permissions of the original user on the host, anyway, which is + # supposed to match up to PUID/PGID. + # + # For permissions, rsync says that "existing files retain their existing permissions" + # and "new files get their normal permission bits set to the source file's + # permissions masked with the receiving directory's default permissions" + # (either via umask or ACL) which I think is what we want. The other alternative + # would be to do something like --chmod=D2755,F644 + + done # loop over found MAP_DIR directories + CONFIG_MAP_FIND_PRUNE_ARGS+=(-o -name "${MAP_DIR}" -prune) + + done < <(echo "${CONFIG_MAP_DIR}" | tr ';' '\n') # loop over ';' separated CONFIG_MAP_DIR values fi # check for CONFIG_MAP_DIR and rsync set +e From 15d9365c95c7bafc7d66e8fdda211ecbe8aba57f Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 27 Apr 2023 13:52:30 -0600 Subject: [PATCH 225/239] idaholab/Malcolm#189; kubernetes - use Secrets for some environment variables instead of ConfigMaps --- config/arkime-secret.env.example | 4 + config/arkime.env.example | 3 - config/auth.env.example | 1 + config/netbox-postgres.env.example | 1 + config/netbox-redis-cache.env.example | 1 + config/netbox-redis.env.example | 1 + config/netbox-secret.env.example | 14 ++ config/netbox.env.example | 14 +- config/process.env.example | 4 +- config/zeek-secret.env.example | 5 + config/zeek.env.example | 4 - docker-compose-standalone.yml | 6 + docker-compose.yml | 6 + docs/asset-interaction-analysis.md | 2 +- docs/contributing-local-modifications.md | 96 --------- docs/file-scanning.md | 2 +- docs/kubernetes.md | 4 +- docs/malcolm-config.md | 6 +- kubernetes/02-opensearch.yml | 20 +- kubernetes/03-dashboards.yml | 10 +- kubernetes/04-upload.yml | 2 +- kubernetes/05-pcap-monitor.yml | 10 +- kubernetes/06-arkime.yml | 14 +- kubernetes/07-api.yml | 10 +- kubernetes/08-dashboards-helper.yml | 10 +- kubernetes/09-zeek.yml | 2 + kubernetes/11-file-monitor.yml | 2 + kubernetes/12-filebeat.yml | 20 +- kubernetes/13-logstash.yml | 32 +-- kubernetes/15-netbox-redis.yml | 2 +- kubernetes/16-netbox-redis-cache.yml | 2 +- kubernetes/17-netbox-postgres.yml | 2 +- kubernetes/18-netbox.yml | 2 + kubernetes/19-htadmin.yml | 5 + kubernetes/21-zeek-live.yml | 2 + kubernetes/99-nginx-proxy.yml | 35 ++-- scripts/control.py | 22 +- scripts/install.py | 4 +- scripts/malcolm_common.py | 2 +- scripts/malcolm_kubernetes.py | 255 +++++++++++++++-------- 40 files changed, 342 insertions(+), 297 deletions(-) create mode 100644 config/arkime-secret.env.example create mode 100644 config/netbox-secret.env.example create mode 100644 config/zeek-secret.env.example diff --git a/config/arkime-secret.env.example b/config/arkime-secret.env.example new file mode 100644 index 000000000..7718ec2ec --- /dev/null +++ b/config/arkime-secret.env.example @@ -0,0 +1,4 @@ +# MaxMind GeoIP database update API key (see +# https://support.maxmind.com/hc/en-us/articles/4407116112539-Using-License-Keys) +MAXMIND_GEOIP_DB_LICENSE_KEY=0 +K8S_SECRET=True \ No newline at end of file diff --git a/config/arkime.env.example b/config/arkime.env.example index 6edb657a9..183e970e3 100644 --- a/config/arkime.env.example +++ b/config/arkime.env.example @@ -3,8 +3,5 @@ MANAGE_PCAP_FILES=false # The number of Arkime capture processes allowed to run concurrently ARKIME_ANALYZE_PCAP_THREADS=1 -# MaxMind GeoIP database update API key (see -# https://support.maxmind.com/hc/en-us/articles/4407116112539-Using-License-Keys) -MAXMIND_GEOIP_DB_LICENSE_KEY=0 OPENSEARCH_MAX_SHARDS_PER_NODE=2500 \ No newline at end of file diff --git a/config/auth.env.example b/config/auth.env.example index 60a2bf032..6ffb97a65 100644 --- a/config/auth.env.example +++ b/config/auth.env.example @@ -1,3 +1,4 @@ # Malcolm Administrator username and encrypted password for nginx reverse proxy (and upload server's SFTP access) MALCOLM_USERNAME=admin MALCOLM_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX== +K8S_SECRET=True \ No newline at end of file diff --git a/config/netbox-postgres.env.example b/config/netbox-postgres.env.example index 8e4861b2e..6814396b9 100644 --- a/config/netbox-postgres.env.example +++ b/config/netbox-postgres.env.example @@ -1,3 +1,4 @@ POSTGRES_DB=netbox POSTGRES_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXX POSTGRES_USER=netbox +K8S_SECRET=True \ No newline at end of file diff --git a/config/netbox-redis-cache.env.example b/config/netbox-redis-cache.env.example index 7883e1460..b5e80205d 100644 --- a/config/netbox-redis-cache.env.example +++ b/config/netbox-redis-cache.env.example @@ -1 +1,2 @@ REDIS_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXX +K8S_SECRET=True \ No newline at end of file diff --git a/config/netbox-redis.env.example b/config/netbox-redis.env.example index 7883e1460..b5e80205d 100644 --- a/config/netbox-redis.env.example +++ b/config/netbox-redis.env.example @@ -1 +1,2 @@ REDIS_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXX +K8S_SECRET=True \ No newline at end of file diff --git a/config/netbox-secret.env.example b/config/netbox-secret.env.example new file mode 100644 index 000000000..d629b84df --- /dev/null +++ b/config/netbox-secret.env.example @@ -0,0 +1,14 @@ +DB_PASSWORD=xxxxxxxxxxxxxxxx +DB_USER=netbox +EMAIL_PASSWORD= +EMAIL_USERNAME=netbox +NAPALM_PASSWORD= +NAPALM_USERNAME= +REDIS_CACHE_PASSWORD=xxxxxxxxxxxxxxxx +REDIS_PASSWORD=xxxxxxxxxxxxxxxx +SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +SUPERUSER_API_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +SUPERUSER_NAME=admin +SUPERUSER_PASSWORD=admin + +K8S_SECRET=True \ No newline at end of file diff --git a/config/netbox.env.example b/config/netbox.env.example index 1bd31f5b3..ddafba06f 100644 --- a/config/netbox.env.example +++ b/config/netbox.env.example @@ -14,16 +14,12 @@ REMOTE_AUTH_SUPERUSERS= EXEMPT_VIEW_PERMISSIONS=* DB_HOST=netbox-postgres DB_NAME=netbox -DB_PASSWORD=xxxxxxxxxxxxxxxx -DB_USER=netbox EMAIL_FROM=netbox@bar.com -EMAIL_PASSWORD= EMAIL_PORT=25 EMAIL_SERVER=localhost EMAIL_SSL_CERTFILE= EMAIL_SSL_KEYFILE= EMAIL_TIMEOUT=5 -EMAIL_USERNAME=netbox # EMAIL_USE_SSL and EMAIL_USE_TLS are mutually exclusive, i.e. they can't both be `true`! EMAIL_USE_SSL=false EMAIL_USE_TLS=false @@ -32,25 +28,17 @@ HOUSEKEEPING_INTERVAL=86400 MAX_PAGE_SIZE=1000 MEDIA_ROOT=/opt/netbox/netbox/media METRICS_ENABLED=false -NAPALM_PASSWORD= NAPALM_TIMEOUT=10 -NAPALM_USERNAME= REDIS_CACHE_DATABASE=1 REDIS_CACHE_HOST=netbox-redis-cache REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY=false -REDIS_CACHE_PASSWORD=xxxxxxxxxxxxxxxx REDIS_CACHE_SSL=false REDIS_DATABASE=0 REDIS_HOST=netbox-redis REDIS_INSECURE_SKIP_TLS_VERIFY=false -REDIS_PASSWORD=xxxxxxxxxxxxxxxx REDIS_SSL=false RELEASE_CHECK_URL=https://api.github.com/repos/netbox-community/netbox/releases -SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx SKIP_STARTUP_SCRIPTS=true SKIP_SUPERUSER=false -SUPERUSER_API_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx SUPERUSER_EMAIL=admin@example.com -SUPERUSER_NAME=admin -SUPERUSER_PASSWORD=admin -WEBHOOKS_ENABLED=true +WEBHOOKS_ENABLED=true \ No newline at end of file diff --git a/config/process.env.example b/config/process.env.example index ca0a1923f..9cd2b8f71 100644 --- a/config/process.env.example +++ b/config/process.env.example @@ -3,5 +3,5 @@ PUID=1000 PGID=1000 # for debugging container init via tini (https://github.com/krallin/tini) TINI_VERBOSITY=1 -# for handling configmap files/directories -CONFIG_MAP_DIR=configmap \ No newline at end of file +# for handling configmap/secrets files/directories +CONFIG_MAP_DIR=configmap;secretmap \ No newline at end of file diff --git a/config/zeek-secret.env.example b/config/zeek-secret.env.example new file mode 100644 index 000000000..8ce2739c6 --- /dev/null +++ b/config/zeek-secret.env.example @@ -0,0 +1,5 @@ +# A VirusTotal Public API v.20 used to submit hashes of Zeek-extracted files +VTOT_API2_KEY=0 +# Specifies the AES-256-CBC decryption password for encrypted Zeek-extracted files served over HTTP +EXTRACTED_FILE_HTTP_SERVER_KEY=quarantined +K8S_SECRET=True \ No newline at end of file diff --git a/config/zeek.env.example b/config/zeek.env.example index 3563a08c3..060fe1290 100644 --- a/config/zeek.env.example +++ b/config/zeek.env.example @@ -21,8 +21,6 @@ EXTRACTED_FILE_PRESERVATION=quarantined EXTRACTED_FILE_MIN_BYTES=64 # The maximum size (in bytes) for files to be extracted by Zeek EXTRACTED_FILE_MAX_BYTES=134217728 -# A VirusTotal Public API v.20 used to submit hashes of Zeek-extracted files -VTOT_API2_KEY=0 # Rate limiting for VirusTotal, ClamAV, YARA and capa with Zeek-extracted files VTOT_REQUESTS_PER_MINUTE=4 CLAMD_MAX_REQUESTS=8 @@ -46,8 +44,6 @@ EXTRACTED_FILE_PIPELINE_VERBOSITY= EXTRACTED_FILE_HTTP_SERVER_ENABLE=false # Whether or not Zeek-extracted files served over HTTP will be AES-256-CBC-encrypted EXTRACTED_FILE_HTTP_SERVER_ENCRYPT=true -# Specifies the AES-256-CBC decryption password for encrypted Zeek-extracted files served over HTTP -EXTRACTED_FILE_HTTP_SERVER_KEY=quarantined # Environment variables for tweaking Zeek at runtime (see local.zeek) # Set to any non-blank value to disable the corresponding feature ZEEK_DISABLE_HASH_ALL_FILES= diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 4066ea3ee..7c9367c9b 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -109,6 +109,7 @@ services: - ./config/opensearch.env - ./config/netbox-common.env - ./config/netbox.env + - ./config/netbox-secret.env - ./config/beats-common.env - ./config/lookup-common.env - ./config/logstash.env @@ -180,6 +181,7 @@ services: - ./config/upload-common.env - ./config/auth.env - ./config/arkime.env + - ./config/arkime-secret.env environment: VIRTUAL_HOST : 'arkime.malcolm.local' ulimits: @@ -222,6 +224,7 @@ services: - ./config/ssl.env - ./config/upload-common.env - ./config/zeek.env + - ./config/zeek-secret.env - ./config/zeek-offline.env depends_on: - opensearch @@ -258,6 +261,7 @@ services: - ./config/upload-common.env - ./config/pcap-capture.env - ./config/zeek.env + - ./config/zeek-secret.env - ./config/zeek-live.env volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro @@ -338,6 +342,7 @@ services: - ./config/process.env - ./config/ssl.env - ./config/zeek.env + - ./config/zeek-secret.env environment: VIRTUAL_HOST : 'file-monitor.malcolm.local' volumes: @@ -487,6 +492,7 @@ services: - ./config/ssl.env - ./config/netbox-common.env - ./config/netbox.env + - ./config/netbox-secret.env environment: VIRTUAL_HOST : 'netbox.malcolm.local' depends_on: diff --git a/docker-compose.yml b/docker-compose.yml index f47da7a76..3338d0424 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -121,6 +121,7 @@ services: - ./config/opensearch.env - ./config/netbox-common.env - ./config/netbox.env + - ./config/netbox-secret.env - ./config/beats-common.env - ./config/lookup-common.env - ./config/logstash.env @@ -202,6 +203,7 @@ services: - ./config/upload-common.env - ./config/auth.env - ./config/arkime.env + - ./config/arkime-secret.env environment: VIRTUAL_HOST : 'arkime.malcolm.local' ulimits: @@ -250,6 +252,7 @@ services: - ./config/ssl.env - ./config/upload-common.env - ./config/zeek.env + - ./config/zeek-secret.env - ./config/zeek-offline.env depends_on: - opensearch @@ -290,6 +293,7 @@ services: - ./config/upload-common.env - ./config/pcap-capture.env - ./config/zeek.env + - ./config/zeek-secret.env - ./config/zeek-live.env volumes: - ./nginx/ca-trust:/var/local/ca-trust:ro @@ -380,6 +384,7 @@ services: - ./config/process.env - ./config/ssl.env - ./config/zeek.env + - ./config/zeek-secret.env environment: VIRTUAL_HOST : 'file-monitor.malcolm.local' volumes: @@ -547,6 +552,7 @@ services: - ./config/ssl.env - ./config/netbox-common.env - ./config/netbox.env + - ./config/netbox-secret.env environment: VIRTUAL_HOST : 'netbox.malcolm.local' depends_on: diff --git a/docs/asset-interaction-analysis.md b/docs/asset-interaction-analysis.md index a72884e26..90637e1d2 100644 --- a/docs/asset-interaction-analysis.md +++ b/docs/asset-interaction-analysis.md @@ -97,4 +97,4 @@ To clear the existing NetBox database and restore a previous backup, run the fol ``` -Note that some of the data in the NetBox database is cryptographically signed with the value of the `SECRET_KEY` environment variable in the `./netbox/env/netbox.env` environment file. A restored NetBox backup **will not work** if this value is different from when it was created. +Note that some of the data in the NetBox database is cryptographically signed with the value of the `SECRET_KEY` environment variable in the `./netbox/env/netbox-secret.env` environment file. A restored NetBox backup **will not work** if this value is different from when it was created. diff --git a/docs/contributing-local-modifications.md b/docs/contributing-local-modifications.md index afec0a841..289513aa3 100644 --- a/docs/contributing-local-modifications.md +++ b/docs/contributing-local-modifications.md @@ -9,9 +9,6 @@ Some configuration changes can be put in place by modifying local copies of conf ``` $ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml opensearch: - - ./config/process.env - - ./config/ssl.env - - ./config/opensearch.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro @@ -19,28 +16,13 @@ opensearch: - ./opensearch-backup:/opt/opensearch/backup:delegated - ./opensearch/opensearch.keystore:/usr/share/opensearch/config/persist/opensearch.keystore:rw dashboards-helper: - - ./config/process.env - - ./config/ssl.env - - ./config/opensearch.env - - ./config/dashboards-helper.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro dashboards: - - ./config/process.env - - ./config/ssl.env - - ./config/opensearch.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro logstash: - - ./config/process.env - - ./config/ssl.env - - ./config/opensearch.env - - ./config/netbox-common.env - - ./config/netbox.env - - ./config/beats-common.env - - ./config/lookup-common.env - - ./config/logstash.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./.opensearch.secondary.curlrc:/var/local/curlrc/.opensearch.secondary.curlrc:ro @@ -49,13 +31,6 @@ logstash: - ./logstash/certs/server.crt:/certs/server.crt:ro - ./logstash/certs/server.key:/certs/server.key:ro filebeat: - - ./config/process.env - - ./config/ssl.env - - ./config/opensearch.env - - ./config/upload-common.env - - ./config/nginx.env - - ./config/beats-common.env - - ./config/filebeat.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./zeek-logs:/zeek @@ -64,107 +39,55 @@ filebeat: - ./filebeat/certs/client.crt:/certs/client.crt:ro - ./filebeat/certs/client.key:/certs/client.key:ro arkime: - - ./config/process.env - - ./config/ssl.env - - ./config/opensearch.env - - ./config/upload-common.env - - ./config/auth.env - - ./config/arkime.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./pcap:/data/pcap - ./arkime-logs:/opt/arkime/logs - ./arkime-raw:/opt/arkime/raw zeek: - - ./config/process.env - - ./config/ssl.env - - ./config/upload-common.env - - ./config/zeek.env - - ./config/zeek-offline.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap:/pcap - ./zeek-logs/upload:/zeek/upload - ./zeek-logs/extract_files:/zeek/extract_files - ./zeek/intel:/opt/zeek/share/zeek/site/intel zeek-live: - - ./config/process.env - - ./config/ssl.env - - ./config/upload-common.env - - ./config/pcap-capture.env - - ./config/zeek.env - - ./config/zeek-live.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./zeek-logs/live:/zeek/live - ./zeek-logs/extract_files:/zeek/extract_files - ./zeek/intel:/opt/zeek/share/zeek/site/intel suricata: - - ./config/process.env - - ./config/ssl.env - - ./config/upload-common.env - - ./config/suricata.env - - ./config/suricata-offline.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./suricata-logs:/var/log/suricata - ./pcap:/data/pcap - ./suricata/rules:/opt/suricata/rules:ro suricata-live: - - ./config/process.env - - ./config/ssl.env - - ./config/upload-common.env - - ./config/pcap-capture.env - - ./config/suricata.env - - ./config/suricata-live.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./suricata-logs:/var/log/suricata - ./suricata/rules:/opt/suricata/rules:ro file-monitor: - - ./config/process.env - - ./config/ssl.env - - ./config/zeek.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./zeek-logs/extract_files:/zeek/extract_files - ./zeek-logs/current:/zeek/logs - ./yara/rules:/yara-rules/custom:ro pcap-capture: - - ./config/process.env - - ./config/ssl.env - - ./config/pcap-capture.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/pcap pcap-monitor: - - ./config/process.env - - ./config/ssl.env - - ./config/opensearch.env - - ./config/upload-common.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro - ./zeek-logs:/zeek - ./pcap:/pcap upload: - - ./config/process.env - - ./config/ssl.env - - ./config/auth.env - - ./config/upload.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/var/www/upload/server/php/chroot/files htadmin: - - ./config/process.env - - ./config/ssl.env - - ./config/auth-common.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./htadmin/config.ini:/var/www/htadmin/config/config.ini:rw - ./htadmin/metadata:/var/www/htadmin/config/metadata:rw - ./nginx/htpasswd:/var/www/htadmin/auth/htpasswd:rw freq: - - ./config/process.env - - ./config/ssl.env - - ./config/lookup-common.env - ./nginx/ca-trust:/var/local/ca-trust:ro netbox: - - ./config/process.env - - ./config/ssl.env - - ./config/netbox-common.env - - ./config/netbox.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./netbox/config/configuration:/etc/netbox/config:ro - ./netbox/config/reports:/etc/netbox/reports:ro @@ -172,36 +95,17 @@ netbox: - ./netbox/media:/opt/netbox/netbox/media:rw - ./net-map.json:/usr/local/share/net-map.json:ro netbox-postgres: - - ./config/process.env - - ./config/ssl.env - - ./config/netbox-common.env - - ./config/netbox-postgres.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./netbox/postgres:/var/lib/postgresql/data:rw netbox-redis: - - ./config/process.env - - ./config/ssl.env - - ./config/netbox-common.env - - ./config/netbox-redis.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./netbox/redis:/data netbox-redis-cache: - - ./config/process.env - - ./config/ssl.env - - ./config/netbox-common.env - - ./config/netbox-redis-cache.env - ./nginx/ca-trust:/var/local/ca-trust:ro api: - - ./config/process.env - - ./config/ssl.env - - ./config/opensearch.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./.opensearch.primary.curlrc:/var/local/curlrc/.opensearch.primary.curlrc:ro nginx-proxy: - - ./config/process.env - - ./config/ssl.env - - ./config/auth-common.env - - ./config/nginx.env - ./nginx/ca-trust:/var/local/ca-trust:ro - ./nginx/nginx_ldap.conf:/etc/nginx/nginx_ldap.conf:ro - ./nginx/htpasswd:/etc/nginx/auth/htpasswd:ro diff --git a/docs/file-scanning.md b/docs/file-scanning.md index 7847bb144..57c9560f8 100644 --- a/docs/file-scanning.md +++ b/docs/file-scanning.md @@ -12,7 +12,7 @@ To specify which files should be extracted, the following values are acceptable Extracted files can be examined through any of the following methods: -* submitting file hashes to [**VirusTotal**](https://www.virustotal.com/en/#search); to enable this method, specify the `VTOT_API2_KEY` [environment variable in `zeek.env`](malcolm-config.md#MalcolmConfigEnvVars) +* submitting file hashes to [**VirusTotal**](https://www.virustotal.com/en/#search); to enable this method, specify the `VTOT_API2_KEY` [environment variable in `zeek-secret.env`](malcolm-config.md#MalcolmConfigEnvVars) * scanning files with [**ClamAV**](https://www.clamav.net/); to enable this method, set the `EXTRACTED_FILE_ENABLE_CLAMAV` [environment variable in `zeek.env`](malcolm-config.md#MalcolmConfigEnvVars) to `true` * scanning files with [**Yara**](https://github.com/VirusTotal/yara); to enable this method, set the `EXTRACTED_FILE_ENABLE_YARA` [environment variable in `zeek.env`](malcolm-config.md#MalcolmConfigEnvVars) to `true` * scanning PE (portable executable) files with [**Capa**](https://github.com/fireeye/capa); to enable this method, set the `EXTRACTED_FILE_ENABLE_CAPA` [environment variable in `zeek.env`](malcolm-config.md#MalcolmConfigEnvVars) to `true` diff --git a/docs/kubernetes.md b/docs/kubernetes.md index c7b6e2c2e..bea11a4dc 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -247,8 +247,8 @@ $ ./scripts/start -f /path/to/kubeconfig.yml The Kubernetes resources under the `malcolm` namespace (its pods, storage volumes, containers, etc.) will be initialized and started using the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/), including: -* creating [ConfigMap objects](https://kubernetes.io/docs/concepts/configuration/configmap/) from Malcolm's [environment variable files](malcolm-config.md#MalcolmConfigEnvVars) -* creating [ConfigMap objects](https://kubernetes.io/docs/concepts/configuration/configmap/) from other configuration files stored locally below the Malcolm directory +* creating [ConfigMap objects](https://kubernetes.io/docs/concepts/configuration/configmap/) and [Secret objects](https://kubernetes.io/docs/concepts/configuration/secret/) from Malcolm's [environment variable files](malcolm-config.md#MalcolmConfigEnvVars) +* creating [ConfigMap objects](https://kubernetes.io/docs/concepts/configuration/configmap/) and [Secret objects](https://kubernetes.io/docs/concepts/configuration/secret/) from other configuration files stored locally below the Malcolm directory * deploying the objects defined in the [Kubernetes manifests]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/kubernetes/) in `./kubernetes` After a few moments you can check the status of the deployment: diff --git a/docs/malcolm-config.md b/docs/malcolm-config.md index 0fa5d4789..ce8317c62 100644 --- a/docs/malcolm-config.md +++ b/docs/malcolm-config.md @@ -8,7 +8,7 @@ Run `./scripts/configure` and answer the questions to configure Malcolm. For an Although the configuration script automates many of the following configuration and tuning parameters, some environment variables of particular interest are listed here for reference. -* **`arkime.env`** - settings for [Arkime](https://arkime.com/) +* **`arkime.env`** and **`arkime-secret.env`** - settings for [Arkime](https://arkime.com/) - `ARKIME_ANALYZE_PCAP_THREADS` – the number of threads available to Arkime for analyzing PCAP files (default `1`) - `MANAGE_PCAP_FILES` – if set to `true`, all PCAP files imported into Malcolm will be marked as available for deletion by Arkime if available storage space becomes too low (default `false`) - `MAXMIND_GEOIP_DB_LICENSE_KEY` - Malcolm uses MaxMind's free GeoLite2 databases for GeoIP lookups. As of December 30, 2019, these databases are [no longer available](https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-geolite2-databases/) for download via a public URL. Instead, they must be downloaded using a MaxMind license key (available without charge [from MaxMind](https://www.maxmind.com/en/geolite2/signup)). The license key can be specified here for GeoIP database downloads during build- and run-time. @@ -33,7 +33,7 @@ Although the configuration script automates many of the following configuration - `FREQ_SEVERITY_THRESHOLD` - when [severity scoring](severity.md#Severity) is enabled, this variable indicates the entropy threshold for assigning severity to events with entropy scores calculated by [`freq`](https://github.com/MarkBaggett/freq); a lower value will only assign severity scores to fewer domain names with higher entropy (e.g., `2.0` for `NQZHTFHRMYMTVBQJE.COM`), while a higher value will assign severity scores to more domain names with lower entropy (e.g., `7.5` for `naturallanguagedomain.example.org`) (default `2.0`) - `SENSITIVE_COUNTRY_CODES` - when [severity scoring](severity.md#Severity) is enabled, this variable defines a comma-separated list of sensitive countries (using [ISO 3166-1 alpha-2 codes](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Current_codes)) (default `'AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ'`, taken from the U.S. Department of Energy Sensitive Country List) - `TOTAL_MEGABYTES_SEVERITY_THRESHOLD` - when [severity scoring](severity.md#Severity) is enabled, this variable indicates the size threshold (in megabytes) for assigning severity to large connections or file transfers (default `1000`) -* **`netbox-common.env`**, `netbox.env`, `netbox-postgres.env`, `netbox-redis-cache.env` and `netbox-redis.env` - settings related to [NetBox](https://netbox.dev/) and [Asset Interaction Analysis](asset-interaction-analysis.md#AssetInteractionAnalysis) +* **`netbox-common.env`**, `netbox.env`, `netbox-secret.env`, `netbox-postgres.env`, `netbox-redis-cache.env` and `netbox-redis.env` - settings related to [NetBox](https://netbox.dev/) and [Asset Interaction Analysis](asset-interaction-analysis.md#AssetInteractionAnalysis) - `NETBOX_DISABLED` - if set to `true`, Malcolm will **not** start and manage a [NetBox](asset-interaction-analysis.md#AssetInteractionAnalysis) instance (default `true`) * **`nginx.env`** - settings specific to Malcolm's nginx reverse proxy - `NGINX_LOG_ACCESS_AND_ERRORS` - if set to `true`, all access to Malcolm via its [web interfaces](quickstart.md#UserInterfaceURLs) will be logged to OpenSearch (default `false`) @@ -67,7 +67,7 @@ Although the configuration script automates many of the following configuration - `SURICATA_…` - the [`suricata` container entrypoint script]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/suricata_config_populate.py) can use **many** more environment variables to tweak [suricata.yaml](https://github.com/OISF/suricata/blob/master/suricata.yaml.in); in that script, `DEFAULT_VARS` defines those variables (albeit without the `SURICATA_` prefix you must add to each for use) * **`upload-common.env`** and **`upload.env`** - settings for dealing with PCAP files [uploaded](upload.md#Upload) to Malcolm for analysis - `AUTO_TAG` – if set to `true`, Malcolm will automatically create Arkime sessions and Zeek logs with tags based on the filename, as described in [Tagging](upload.md#Tagging) (default `true`) -* **`zeek.env`**, **`zeek-live.env`** and **`zeek-offline.env`** - settings for [Zeek](https://www.zeek.org/index.html) and for scanning [extracted files](file-scanning.md#ZeekFileExtraction) Zeek observes in network traffic +* **`zeek.env`**, **`zeek-secret.env`**, **`zeek-live.env`** and **`zeek-offline.env`** - settings for [Zeek](https://www.zeek.org/index.html) and for scanning [extracted files](file-scanning.md#ZeekFileExtraction) Zeek observes in network traffic - `EXTRACTED_FILE_CAPA_VERBOSE` – if set to `true`, all Capa rule hits will be logged; otherwise (`false`) only [MITRE ATT&CK® technique](https://attack.mitre.org/techniques) classifications will be logged - `EXTRACTED_FILE_ENABLE_CAPA` – if set to `true`, [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) that are determined to be PE (portable executable) files will be scanned with [Capa](https://github.com/fireeye/capa) - `EXTRACTED_FILE_ENABLE_CLAMAV` – if set to `true`, [Zeek-extracted files](file-scanning.md#ZeekFileExtraction) will be scanned with [ClamAV](https://www.clamav.net/) diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index 16899ed91..976a6988c 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -56,14 +56,14 @@ spec: volumeMounts: - mountPath: /var/local/ca-trust/configmap name: opensearch-var-local-catrust-volume - - mountPath: /var/local/curlrc/configmap - name: opensearch-opensearch-curlrc-volume + - mountPath: /var/local/curlrc/secretmap + name: opensearch-opensearch-curlrc-secret-volume - mountPath: "/usr/share/opensearch/data" name: opensearch-data-volume - mountPath: "/opt/opensearch/backup" name: opensearch-backup-volume - - name: opensearch-keystore-default-volume - mountPath: /usr/share/opensearch/config/bootstrap/configmap + - name: opensearch-keystore-default-secret-volume + mountPath: /usr/share/opensearch/config/bootstrap/secretmap - name: opensearch-config-persist-volume mountPath: /usr/share/opensearch/config/persist subPath: "opensearch" @@ -71,18 +71,18 @@ spec: - name: opensearch-var-local-catrust-volume configMap: name: var-local-catrust - - name: opensearch-opensearch-curlrc-volume - configMap: - name: opensearch-curlrc + - name: opensearch-opensearch-curlrc-secret-volume + secret: + secretName: opensearch-curlrc - name: opensearch-data-volume persistentVolumeClaim: claimName: opensearch-claim - name: opensearch-backup-volume persistentVolumeClaim: claimName: opensearch-backup-claim - - name: opensearch-keystore-default-volume - configMap: - name: opensearch-keystore + - name: opensearch-keystore-default-secret-volume + secret: + secretName: opensearch-keystore - name: opensearch-config-persist-volume persistentVolumeClaim: claimName: config-claim \ No newline at end of file diff --git a/kubernetes/03-dashboards.yml b/kubernetes/03-dashboards.yml index 4f62d5fc7..4a2043177 100644 --- a/kubernetes/03-dashboards.yml +++ b/kubernetes/03-dashboards.yml @@ -61,12 +61,12 @@ spec: volumeMounts: - mountPath: /var/local/ca-trust/configmap name: dashboards-var-local-catrust-volume - - mountPath: /var/local/curlrc/configmap - name: dashboards-opensearch-curlrc-volume + - mountPath: /var/local/curlrc/secretmap + name: dashboards-opensearch-curlrc-secret-volume volumes: - name: dashboards-var-local-catrust-volume configMap: name: var-local-catrust - - name: dashboards-opensearch-curlrc-volume - configMap: - name: opensearch-curlrc + - name: dashboards-opensearch-curlrc-secret-volume + secret: + secretName: opensearch-curlrc diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 1c15e0d3c..3e47c9d1b 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -50,7 +50,7 @@ spec: name: process-env - configMapRef: name: ssl-env - - configMapRef: + - secretRef: name: auth-env - configMapRef: name: upload-env diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml index e19938fc4..eb649186e 100644 --- a/kubernetes/05-pcap-monitor.yml +++ b/kubernetes/05-pcap-monitor.yml @@ -62,8 +62,8 @@ spec: volumeMounts: - mountPath: /var/local/ca-trust/configmap name: pcap-monitor-var-local-catrust-volume - - mountPath: /var/local/curlrc/configmap - name: pcap-monitor-opensearch-curlrc-volume + - mountPath: /var/local/curlrc/secretmap + name: pcap-monitor-opensearch-curlrc-secret-volume - mountPath: "/pcap" name: pcap-monitor-pcap-volume - mountPath: "/zeek" @@ -72,9 +72,9 @@ spec: - name: pcap-monitor-var-local-catrust-volume configMap: name: var-local-catrust - - name: pcap-monitor-opensearch-curlrc-volume - configMap: - name: opensearch-curlrc + - name: pcap-monitor-opensearch-curlrc-secret-volume + secret: + secretName: opensearch-curlrc - name: pcap-monitor-pcap-volume persistentVolumeClaim: claimName: pcap-claim diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index cc373ff35..0d5a3e70c 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -50,12 +50,14 @@ spec: name: ssl-env - configMapRef: name: opensearch-env - - configMapRef: + - secretRef: name: auth-env - configMapRef: name: upload-common-env - configMapRef: name: arkime-env + - secretRef: + name: arkime-secret-env env: - name: VIRTUAL_HOST value: "arkime.malcolm.local" @@ -72,8 +74,8 @@ spec: volumeMounts: - mountPath: /var/local/ca-trust/configmap name: arkime-var-local-catrust-volume - - mountPath: /var/local/curlrc/configmap - name: arkime-opensearch-curlrc-volume + - mountPath: /var/local/curlrc/secretmap + name: arkime-opensearch-curlrc-secret-volume - mountPath: "/data/pcap" name: arkime-pcap-volume - name: arkime-runtime-logs-volume @@ -83,9 +85,9 @@ spec: - name: arkime-var-local-catrust-volume configMap: name: var-local-catrust - - name: arkime-opensearch-curlrc-volume - configMap: - name: opensearch-curlrc + - name: arkime-opensearch-curlrc-secret-volume + secret: + secretName: opensearch-curlrc - name: arkime-pcap-volume persistentVolumeClaim: claimName: pcap-claim diff --git a/kubernetes/07-api.yml b/kubernetes/07-api.yml index 42cf3c3e1..a93795ec5 100644 --- a/kubernetes/07-api.yml +++ b/kubernetes/07-api.yml @@ -63,12 +63,12 @@ spec: volumeMounts: - mountPath: /var/local/ca-trust/configmap name: api-var-local-catrust-volume - - mountPath: /var/local/curlrc/configmap - name: api-opensearch-curlrc-volume + - mountPath: /var/local/curlrc/secretmap + name: api-opensearch-curlrc-secret-volume volumes: - name: api-var-local-catrust-volume configMap: name: var-local-catrust - - name: api-opensearch-curlrc-volume - configMap: - name: opensearch-curlrc + - name: api-opensearch-curlrc-secret-volume + secret: + secretName: opensearch-curlrc diff --git a/kubernetes/08-dashboards-helper.yml b/kubernetes/08-dashboards-helper.yml index 4ce938b93..a0b26a4e0 100644 --- a/kubernetes/08-dashboards-helper.yml +++ b/kubernetes/08-dashboards-helper.yml @@ -65,12 +65,12 @@ spec: volumeMounts: - mountPath: /var/local/ca-trust/configmap name: dashboards-helper-var-local-catrust-volume - - mountPath: /var/local/curlrc/configmap - name: dashboards-helper-opensearch-curlrc-volume + - mountPath: /var/local/curlrc/secretmap + name: dashboards-helper-opensearch-curlrc-secret-volume volumes: - name: dashboards-helper-var-local-catrust-volume configMap: name: var-local-catrust - - name: dashboards-helper-opensearch-curlrc-volume - configMap: - name: opensearch-curlrc + - name: dashboards-helper-opensearch-curlrc-secret-volume + secret: + secretName: opensearch-curlrc diff --git a/kubernetes/09-zeek.yml b/kubernetes/09-zeek.yml index c99d3e21b..4c505c03e 100644 --- a/kubernetes/09-zeek.yml +++ b/kubernetes/09-zeek.yml @@ -37,6 +37,8 @@ spec: name: upload-common-env - configMapRef: name: zeek-env + - secretRef: + name: zeek-secret-env - configMapRef: name: zeek-offline-env livenessProbe: diff --git a/kubernetes/11-file-monitor.yml b/kubernetes/11-file-monitor.yml index 9606cc927..503302dc5 100644 --- a/kubernetes/11-file-monitor.yml +++ b/kubernetes/11-file-monitor.yml @@ -51,6 +51,8 @@ spec: name: ssl-env - configMapRef: name: zeek-env + - secretRef: + name: zeek-secret-env env: - name: VIRTUAL_HOST value: "file-monitor.malcolm.local" diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml index 74447c240..73bef36aa 100644 --- a/kubernetes/12-filebeat.yml +++ b/kubernetes/12-filebeat.yml @@ -68,10 +68,10 @@ spec: volumeMounts: - mountPath: /var/local/ca-trust/configmap name: filebeat-var-local-catrust-volume - - mountPath: /var/local/curlrc/configmap - name: filebeat-opensearch-curlrc-volume - - mountPath: /certs/configmap - name: filebeat-certs-volume + - mountPath: /var/local/curlrc/secretmap + name: filebeat-opensearch-curlrc-secret-volume + - mountPath: /certs/secretmap + name: filebeat-certs-secret-volume - mountPath: "/zeek" name: filebeat-zeek-volume - mountPath: "/suricata" @@ -83,12 +83,12 @@ spec: - name: filebeat-var-local-catrust-volume configMap: name: var-local-catrust - - name: filebeat-opensearch-curlrc-volume - configMap: - name: opensearch-curlrc - - name: filebeat-certs-volume - configMap: - name: filebeat-certs + - name: filebeat-opensearch-curlrc-secret-volume + secret: + secretName: opensearch-curlrc + - name: filebeat-certs-secret-volume + secret: + secretName: filebeat-certs - name: filebeat-zeek-volume persistentVolumeClaim: claimName: zeek-claim diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index 255eb4d78..0a003523b 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -77,6 +77,8 @@ spec: name: netbox-common-env - configMapRef: name: netbox-env + - secretRef: + name: netbox-secret-env - configMapRef: name: beats-common-env - configMapRef: @@ -96,14 +98,14 @@ spec: volumeMounts: - mountPath: /var/local/ca-trust/configmap name: logstash-var-local-catrust-volume - - mountPath: /var/local/curlrc/configmap - name: logstash-opensearch-curlrc-volume - - mountPath: /certs/configmap - name: logstash-certs-volume + - mountPath: /var/local/curlrc/secretmap + name: logstash-opensearch-curlrc-secret-volume + - mountPath: /certs/secretmap + name: logstash-certs-secret-volume - mountPath: /etc/configmap name: logstash-maps-volume - - name: logstash-keystore-default-volume - mountPath: /usr/share/logstash/config/bootstrap/configmap + - name: logstash-keystore-default-secret-volume + mountPath: /usr/share/logstash/config/bootstrap/secretmap - name: logstash-config-persist-volume mountPath: /usr/share/logstash/config/persist subPath: "logstash" @@ -111,18 +113,18 @@ spec: - name: logstash-var-local-catrust-volume configMap: name: var-local-catrust - - name: logstash-opensearch-curlrc-volume - configMap: - name: opensearch-curlrc - - name: logstash-certs-volume - configMap: - name: logstash-certs + - name: logstash-opensearch-curlrc-secret-volume + secret: + secretName: opensearch-curlrc + - name: logstash-certs-secret-volume + secret: + secretName: logstash-certs - name: logstash-maps-volume configMap: name: logstash-maps - - name: logstash-keystore-default-volume - configMap: - name: logstash-keystore + - name: logstash-keystore-default-secret-volume + secret: + secretName: logstash-keystore - name: logstash-config-persist-volume persistentVolumeClaim: claimName: config-claim \ No newline at end of file diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index 173005739..575a9f0f9 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -47,7 +47,7 @@ spec: name: ssl-env - configMapRef: name: netbox-common-env - - configMapRef: + - secretRef: name: netbox-redis-env env: - name: VIRTUAL_HOST diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index 31e5b21b3..959aae61b 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -47,7 +47,7 @@ spec: name: ssl-env - configMapRef: name: netbox-common-env - - configMapRef: + - secretRef: name: netbox-redis-cache-env env: - name: VIRTUAL_HOST diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index e719d1f5a..7f86bb314 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -45,7 +45,7 @@ spec: name: ssl-env - configMapRef: name: netbox-common-env - - configMapRef: + - secretRef: name: netbox-postgres-env env: - name: VIRTUAL_HOST diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index 67abd4a09..a12fbf112 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -59,6 +59,8 @@ spec: name: netbox-common-env - configMapRef: name: netbox-env + - secretRef: + name: netbox-secret-env env: - name: VIRTUAL_HOST value: "netbox.malcolm.local" diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml index 0a87e3872..2fa785ebf 100644 --- a/kubernetes/19-htadmin.yml +++ b/kubernetes/19-htadmin.yml @@ -56,6 +56,8 @@ spec: subPath: "auth" - mountPath: /var/www/htadmin/default/configmap name: htadmin-config-default-volume + - mountPath: /var/www/htadmin/default/secretmap + name: htadmin-config-default-secret-volume - mountPath: /var/www/htadmin/config name: htadmin-config-volume subPath: "htadmin" @@ -69,4 +71,7 @@ spec: - name: htadmin-config-default-volume configMap: name: htadmin-config + - name: htadmin-config-default-secret-volume + secret: + secretName: htadmin-config diff --git a/kubernetes/21-zeek-live.yml b/kubernetes/21-zeek-live.yml index 2f8c6b03f..23f0c7a2d 100644 --- a/kubernetes/21-zeek-live.yml +++ b/kubernetes/21-zeek-live.yml @@ -37,6 +37,8 @@ spec: name: upload-common-env - configMapRef: name: zeek-env + - secretRef: + name: zeek-secret-env - configMapRef: name: zeek-live-env - configMapRef: diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index 72ebc014f..4b0b544d3 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -73,19 +73,21 @@ spec: volumeMounts: - name: nginx-etc-nginx-volume mountPath: /etc/nginx/configmap + - name: nginx-etc-nginx-secret-volume + mountPath: /etc/nginx/secretmap - name: nginx-var-local-catrust-volume mountPath: /var/local/ca-trust/configmap - - name: nginx-etc-nginx-certs-volume - mountPath: /etc/nginx/certs/configmap + - name: nginx-etc-nginx-certs-secret-volume + mountPath: /etc/nginx/certs/secretmap - name: nginx-etc-nginx-certs-pem-volume mountPath: /etc/nginx/dhparam/configmap - - name: nginx-opensearch-curlrc-volume - mountPath: /var/local/curlrc/configmap + - name: nginx-opensearch-curlrc-secret-volume + mountPath: /var/local/curlrc/secretmap - name: nginx-etc-auth-volume mountPath: /etc/nginx/auth subPath: "auth" - - name: nginx-etc-auth-default-volume - mountPath: /tmp/auth/default/configmap + - name: nginx-etc-auth-default-secret-volume + mountPath: /tmp/auth/default/secretmap - name: nginx-runtime-logs-volume mountPath: /var/log/nginx subPath: "nginx" @@ -93,24 +95,27 @@ spec: - name: nginx-etc-nginx-volume configMap: name: etc-nginx + - name: nginx-etc-nginx-secret-volume + secret: + secretName: etc-nginx - name: nginx-var-local-catrust-volume configMap: name: var-local-catrust - - name: nginx-etc-nginx-certs-volume - configMap: - name: etc-nginx-certs + - name: nginx-etc-nginx-certs-secret-volume + secret: + secretName: etc-nginx-certs - name: nginx-etc-nginx-certs-pem-volume configMap: name: etc-nginx-certs-pem - - name: nginx-opensearch-curlrc-volume - configMap: - name: opensearch-curlrc + - name: nginx-opensearch-curlrc-secret-volume + secret: + secretName: opensearch-curlrc - name: nginx-etc-auth-volume persistentVolumeClaim: claimName: config-claim - - name: nginx-etc-auth-default-volume - configMap: - name: etc-nginx-auth + - name: nginx-etc-auth-default-secret-volume + secret: + secretName: etc-nginx-auth - name: nginx-runtime-logs-volume persistentVolumeClaim: claimName: runtime-logs-claim \ No newline at end of file diff --git a/scripts/control.py b/scripts/control.py index 6a9129d3b..d82df1738 100755 --- a/scripts/control.py +++ b/scripts/control.py @@ -1119,7 +1119,9 @@ def authSetup(wipe=False): 'netbox', "(Re)generate internal passwords for NetBox", False, - not os.path.isfile(os.path.join(MalcolmPath, os.path.join('netbox', os.path.join('env', 'netbox.env')))), + not os.path.isfile( + os.path.join(MalcolmPath, os.path.join('netbox', os.path.join('env', 'netbox-secret.env'))) + ), ), ( 'txfwcerts', @@ -1190,6 +1192,7 @@ def authSetup(wipe=False): ) f.write(f'MALCOLM_USERNAME={username}\n') f.write(f'MALCOLM_PASSWORD={b64encode(passwordEncrypted.encode()).decode("ascii")}\n') + f.write('K8S_SECRET=True\n') os.chmod(authEnvFile, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) # create or update the htpasswd file @@ -1627,20 +1630,23 @@ def authSetup(wipe=False): f.write('POSTGRES_DB=netbox\n') f.write(f'POSTGRES_PASSWORD={netboxPostGresPassword}\n') f.write('POSTGRES_USER=netbox\n') + f.write('K8S_SECRET=True\n') os.chmod('netbox-postgres.env', stat.S_IRUSR | stat.S_IWUSR) with open('netbox-redis-cache.env', 'w') as f: f.write(f'REDIS_PASSWORD={netboxRedisCachePassword}\n') + f.write('K8S_SECRET=True\n') os.chmod('netbox-redis-cache.env', stat.S_IRUSR | stat.S_IWUSR) with open('netbox-redis.env', 'w') as f: f.write(f'REDIS_PASSWORD={netboxRedisPassword}\n') + f.write('K8S_SECRET=True\n') os.chmod('netbox-redis.env', stat.S_IRUSR | stat.S_IWUSR) - if (not os.path.isfile('netbox.env')) and (os.path.isfile('netbox.env.example')): - shutil.copy2('netbox.env.example', 'netbox.env') + if (not os.path.isfile('netbox-secret.env')) and (os.path.isfile('netbox-secret.env.example')): + shutil.copy2('netbox-secret.env.example', 'netbox-secret.env') - with fileinput.FileInput('netbox.env', inplace=True, backup=None) as envFile: + with fileinput.FileInput('netbox-secret.env', inplace=True, backup=None) as envFile: for line in envFile: line = line.rstrip("\n") @@ -1680,10 +1686,16 @@ def authSetup(wipe=False): fr"\g<1>{netboxSuToken}", line, ) + elif line.startswith('K8S_SECRET'): + line = re.sub( + r'(SUPERUSER_API_TOKEN\s*=\s*)(\S+)', + fr"\g<1>True", + line, + ) print(line) - os.chmod('netbox.env', stat.S_IRUSR | stat.S_IWUSR) + os.chmod('netbox-secret.env', stat.S_IRUSR | stat.S_IWUSR) elif authItem[0] == 'txfwcerts': DisplayMessage( diff --git a/scripts/install.py b/scripts/install.py index e5e9ab45a..8afec8a11 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -1269,13 +1269,13 @@ def tweak_malcolm_runtime( ), # key for encrypted HTTP-served extracted files (' -> '' for escaping in YAML) EnvValue( - os.path.join(args.configDir, 'zeek.env'), + os.path.join(args.configDir, 'zeek-secret.env'), 'EXTRACTED_FILE_HTTP_SERVER_KEY', fileCarveHttpServeEncryptKey, ), # virustotal API key EnvValue( - os.path.join(args.configDir, 'zeek.env'), + os.path.join(args.configDir, 'zeek-secret.env'), 'VTOT_API2_KEY', vtotApiKey, ), diff --git a/scripts/malcolm_common.py b/scripts/malcolm_common.py index 94e3c9c55..8dfb84a3b 100644 --- a/scripts/malcolm_common.py +++ b/scripts/malcolm_common.py @@ -601,7 +601,7 @@ def MalcolmAuthFilesExist(configDir=None): and os.path.isfile(os.path.join(MalcolmPath, os.path.join('nginx', os.path.join('certs', 'cert.pem')))) and os.path.isfile(os.path.join(MalcolmPath, os.path.join('nginx', os.path.join('certs', 'key.pem')))) and os.path.isfile(os.path.join(MalcolmPath, os.path.join('htadmin', 'config.ini'))) - and os.path.isfile(os.path.join(configDirToCheck, 'netbox.env')) + and os.path.isfile(os.path.join(configDirToCheck, 'netbox-secret.env')) and os.path.isfile(os.path.join(configDirToCheck, 'netbox-postgres.env')) and os.path.isfile(os.path.join(configDirToCheck, 'netbox-redis-cache.env')) and os.path.isfile(os.path.join(configDirToCheck, 'netbox-redis.env')) diff --git a/scripts/malcolm_kubernetes.py b/scripts/malcolm_kubernetes.py index 73dc83158..9dc88a517 100644 --- a/scripts/malcolm_kubernetes.py +++ b/scripts/malcolm_kubernetes.py @@ -34,63 +34,128 @@ ################################################################################################### MALCOLM_IMAGE_PREFIX = 'ghcr.io/idaholab/malcolm/' +MALCOLM_DOTFILE_SECRET_KEY = 'K8S_SECRET' + MALCOLM_CONFIGMAPS = { 'etc-nginx': [ - os.path.join(MalcolmPath, os.path.join('nginx', 'nginx_ldap.conf')), - os.path.join(MalcolmPath, os.path.join('nginx', 'nginx.conf')), + { + 'secret': True, + 'path': os.path.join(MalcolmPath, os.path.join('nginx', 'nginx_ldap.conf')), + }, + { + 'secret': False, + 'path': os.path.join(MalcolmPath, os.path.join('nginx', 'nginx.conf')), + }, ], 'var-local-catrust': [ - os.path.join(MalcolmPath, os.path.join('nginx', 'ca-trust')), + { + 'secret': False, + 'path': os.path.join(MalcolmPath, os.path.join('nginx', 'ca-trust')), + }, ], 'etc-nginx-certs': [ - os.path.join(MalcolmPath, os.path.join('nginx', 'certs')), + { + 'secret': True, + 'path': os.path.join(MalcolmPath, os.path.join('nginx', 'certs')), + }, ], 'etc-nginx-certs-pem': [ - os.path.join(MalcolmPath, os.path.join(os.path.join('nginx', 'certs'), 'dhparam.pem')), + { + 'secret': False, + 'path': os.path.join(MalcolmPath, os.path.join(os.path.join('nginx', 'certs'), 'dhparam.pem')), + }, ], 'etc-nginx-auth': [ - os.path.join(MalcolmPath, os.path.join('nginx', 'htpasswd')), + { + 'secret': True, + 'path': os.path.join(MalcolmPath, os.path.join('nginx', 'htpasswd')), + }, ], 'opensearch-curlrc': [ - os.path.join(MalcolmPath, '.opensearch.primary.curlrc'), - os.path.join(MalcolmPath, '.opensearch.secondary.curlrc'), + { + 'secret': True, + 'path': os.path.join(MalcolmPath, '.opensearch.primary.curlrc'), + }, + { + 'secret': True, + 'path': os.path.join(MalcolmPath, '.opensearch.secondary.curlrc'), + }, ], 'opensearch-keystore': [ - os.path.join(MalcolmPath, os.path.join('opensearch', 'opensearch.keystore')), + { + 'secret': True, + 'path': os.path.join(MalcolmPath, os.path.join('opensearch', 'opensearch.keystore')), + }, ], 'logstash-certs': [ - os.path.join(MalcolmPath, os.path.join('logstash', 'certs')), + { + 'secret': True, + 'path': os.path.join(MalcolmPath, os.path.join('logstash', 'certs')), + }, ], 'logstash-maps': [ - os.path.join(MalcolmPath, os.path.join('logstash', 'maps')), + { + 'secret': False, + 'path': os.path.join(MalcolmPath, os.path.join('logstash', 'maps')), + }, ], 'logstash-keystore': [ - os.path.join(MalcolmPath, os.path.join('logstash', 'logstash.keystore')), + { + 'secret': True, + 'path': os.path.join(MalcolmPath, os.path.join('logstash', 'logstash.keystore')), + }, ], 'yara-rules': [ - os.path.join(MalcolmPath, os.path.join('yara', 'rules')), + { + 'secret': False, + 'path': os.path.join(MalcolmPath, os.path.join('yara', 'rules')), + }, ], 'suricata-rules': [ - os.path.join(MalcolmPath, os.path.join('suricata', 'rules')), + { + 'secret': False, + 'path': os.path.join(MalcolmPath, os.path.join('suricata', 'rules')), + }, ], 'filebeat-certs': [ - os.path.join(MalcolmPath, os.path.join('filebeat', 'certs')), + { + 'secret': True, + 'path': os.path.join(MalcolmPath, os.path.join('filebeat', 'certs')), + }, ], 'netbox-netmap-json': [ - 'net-map.json', + { + 'secret': False, + 'path': os.path.join(MalcolmPath, 'net-map.json'), + }, ], 'netbox-config': [ - os.path.join(MalcolmPath, os.path.join(os.path.join('netbox', 'config'), 'configuration')), + { + 'secret': False, + 'path': os.path.join(MalcolmPath, os.path.join(os.path.join('netbox', 'config'), 'configuration')), + }, ], 'netbox-reports': [ - os.path.join(MalcolmPath, os.path.join(os.path.join('netbox', 'config'), 'reports')), + { + 'secret': False, + 'path': os.path.join(MalcolmPath, os.path.join(os.path.join('netbox', 'config'), 'reports')), + }, ], 'netbox-scripts': [ - os.path.join(MalcolmPath, os.path.join(os.path.join('netbox', 'config'), 'scripts')), + { + 'secret': False, + 'path': os.path.join(MalcolmPath, os.path.join(os.path.join('netbox', 'config'), 'scripts')), + }, ], 'htadmin-config': [ - os.path.join(MalcolmPath, os.path.join('htadmin', 'config.ini')), - os.path.join(MalcolmPath, os.path.join('htadmin', 'metadata')), + { + 'secret': False, + 'path': os.path.join(MalcolmPath, os.path.join('htadmin', 'config.ini')), + }, + { + 'secret': True, + 'path': os.path.join(MalcolmPath, os.path.join('htadmin', 'metadata')), + }, ], } @@ -584,85 +649,107 @@ def StartMalcolm(namespace, malcolmPath, configPath): # create configmaps from files results_dict['create_namespaced_config_map']['result'] = dict() + results_dict['create_namespaced_secret']['result'] = dict() for configMapName, configMapFiles in MALCOLM_CONFIGMAPS.items(): - try: - dataMap = {} - binaryDataMap = {} - for fname in configMapFiles: - if os.path.isfile(fname): - contents = file_contents( - fname, - binary_fallback=True, - ) - if hasattr(contents, 'decode'): - binaryDataMap[os.path.basename(fname)] = base64.b64encode(contents).decode('utf-8') - else: - dataMap[os.path.basename(fname)] = contents - elif os.path.isdir(fname): - for subfname in glob.iglob(os.path.join(os.path.join(fname, '**'), '*'), recursive=True): - if os.path.isfile(subfname): + for isSecret in (True, False): + resultsEntry = 'create_namespaced_secret' if isSecret else 'create_namespaced_config_map' + mapFiles = [x['path'] for x in configMapFiles if (x.get('secret', False) is isSecret)] + if mapFiles: + try: + dataMap = {} + binaryDataMap = {} + for fname in mapFiles: + if os.path.isfile(fname): contents = file_contents( - subfname, + fname, binary_fallback=True, ) if hasattr(contents, 'decode'): - binaryDataMap[os.path.basename(subfname)] = base64.b64encode(contents).decode( - 'utf-8' - ) + binaryDataMap[os.path.basename(fname)] = base64.b64encode(contents).decode('utf-8') else: - dataMap[os.path.basename(subfname)] = contents - results_dict['create_namespaced_config_map']['result'][ - configMapName - ] = client.create_namespaced_config_map( - namespace=namespace, - body=kubeImported.client.V1ConfigMap( - metadata=kubeImported.client.V1ObjectMeta( + dataMap[os.path.basename(fname)] = contents + elif os.path.isdir(fname): + for subfname in glob.iglob( + os.path.join(os.path.join(fname, '**'), '*'), recursive=True + ): + if os.path.isfile(subfname): + contents = file_contents( + subfname, + binary_fallback=True, + ) + if hasattr(contents, 'decode'): + binaryDataMap[os.path.basename(subfname)] = base64.b64encode( + contents + ).decode('utf-8') + else: + dataMap[os.path.basename(subfname)] = contents + metadata = kubeImported.client.V1ObjectMeta( name=configMapName, namespace=namespace, - ), - data=dataMap if dataMap else {}, - binary_data=binaryDataMap if binaryDataMap else {}, - ), - ).metadata - except kubeImported.client.rest.ApiException as x: - if x.status != 409: - if 'error' not in results_dict['create_namespaced_config_map']: - results_dict['create_namespaced_config_map']['error'] = dict() - results_dict['create_namespaced_config_map']['error'][ - os.path.basename(configMapName) - ] = LoadStrIfJson(str(x)) - if not results_dict['create_namespaced_config_map']['error'][os.path.basename(configMapName)]: - results_dict['create_namespaced_config_map']['error'][os.path.basename(configMapName)] = str(x) - - # create configmaps from .env files + ) + if isSecret: + results_dict[resultsEntry]['result'][configMapName] = client.create_namespaced_secret( + namespace=namespace, + body=kubeImported.client.V1Secret( + metadata=metadata, + string_data=dataMap if dataMap else {}, + data=binaryDataMap if binaryDataMap else {}, + ), + ).metadata + else: + results_dict[resultsEntry]['result'][configMapName] = client.create_namespaced_config_map( + namespace=namespace, + body=kubeImported.client.V1ConfigMap( + metadata=metadata, + data=dataMap if dataMap else {}, + binary_data=binaryDataMap if binaryDataMap else {}, + ), + ).metadata + except kubeImported.client.rest.ApiException as x: + if x.status != 409: + if 'error' not in results_dict[resultsEntry]: + results_dict[resultsEntry]['error'] = dict() + results_dict[resultsEntry]['error'][os.path.basename(configMapName)] = LoadStrIfJson(str(x)) + if not results_dict[resultsEntry]['error'][os.path.basename(configMapName)]: + results_dict[resultsEntry]['error'][os.path.basename(configMapName)] = str(x) + + # create configmaps (or secrets, given a K8S_SECRET key) from .env files results_dict['create_namespaced_config_map_from_env_file']['result'] = dict() + results_dict['create_namespaced_secret_from_env_file']['result'] = dict() for envFileName in glob.iglob(os.path.join(configPath, '*.env'), recursive=False): if os.path.isfile(envFileName): try: - configMap = kubeImported.client.V1ConfigMap() - configMap.metadata = kubeImported.client.V1ObjectMeta( + values = dotenvImported.dotenv_values(envFileName) + isSecret = val2bool(values.pop(MALCOLM_DOTFILE_SECRET_KEY, False)) + metadata = kubeImported.client.V1ObjectMeta( name=remove_suffix(os.path.basename(envFileName), '.env') + '-env' ) - configMap.data = dotenvImported.dotenv_values(envFileName) - results_dict['create_namespaced_config_map_from_env_file']['result'][ - configMap.metadata.name - ] = client.create_namespaced_config_map( - namespace=namespace, - body=configMap, - ).metadata + if isSecret: + resultsEntry = 'create_namespaced_secret_from_env_file' + results_dict[resultsEntry]['result'][metadata.name] = client.create_namespaced_secret( + namespace=namespace, + body=kubeImported.client.V1Secret( + metadata=metadata, + string_data=values if values else {}, + ), + ).metadata + else: + resultsEntry = 'create_namespaced_config_map_from_env_file' + results_dict[resultsEntry]['result'][metadata.name] = client.create_namespaced_config_map( + namespace=namespace, + body=kubeImported.client.V1ConfigMap( + metadata=metadata, + data=values if values else {}, + ), + ).metadata + except kubeImported.client.rest.ApiException as x: if x.status != 409: - if 'error' not in results_dict['create_namespaced_config_map_from_env_file']: - results_dict['create_namespaced_config_map_from_env_file']['error'] = dict() - results_dict['create_namespaced_config_map_from_env_file']['error'][ - os.path.basename(envFileName) - ] = LoadStrIfJson(str(x)) - if not results_dict['create_namespaced_config_map_from_env_file']['error'][ - os.path.basename(envFileName) - ]: - results_dict['create_namespaced_config_map_from_env_file']['error'][ - os.path.basename(envFileName) - ] = str(x) + if 'error' not in results_dict[resultsEntry]: + results_dict[resultsEntry]['error'] = dict() + results_dict[resultsEntry]['error'][os.path.basename(envFileName)] = LoadStrIfJson(str(x)) + if not results_dict[resultsEntry]['error'][os.path.basename(envFileName)]: + results_dict[resultsEntry]['error'][os.path.basename(envFileName)] = str(x) # apply manifests results_dict['create_from_yaml']['result'] = dict() From b590f34aa695da986b39942e68fe5239d2255905 Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 27 Apr 2023 13:54:24 -0600 Subject: [PATCH 226/239] change image references for kubernetes (only in my development branch) --- kubernetes/02-opensearch.yml | 2 +- kubernetes/03-dashboards.yml | 2 +- kubernetes/04-upload.yml | 2 +- kubernetes/05-pcap-monitor.yml | 2 +- kubernetes/06-arkime.yml | 2 +- kubernetes/07-api.yml | 2 +- kubernetes/08-dashboards-helper.yml | 2 +- kubernetes/09-zeek.yml | 2 +- kubernetes/10-suricata.yml | 2 +- kubernetes/11-file-monitor.yml | 2 +- kubernetes/12-filebeat.yml | 2 +- kubernetes/13-logstash.yml | 2 +- kubernetes/15-netbox-redis.yml | 2 +- kubernetes/16-netbox-redis-cache.yml | 2 +- kubernetes/17-netbox-postgres.yml | 2 +- kubernetes/18-netbox.yml | 2 +- kubernetes/19-htadmin.yml | 2 +- kubernetes/20-pcap-capture.yml | 2 +- kubernetes/21-zeek-live.yml | 2 +- kubernetes/22-suricata-live.yml | 2 +- kubernetes/23-freq.yml | 2 +- kubernetes/99-nginx-proxy.yml | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index 976a6988c..e2f70e54c 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: opensearch-container - image: ghcr.io/idaholab/malcolm/opensearch:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/opensearch:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/03-dashboards.yml b/kubernetes/03-dashboards.yml index 4a2043177..cfbb8b422 100644 --- a/kubernetes/03-dashboards.yml +++ b/kubernetes/03-dashboards.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: dashboards-container - image: ghcr.io/idaholab/malcolm/dashboards:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/dashboards:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 3e47c9d1b..19915597c 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -34,7 +34,7 @@ spec: spec: containers: - name: upload-container - image: ghcr.io/idaholab/malcolm/file-upload:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/file-upload:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml index eb649186e..b8b76084b 100644 --- a/kubernetes/05-pcap-monitor.yml +++ b/kubernetes/05-pcap-monitor.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: pcap-monitor-container - image: ghcr.io/idaholab/malcolm/pcap-monitor:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/pcap-monitor:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index 0d5a3e70c..ef24eb44b 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: arkime-container - image: ghcr.io/idaholab/malcolm/arkime:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/arkime:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/07-api.yml b/kubernetes/07-api.yml index a93795ec5..3f611da69 100644 --- a/kubernetes/07-api.yml +++ b/kubernetes/07-api.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: api-container - image: ghcr.io/idaholab/malcolm/api:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/api:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/08-dashboards-helper.yml b/kubernetes/08-dashboards-helper.yml index a0b26a4e0..529647210 100644 --- a/kubernetes/08-dashboards-helper.yml +++ b/kubernetes/08-dashboards-helper.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: dashboards-helper-container - image: ghcr.io/idaholab/malcolm/dashboards-helper:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/dashboards-helper:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/09-zeek.yml b/kubernetes/09-zeek.yml index 4c505c03e..f7fd93b7f 100644 --- a/kubernetes/09-zeek.yml +++ b/kubernetes/09-zeek.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: zeek-offline-container - image: ghcr.io/idaholab/malcolm/zeek:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/zeek:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/10-suricata.yml b/kubernetes/10-suricata.yml index 40024e812..da525b557 100644 --- a/kubernetes/10-suricata.yml +++ b/kubernetes/10-suricata.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: suricata-offline-container - image: ghcr.io/idaholab/malcolm/suricata:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/suricata:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/11-file-monitor.yml b/kubernetes/11-file-monitor.yml index 503302dc5..39a29b65d 100644 --- a/kubernetes/11-file-monitor.yml +++ b/kubernetes/11-file-monitor.yml @@ -33,7 +33,7 @@ spec: spec: containers: - name: file-monitor-container - image: ghcr.io/idaholab/malcolm/file-monitor:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/file-monitor:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml index 73bef36aa..7676193c0 100644 --- a/kubernetes/12-filebeat.yml +++ b/kubernetes/12-filebeat.yml @@ -31,7 +31,7 @@ spec: spec: containers: - name: filebeat-container - image: ghcr.io/idaholab/malcolm/filebeat-oss:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/filebeat-oss:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index 0a003523b..081c47ed3 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -47,7 +47,7 @@ spec: # topologyKey: "kubernetes.io/hostname" containers: - name: logstash-container - image: ghcr.io/idaholab/malcolm/logstash-oss:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/logstash-oss:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index 575a9f0f9..984110449 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: netbox-redis-container - image: ghcr.io/idaholab/malcolm/redis:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/redis:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index 959aae61b..22f1be5a4 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: netbox-redis-cache-container - image: ghcr.io/idaholab/malcolm/redis:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/redis:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index 7f86bb314..1497c1bea 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: netbox-postgres-container - image: ghcr.io/idaholab/malcolm/postgresql:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/postgresql:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index a12fbf112..5a613fc8e 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -36,7 +36,7 @@ spec: spec: containers: - name: netbox-container - image: ghcr.io/idaholab/malcolm/netbox:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/netbox:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml index 2fa785ebf..716a46363 100644 --- a/kubernetes/19-htadmin.yml +++ b/kubernetes/19-htadmin.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: htadmin-container - image: ghcr.io/idaholab/malcolm/htadmin:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/htadmin:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/20-pcap-capture.yml b/kubernetes/20-pcap-capture.yml index 53a9f971e..63360e5a4 100644 --- a/kubernetes/20-pcap-capture.yml +++ b/kubernetes/20-pcap-capture.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: pcap-capture-container - image: ghcr.io/idaholab/malcolm/pcap-capture:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/pcap-capture:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/21-zeek-live.yml b/kubernetes/21-zeek-live.yml index 23f0c7a2d..3eb2cf92f 100644 --- a/kubernetes/21-zeek-live.yml +++ b/kubernetes/21-zeek-live.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: zeek-live-container - image: ghcr.io/idaholab/malcolm/zeek:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/zeek:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/22-suricata-live.yml b/kubernetes/22-suricata-live.yml index 73fb72532..fe0ee1d21 100644 --- a/kubernetes/22-suricata-live.yml +++ b/kubernetes/22-suricata-live.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: suricata-live-container - image: ghcr.io/idaholab/malcolm/suricata:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/suricata:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/23-freq.yml b/kubernetes/23-freq.yml index a57617d05..b9dc580df 100644 --- a/kubernetes/23-freq.yml +++ b/kubernetes/23-freq.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: freq-container - image: ghcr.io/idaholab/malcolm/freq:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/freq:development imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index 4b0b544d3..5c2f49b4c 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -37,7 +37,7 @@ spec: spec: containers: - name: nginx-proxy-container - image: ghcr.io/idaholab/malcolm/nginx-proxy:23.04.1 + image: ghcr.io/mmguero-dev/malcolm/nginx-proxy:development imagePullPolicy: Always stdin: false tty: true From b92bdb38ccab6c7b088b1ae84822751cf649ee4e Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 28 Apr 2023 10:03:17 -0600 Subject: [PATCH 227/239] ensure created .env files get correct permissions/ownership --- scripts/install.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/scripts/install.py b/scripts/install.py index 8afec8a11..48e1596b8 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -970,12 +970,34 @@ def tweak_malcolm_runtime( shutil.copy2(defaultEnvExampleFile, args.configDir) # if a specific config/*.env file doesn't exist, use the *.example.env files as defaults - envExampleFiles = glob.glob(os.path.join(args.configDir, '*.env.example')) - for envExampleFile in envExampleFiles: + for envExampleFile in glob.glob(os.path.join(args.configDir, '*.env.example')): envFile = envExampleFile[: -len('.example')] if not os.path.isfile(envFile): shutil.copyfile(envExampleFile, envFile) + # change ownership of .envs file to match puid/pgid + if ( + ((self.platform == PLATFORM_LINUX) or (self.platform == PLATFORM_MAC)) + and (self.scriptUser == "root") + and (getpwuid(os.stat(args.configDir).st_uid).pw_name == self.scriptUser) + ): + if args.debug: + eprint(f"Setting permissions of {args.configDir} to {puid}:{pgid}") + os.chown(args.configDir, int(puid), int(pgid)) + envFiles = [] + for exts in ('*.env', '*.env.example'): + envFiles.extend(glob.glob(os.path.join(args.configDir, exts))) + for envFile in envFiles: + if ( + ((self.platform == PLATFORM_LINUX) or (self.platform == PLATFORM_MAC)) + and (self.scriptUser == "root") + and (getpwuid(os.stat(envFile).st_uid).pw_name == self.scriptUser) + ): + if args.debug: + eprint(f"Setting permissions of {envFile} to {puid}:{pgid}") + os.chown(envFile, int(puid), int(pgid)) + + # define environment variables to be set in .env files EnvValue = namedtuple("EnvValue", ["envFile", "key", "value"], rename=False) EnvValues = [ From 7796a60ce9155721b2ea4f8e9f895e650b773350 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 28 Apr 2023 10:17:52 -0600 Subject: [PATCH 228/239] put CSRF_TRUSTED_ORIGINS in correct .env file --- config/netbox-common.env.example | 3 --- config/netbox.env.example | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/netbox-common.env.example b/config/netbox-common.env.example index aff0c64fa..693425208 100644 --- a/config/netbox-common.env.example +++ b/config/netbox-common.env.example @@ -10,6 +10,3 @@ NETBOX_REDIS_DISABLED=true NETBOX_REDIS_CACHE_DISABLED=true # Whether or not to periodically query network traffic metadata and use it to populate NetBox NETBOX_CRON=false -# If using the NetBox interface to create API tokens, set this -# (see https://docs.djangoproject.com/en/4.1/ref/settings/#csrf-trusted-origins) -# CSRF_TRUSTED_ORIGINS=https://malcolm.example.org \ No newline at end of file diff --git a/config/netbox.env.example b/config/netbox.env.example index ddafba06f..0a8a0be98 100644 --- a/config/netbox.env.example +++ b/config/netbox.env.example @@ -1,4 +1,7 @@ CORS_ORIGIN_ALLOW_ALL=True +# If using the NetBox interface to create API tokens, set this +# (see https://docs.djangoproject.com/en/4.1/ref/settings/#csrf-trusted-origins) +# CSRF_TRUSTED_ORIGINS=https://malcolm.example.org CSRF_TRUSTED_ORIGINS=http://* https://* BASE_PATH=netbox REMOTE_AUTH_ENABLED=True From 314084e654658e5251b118f0bb9854175c8d76c8 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 28 Apr 2023 12:56:00 -0600 Subject: [PATCH 229/239] Added a missing script into the ISOs --- .github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml | 1 + malcolm-iso/build.sh | 1 + sensor-iso/build_via_vagrant.sh | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml b/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml index ef45d02cb..bd68526d0 100644 --- a/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml +++ b/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml @@ -91,6 +91,7 @@ jobs: name: Build image run: | cp -r ./shared ./docs ./_config.yml ./_includes ./_layouts ./Gemfile ./README.md ./sensor-iso + cp ./scripts/malcolm_utils.py ./sensor-iso/shared/ cp ./scripts/documentation_build.sh ./sensor-iso/docs/ cp -r ./arkime/patch ./sensor-iso/shared/arkime_patch pushd ./sensor-iso diff --git a/malcolm-iso/build.sh b/malcolm-iso/build.sh index 590970043..9e02a58bc 100755 --- a/malcolm-iso/build.sh +++ b/malcolm-iso/build.sh @@ -183,6 +183,7 @@ if [ -d "$WORKDIR" ]; then # copy shared scripts and some branding stuff mkdir -p ./config/includes.chroot/usr/local/bin/ rsync -a "$SCRIPT_PATH/../shared/bin/" ./config/includes.chroot/usr/local/bin/ + cp "$SCRIPT_PATH/../scripts/malcolm_utils.py" ./config/includes.chroot/usr/local/bin/ chown -R root:root ./config/includes.chroot/usr/local/bin/ mkdir -p ./config/includes.chroot/usr/share/images/desktop-base/ diff --git a/sensor-iso/build_via_vagrant.sh b/sensor-iso/build_via_vagrant.sh index 3ea149d73..563f53c4d 100755 --- a/sensor-iso/build_via_vagrant.sh +++ b/sensor-iso/build_via_vagrant.sh @@ -86,6 +86,7 @@ cp -r "$SCRIPT_PATH"/../shared \ "$SCRIPT_PATH"/../Gemfile \ "$SCRIPT_PATH"/../README.md "$SCRIPT_PATH"/ cp "$SCRIPT_PATH"/../scripts/documentation_build.sh "$SCRIPT_PATH"/docs/ +cp "$SCRIPT_PATH"/../scripts/malcolm_utils.py "$SCRIPT_PATH"/shared/ YML_IMAGE_VERSION="$(grep -P "^\s+image:\s*malcolm" "$SCRIPT_PATH"/../docker-compose-standalone.yml | awk '{print $2}' | cut -d':' -f2 | uniq -c | sort -nr | awk '{print $2}' | head -n 1)" [[ -n $YML_IMAGE_VERSION ]] && echo "$YML_IMAGE_VERSION" > "$SCRIPT_PATH"/shared/version.txt From 29100c3104623a32bd759ca7dd31e9a0ca77b7f2 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 28 Apr 2023 18:45:24 -0600 Subject: [PATCH 230/239] Bump version for v23.05.0 release --- docker-compose-standalone.yml | 44 +++++++------- docker-compose.yml | 44 +++++++------- docs/download.md | 4 +- docs/hedgehog-iso-build.md | 2 +- docs/kubernetes.md | 88 ++++++++++++++-------------- docs/malcolm-iso.md | 2 +- docs/quickstart.md | 38 ++++++------ docs/ubuntu-install-example.md | 38 ++++++------ kubernetes/02-opensearch.yml | 2 +- kubernetes/03-dashboards.yml | 2 +- kubernetes/04-upload.yml | 2 +- kubernetes/05-pcap-monitor.yml | 2 +- kubernetes/06-arkime.yml | 2 +- kubernetes/07-api.yml | 2 +- kubernetes/08-dashboards-helper.yml | 2 +- kubernetes/09-zeek.yml | 2 +- kubernetes/10-suricata.yml | 2 +- kubernetes/11-file-monitor.yml | 2 +- kubernetes/12-filebeat.yml | 2 +- kubernetes/13-logstash.yml | 2 +- kubernetes/15-netbox-redis.yml | 2 +- kubernetes/16-netbox-redis-cache.yml | 2 +- kubernetes/17-netbox-postgres.yml | 2 +- kubernetes/18-netbox.yml | 2 +- kubernetes/19-htadmin.yml | 2 +- kubernetes/20-pcap-capture.yml | 2 +- kubernetes/21-zeek-live.yml | 2 +- kubernetes/22-suricata-live.yml | 2 +- kubernetes/23-freq.yml | 2 +- kubernetes/99-nginx-proxy.yml | 2 +- 30 files changed, 152 insertions(+), 152 deletions(-) diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 7c9367c9b..f1efc89c0 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -4,7 +4,7 @@ version: '3.7' services: opensearch: - image: ghcr.io/idaholab/malcolm/opensearch:23.04.1 + image: ghcr.io/idaholab/malcolm/opensearch:23.05.0 restart: "no" stdin_open: false tty: true @@ -37,7 +37,7 @@ services: retries: 3 start_period: 180s dashboards-helper: - image: ghcr.io/idaholab/malcolm/dashboards-helper:23.04.1 + image: ghcr.io/idaholab/malcolm/dashboards-helper:23.05.0 restart: "no" stdin_open: false tty: true @@ -64,7 +64,7 @@ services: retries: 3 start_period: 30s dashboards: - image: ghcr.io/idaholab/malcolm/dashboards:23.04.1 + image: ghcr.io/idaholab/malcolm/dashboards:23.05.0 restart: "no" stdin_open: false tty: true @@ -90,7 +90,7 @@ services: retries: 3 start_period: 210s logstash: - image: ghcr.io/idaholab/malcolm/logstash-oss:23.04.1 + image: ghcr.io/idaholab/malcolm/logstash-oss:23.05.0 restart: "no" stdin_open: false tty: true @@ -132,7 +132,7 @@ services: retries: 3 start_period: 600s filebeat: - image: ghcr.io/idaholab/malcolm/filebeat-oss:23.04.1 + image: ghcr.io/idaholab/malcolm/filebeat-oss:23.05.0 restart: "no" stdin_open: false tty: true @@ -167,7 +167,7 @@ services: retries: 3 start_period: 60s arkime: - image: ghcr.io/idaholab/malcolm/arkime:23.04.1 + image: ghcr.io/idaholab/malcolm/arkime:23.05.0 restart: "no" stdin_open: false tty: true @@ -203,7 +203,7 @@ services: retries: 3 start_period: 210s zeek: - image: ghcr.io/idaholab/malcolm/zeek:23.04.1 + image: ghcr.io/idaholab/malcolm/zeek:23.05.0 restart: "no" stdin_open: false tty: true @@ -241,7 +241,7 @@ services: retries: 3 start_period: 60s zeek-live: - image: ghcr.io/idaholab/malcolm/zeek:23.04.1 + image: ghcr.io/idaholab/malcolm/zeek:23.05.0 restart: "no" stdin_open: false tty: true @@ -269,7 +269,7 @@ services: - ./zeek-logs/extract_files:/zeek/extract_files - ./zeek/intel:/opt/zeek/share/zeek/site/intel suricata: - image: ghcr.io/idaholab/malcolm/suricata:23.04.1 + image: ghcr.io/idaholab/malcolm/suricata:23.05.0 restart: "no" stdin_open: false tty: true @@ -305,7 +305,7 @@ services: retries: 3 start_period: 120s suricata-live: - image: ghcr.io/idaholab/malcolm/suricata:23.04.1 + image: ghcr.io/idaholab/malcolm/suricata:23.05.0 restart: "no" stdin_open: false tty: true @@ -331,7 +331,7 @@ services: - ./suricata-logs:/var/log/suricata - ./suricata/rules:/opt/suricata/rules:ro file-monitor: - image: ghcr.io/idaholab/malcolm/file-monitor:23.04.1 + image: ghcr.io/idaholab/malcolm/file-monitor:23.05.0 restart: "no" stdin_open: false tty: true @@ -357,7 +357,7 @@ services: retries: 3 start_period: 60s pcap-capture: - image: ghcr.io/idaholab/malcolm/pcap-capture:23.04.1 + image: ghcr.io/idaholab/malcolm/pcap-capture:23.05.0 restart: "no" stdin_open: false tty: true @@ -379,7 +379,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/pcap pcap-monitor: - image: ghcr.io/idaholab/malcolm/pcap-monitor:23.04.1 + image: ghcr.io/idaholab/malcolm/pcap-monitor:23.05.0 restart: "no" stdin_open: false tty: true @@ -405,7 +405,7 @@ services: retries: 3 start_period: 90s upload: - image: ghcr.io/idaholab/malcolm/file-upload:23.04.1 + image: ghcr.io/idaholab/malcolm/file-upload:23.05.0 restart: "no" stdin_open: false tty: true @@ -433,7 +433,7 @@ services: retries: 3 start_period: 60s htadmin: - image: ghcr.io/idaholab/malcolm/htadmin:23.04.1 + image: ghcr.io/idaholab/malcolm/htadmin:23.05.0 restart: "no" stdin_open: false tty: true @@ -458,7 +458,7 @@ services: retries: 3 start_period: 60s freq: - image: ghcr.io/idaholab/malcolm/freq:23.04.1 + image: ghcr.io/idaholab/malcolm/freq:23.05.0 restart: "no" stdin_open: false tty: true @@ -480,7 +480,7 @@ services: retries: 3 start_period: 60s netbox: - image: ghcr.io/idaholab/malcolm/netbox:23.04.1 + image: ghcr.io/idaholab/malcolm/netbox:23.05.0 restart: "no" stdin_open: false tty: true @@ -513,7 +513,7 @@ services: retries: 3 start_period: 120s netbox-postgres: - image: ghcr.io/idaholab/malcolm/postgresql:23.04.1 + image: ghcr.io/idaholab/malcolm/postgresql:23.05.0 restart: "no" stdin_open: false tty: true @@ -537,7 +537,7 @@ services: retries: 3 start_period: 45s netbox-redis: - image: ghcr.io/idaholab/malcolm/redis:23.04.1 + image: ghcr.io/idaholab/malcolm/redis:23.05.0 restart: "no" stdin_open: false tty: true @@ -565,7 +565,7 @@ services: retries: 3 start_period: 45s netbox-redis-cache: - image: ghcr.io/idaholab/malcolm/redis:23.04.1 + image: ghcr.io/idaholab/malcolm/redis:23.05.0 restart: "no" stdin_open: false tty: true @@ -592,7 +592,7 @@ services: retries: 3 start_period: 45s api: - image: ghcr.io/idaholab/malcolm/api:23.04.1 + image: ghcr.io/idaholab/malcolm/api:23.05.0 command: gunicorn --bind 0:5000 manage:app restart: "no" stdin_open: false @@ -616,7 +616,7 @@ services: retries: 3 start_period: 60s nginx-proxy: - image: ghcr.io/idaholab/malcolm/nginx-proxy:23.04.1 + image: ghcr.io/idaholab/malcolm/nginx-proxy:23.05.0 restart: "no" stdin_open: false tty: true diff --git a/docker-compose.yml b/docker-compose.yml index 3338d0424..e2a7bbed1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: build: context: . dockerfile: Dockerfiles/opensearch.Dockerfile - image: ghcr.io/idaholab/malcolm/opensearch:23.04.1 + image: ghcr.io/idaholab/malcolm/opensearch:23.05.0 restart: "no" stdin_open: false tty: true @@ -43,7 +43,7 @@ services: build: context: . dockerfile: Dockerfiles/dashboards-helper.Dockerfile - image: ghcr.io/idaholab/malcolm/dashboards-helper:23.04.1 + image: ghcr.io/idaholab/malcolm/dashboards-helper:23.05.0 restart: "no" stdin_open: false tty: true @@ -73,7 +73,7 @@ services: build: context: . dockerfile: Dockerfiles/dashboards.Dockerfile - image: ghcr.io/idaholab/malcolm/dashboards:23.04.1 + image: ghcr.io/idaholab/malcolm/dashboards:23.05.0 restart: "no" stdin_open: false tty: true @@ -102,7 +102,7 @@ services: build: context: . dockerfile: Dockerfiles/logstash.Dockerfile - image: ghcr.io/idaholab/malcolm/logstash-oss:23.04.1 + image: ghcr.io/idaholab/malcolm/logstash-oss:23.05.0 restart: "no" stdin_open: false tty: true @@ -151,7 +151,7 @@ services: build: context: . dockerfile: Dockerfiles/filebeat.Dockerfile - image: ghcr.io/idaholab/malcolm/filebeat-oss:23.04.1 + image: ghcr.io/idaholab/malcolm/filebeat-oss:23.05.0 restart: "no" stdin_open: false tty: true @@ -189,7 +189,7 @@ services: build: context: . dockerfile: Dockerfiles/arkime.Dockerfile - image: ghcr.io/idaholab/malcolm/arkime:23.04.1 + image: ghcr.io/idaholab/malcolm/arkime:23.05.0 restart: "no" stdin_open: false tty: true @@ -231,7 +231,7 @@ services: build: context: . dockerfile: Dockerfiles/zeek.Dockerfile - image: ghcr.io/idaholab/malcolm/zeek:23.04.1 + image: ghcr.io/idaholab/malcolm/zeek:23.05.0 restart: "no" stdin_open: false tty: true @@ -273,7 +273,7 @@ services: build: context: . dockerfile: Dockerfiles/zeek.Dockerfile - image: ghcr.io/idaholab/malcolm/zeek:23.04.1 + image: ghcr.io/idaholab/malcolm/zeek:23.05.0 restart: "no" stdin_open: false tty: true @@ -305,7 +305,7 @@ services: build: context: . dockerfile: Dockerfiles/suricata.Dockerfile - image: ghcr.io/idaholab/malcolm/suricata:23.04.1 + image: ghcr.io/idaholab/malcolm/suricata:23.05.0 restart: "no" stdin_open: false tty: true @@ -344,7 +344,7 @@ services: build: context: . dockerfile: Dockerfiles/suricata.Dockerfile - image: ghcr.io/idaholab/malcolm/suricata:23.04.1 + image: ghcr.io/idaholab/malcolm/suricata:23.05.0 restart: "no" stdin_open: false tty: true @@ -373,7 +373,7 @@ services: build: context: . dockerfile: Dockerfiles/file-monitor.Dockerfile - image: ghcr.io/idaholab/malcolm/file-monitor:23.04.1 + image: ghcr.io/idaholab/malcolm/file-monitor:23.05.0 restart: "no" stdin_open: false tty: true @@ -402,7 +402,7 @@ services: build: context: . dockerfile: Dockerfiles/pcap-capture.Dockerfile - image: ghcr.io/idaholab/malcolm/pcap-capture:23.04.1 + image: ghcr.io/idaholab/malcolm/pcap-capture:23.05.0 restart: "no" stdin_open: false tty: true @@ -427,7 +427,7 @@ services: build: context: . dockerfile: Dockerfiles/pcap-monitor.Dockerfile - image: ghcr.io/idaholab/malcolm/pcap-monitor:23.04.1 + image: ghcr.io/idaholab/malcolm/pcap-monitor:23.05.0 restart: "no" stdin_open: false tty: true @@ -456,7 +456,7 @@ services: build: context: . dockerfile: Dockerfiles/file-upload.Dockerfile - image: ghcr.io/idaholab/malcolm/file-upload:23.04.1 + image: ghcr.io/idaholab/malcolm/file-upload:23.05.0 restart: "no" stdin_open: false tty: true @@ -484,7 +484,7 @@ services: retries: 3 start_period: 60s htadmin: - image: ghcr.io/idaholab/malcolm/htadmin:23.04.1 + image: ghcr.io/idaholab/malcolm/htadmin:23.05.0 build: context: . dockerfile: Dockerfiles/htadmin.Dockerfile @@ -512,7 +512,7 @@ services: retries: 3 start_period: 60s freq: - image: ghcr.io/idaholab/malcolm/freq:23.04.1 + image: ghcr.io/idaholab/malcolm/freq:23.05.0 build: context: . dockerfile: Dockerfiles/freq.Dockerfile @@ -537,7 +537,7 @@ services: retries: 3 start_period: 60s netbox: - image: ghcr.io/idaholab/malcolm/netbox:23.04.1 + image: ghcr.io/idaholab/malcolm/netbox:23.05.0 build: context: . dockerfile: Dockerfiles/netbox.Dockerfile @@ -574,7 +574,7 @@ services: retries: 3 start_period: 120s netbox-postgres: - image: ghcr.io/idaholab/malcolm/postgresql:23.04.1 + image: ghcr.io/idaholab/malcolm/postgresql:23.05.0 build: context: . dockerfile: Dockerfiles/postgresql.Dockerfile @@ -601,7 +601,7 @@ services: retries: 3 start_period: 45s netbox-redis: - image: ghcr.io/idaholab/malcolm/redis:23.04.1 + image: ghcr.io/idaholab/malcolm/redis:23.05.0 build: context: . dockerfile: Dockerfiles/redis.Dockerfile @@ -632,7 +632,7 @@ services: retries: 3 start_period: 45s netbox-redis-cache: - image: ghcr.io/idaholab/malcolm/redis:23.04.1 + image: ghcr.io/idaholab/malcolm/redis:23.05.0 build: context: . dockerfile: Dockerfiles/redis.Dockerfile @@ -662,7 +662,7 @@ services: retries: 3 start_period: 45s api: - image: ghcr.io/idaholab/malcolm/api:23.04.1 + image: ghcr.io/idaholab/malcolm/api:23.05.0 build: context: . dockerfile: Dockerfiles/api.Dockerfile @@ -692,7 +692,7 @@ services: build: context: . dockerfile: Dockerfiles/nginx.Dockerfile - image: ghcr.io/idaholab/malcolm/nginx-proxy:23.04.1 + image: ghcr.io/idaholab/malcolm/nginx-proxy:23.05.0 restart: "no" stdin_open: false tty: true diff --git a/docs/download.md b/docs/download.md index 2a7f0fc5e..d2b26d4b1 100644 --- a/docs/download.md +++ b/docs/download.md @@ -16,7 +16,7 @@ While official downloads of the Malcolm installer ISO are not provided, an **uno | ISO | SHA256 | |---|---| -| [malcolm-23.04.1.iso](/iso/malcolm-23.04.1.iso) (5.3GiB) | [`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`](/iso/malcolm-23.04.1.iso.sha256.txt) | +| [malcolm-23.05.0.iso](/iso/malcolm-23.05.0.iso) (5.3GiB) | [`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`](/iso/malcolm-23.05.0.iso.sha256.txt) | ## Hedgehog Linux @@ -26,7 +26,7 @@ While official downloads of the Malcolm installer ISO are not provided, an **uno | ISO | SHA256 | |---|---| -| [hedgehog-23.04.1.iso](/iso/hedgehog-23.04.1.iso) (2.3GiB) | [`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`](/iso/hedgehog-23.04.1.iso.sha256.txt) | +| [hedgehog-23.05.0.iso](/iso/hedgehog-23.05.0.iso) (2.3GiB) | [`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`](/iso/hedgehog-23.05.0.iso.sha256.txt) | ## Warning diff --git a/docs/hedgehog-iso-build.md b/docs/hedgehog-iso-build.md index 746ea6e84..dcf35e219 100644 --- a/docs/hedgehog-iso-build.md +++ b/docs/hedgehog-iso-build.md @@ -29,7 +29,7 @@ Building the ISO may take 90 minutes or more depending on your system. As the bu ``` … -Finished, created "/sensor-build/hedgehog-23.04.1.iso" +Finished, created "/sensor-build/hedgehog-23.05.0.iso" … ``` diff --git a/docs/kubernetes.md b/docs/kubernetes.md index bea11a4dc..e743c10b5 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -261,28 +261,28 @@ agent2 | agent2 | 192.168.56.12 | agent2 | k3s | 6000m | agent1 | agent1 | 192.168.56.11 | agent1 | k3s | 6000m | 861.34m | 14.36% | 19.55Gi | 9.29Gi | 61.28Gi | 11 | Pod Name | State | Pod IP | Pod Kind | Worker Node | CPU Usage | Memory Usage | Container Name:Restarts | Container Image | -api-deployment-6f4686cf59-bn286 | Running | 10.42.2.14 | ReplicaSet | agent1 | 0.11m | 59.62Mi | api-container:0 | api:23.04.1 | -file-monitor-deployment-855646bd75-vk7st | Running | 10.42.2.16 | ReplicaSet | agent1 | 8.47m | 1.46Gi | file-monitor-container:0 | file-monitor:23.04.1 | -zeek-live-deployment-64b69d4b6f-947vr | Running | 10.42.2.17 | ReplicaSet | agent1 | 0.02m | 12.44Mi | zeek-live-container:0 | zeek:23.04.1 | -dashboards-helper-deployment-69dc54f6b6-ln4sq | Running | 10.42.2.15 | ReplicaSet | agent1 | 10.77m | 38.43Mi | dashboards-helper-container:0 | dashboards-helper:23.04.1 | -upload-deployment-586568844b-4jnk9 | Running | 10.42.2.18 | ReplicaSet | agent1 | 0.15m | 29.78Mi | upload-container:0 | file-upload:23.04.1 | -filebeat-deployment-6ff8bc444f-t7h49 | Running | 10.42.2.20 | ReplicaSet | agent1 | 2.84m | 70.71Mi | filebeat-container:0 | filebeat-oss:23.04.1 | -zeek-offline-deployment-844f4865bd-g2sdm | Running | 10.42.2.21 | ReplicaSet | agent1 | 0.17m | 41.92Mi | zeek-offline-container:0 | zeek:23.04.1 | -logstash-deployment-6fbc9fdcd5-hwx8s | Running | 10.42.2.22 | ReplicaSet | agent1 | 85.55m | 2.91Gi | logstash-container:0 | logstash-oss:23.04.1 | -netbox-deployment-cdcff4977-hbbw5 | Running | 10.42.2.23 | ReplicaSet | agent1 | 807.64m | 702.86Mi | netbox-container:0 | netbox:23.04.1 | -suricata-offline-deployment-6ccdb89478-z5696 | Running | 10.42.2.19 | ReplicaSet | agent1 | 0.22m | 34.88Mi | suricata-offline-container:0 | suricata:23.04.1 | -dashboards-deployment-69b5465db-vz88g | Running | 10.42.1.14 | ReplicaSet | agent2 | 0.94m | 100.12Mi | dashboards-container:0 | dashboards:23.04.1 | -netbox-redis-cache-deployment-5f77d47b8b-z7t2z | Running | 10.42.1.15 | ReplicaSet | agent2 | 3.57m | 7.36Mi | netbox-redis-cache-container:0 | redis:23.04.1 | -suricata-live-deployment-6494c77759-9rlnt | Running | 10.42.1.16 | ReplicaSet | agent2 | 0.02m | 9.69Mi | suricata-live-container:0 | suricata:23.04.1 | -freq-deployment-cfd84fd97-dnngf | Running | 10.42.1.17 | ReplicaSet | agent2 | 0.2m | 26.36Mi | freq-container:0 | freq:23.04.1 | -arkime-deployment-56999cdd66-s98pp | Running | 10.42.1.18 | ReplicaSet | agent2 | 4.15m | 113.07Mi | arkime-container:0 | arkime:23.04.1 | -pcap-monitor-deployment-594ff674c4-fsm7m | Running | 10.42.1.19 | ReplicaSet | agent2 | 1.24m | 48.44Mi | pcap-monitor-container:0 | pcap-monitor:23.04.1 | -pcap-capture-deployment-7c8bf6957-jzpzn | Running | 10.42.1.20 | ReplicaSet | agent2 | 0.02m | 9.64Mi | pcap-capture-container:0 | pcap-capture:23.04.1 | -netbox-postgres-deployment-5879b8dffc-kkt56 | Running | 10.42.1.21 | ReplicaSet | agent2 | 70.91m | 33.02Mi | netbox-postgres-container:0 | postgresql:23.04.1 | -htadmin-deployment-6fc46888b9-sq6ln | Running | 10.42.1.23 | ReplicaSet | agent2 | 0.14m | 30.53Mi | htadmin-container:0 | htadmin:23.04.1 | -netbox-redis-deployment-5bcd8f6c96-j5xpf | Running | 10.42.1.24 | ReplicaSet | agent2 | 1.46m | 7.34Mi | netbox-redis-container:0 | redis:23.04.1 | -nginx-proxy-deployment-69fcc4968d-f68tq | Running | 10.42.1.22 | ReplicaSet | agent2 | 0.31m | 22.63Mi | nginx-proxy-container:0 | nginx-proxy:23.04.1 | -opensearch-deployment-75498799f6-4zmwd | Running | 10.42.1.25 | ReplicaSet | agent2 | 89.8m | 11.03Gi | opensearch-container:0 | opensearch:23.04.1 | +api-deployment-6f4686cf59-bn286 | Running | 10.42.2.14 | ReplicaSet | agent1 | 0.11m | 59.62Mi | api-container:0 | api:23.05.0 | +file-monitor-deployment-855646bd75-vk7st | Running | 10.42.2.16 | ReplicaSet | agent1 | 8.47m | 1.46Gi | file-monitor-container:0 | file-monitor:23.05.0 | +zeek-live-deployment-64b69d4b6f-947vr | Running | 10.42.2.17 | ReplicaSet | agent1 | 0.02m | 12.44Mi | zeek-live-container:0 | zeek:23.05.0 | +dashboards-helper-deployment-69dc54f6b6-ln4sq | Running | 10.42.2.15 | ReplicaSet | agent1 | 10.77m | 38.43Mi | dashboards-helper-container:0 | dashboards-helper:23.05.0 | +upload-deployment-586568844b-4jnk9 | Running | 10.42.2.18 | ReplicaSet | agent1 | 0.15m | 29.78Mi | upload-container:0 | file-upload:23.05.0 | +filebeat-deployment-6ff8bc444f-t7h49 | Running | 10.42.2.20 | ReplicaSet | agent1 | 2.84m | 70.71Mi | filebeat-container:0 | filebeat-oss:23.05.0 | +zeek-offline-deployment-844f4865bd-g2sdm | Running | 10.42.2.21 | ReplicaSet | agent1 | 0.17m | 41.92Mi | zeek-offline-container:0 | zeek:23.05.0 | +logstash-deployment-6fbc9fdcd5-hwx8s | Running | 10.42.2.22 | ReplicaSet | agent1 | 85.55m | 2.91Gi | logstash-container:0 | logstash-oss:23.05.0 | +netbox-deployment-cdcff4977-hbbw5 | Running | 10.42.2.23 | ReplicaSet | agent1 | 807.64m | 702.86Mi | netbox-container:0 | netbox:23.05.0 | +suricata-offline-deployment-6ccdb89478-z5696 | Running | 10.42.2.19 | ReplicaSet | agent1 | 0.22m | 34.88Mi | suricata-offline-container:0 | suricata:23.05.0 | +dashboards-deployment-69b5465db-vz88g | Running | 10.42.1.14 | ReplicaSet | agent2 | 0.94m | 100.12Mi | dashboards-container:0 | dashboards:23.05.0 | +netbox-redis-cache-deployment-5f77d47b8b-z7t2z | Running | 10.42.1.15 | ReplicaSet | agent2 | 3.57m | 7.36Mi | netbox-redis-cache-container:0 | redis:23.05.0 | +suricata-live-deployment-6494c77759-9rlnt | Running | 10.42.1.16 | ReplicaSet | agent2 | 0.02m | 9.69Mi | suricata-live-container:0 | suricata:23.05.0 | +freq-deployment-cfd84fd97-dnngf | Running | 10.42.1.17 | ReplicaSet | agent2 | 0.2m | 26.36Mi | freq-container:0 | freq:23.05.0 | +arkime-deployment-56999cdd66-s98pp | Running | 10.42.1.18 | ReplicaSet | agent2 | 4.15m | 113.07Mi | arkime-container:0 | arkime:23.05.0 | +pcap-monitor-deployment-594ff674c4-fsm7m | Running | 10.42.1.19 | ReplicaSet | agent2 | 1.24m | 48.44Mi | pcap-monitor-container:0 | pcap-monitor:23.05.0 | +pcap-capture-deployment-7c8bf6957-jzpzn | Running | 10.42.1.20 | ReplicaSet | agent2 | 0.02m | 9.64Mi | pcap-capture-container:0 | pcap-capture:23.05.0 | +netbox-postgres-deployment-5879b8dffc-kkt56 | Running | 10.42.1.21 | ReplicaSet | agent2 | 70.91m | 33.02Mi | netbox-postgres-container:0 | postgresql:23.05.0 | +htadmin-deployment-6fc46888b9-sq6ln | Running | 10.42.1.23 | ReplicaSet | agent2 | 0.14m | 30.53Mi | htadmin-container:0 | htadmin:23.05.0 | +netbox-redis-deployment-5bcd8f6c96-j5xpf | Running | 10.42.1.24 | ReplicaSet | agent2 | 1.46m | 7.34Mi | netbox-redis-container:0 | redis:23.05.0 | +nginx-proxy-deployment-69fcc4968d-f68tq | Running | 10.42.1.22 | ReplicaSet | agent2 | 0.31m | 22.63Mi | nginx-proxy-container:0 | nginx-proxy:23.05.0 | +opensearch-deployment-75498799f6-4zmwd | Running | 10.42.1.25 | ReplicaSet | agent2 | 89.8m | 11.03Gi | opensearch-container:0 | opensearch:23.05.0 | ``` The other control scripts (`stop`, `restart`, `logs`, etc.) work in a similar manner as in a Docker-based deployment. One notable difference is the `wipe` script: data on PersistentVolume storage cannot be deleted by `wipe`. It must be deleted manually on the storage media underlying the PersistentVolumes. @@ -536,28 +536,28 @@ agent1 | agent1 | 192.168.56.11 | agent1 | k3s | 6000m | agent2 | agent2 | 192.168.56.12 | agent2 | k3s | 6000m | 552.71m | 9.21% | 19.55Gi | 13.27Gi | 61.28Gi | 12 | Pod Name | State | Pod IP | Pod Kind | Worker Node | CPU Usage | Memory Usage | Container Name:Restarts | Container Image | -netbox-redis-cache-deployment-5f77d47b8b-jr9nt | Running | 10.42.2.6 | ReplicaSet | agent2 | 1.89m | 7.24Mi | netbox-redis-cache-container:0 | redis:23.04.1 | -netbox-redis-deployment-5bcd8f6c96-bkzmh | Running | 10.42.2.5 | ReplicaSet | agent2 | 1.62m | 7.52Mi | netbox-redis-container:0 | redis:23.04.1 | -dashboards-helper-deployment-69dc54f6b6-ks7ps | Running | 10.42.2.4 | ReplicaSet | agent2 | 12.95m | 40.75Mi | dashboards-helper-container:0 | dashboards-helper:23.04.1 | -freq-deployment-cfd84fd97-5bwp6 | Running | 10.42.2.8 | ReplicaSet | agent2 | 0.11m | 26.33Mi | freq-container:0 | freq:23.04.1 | -pcap-capture-deployment-7c8bf6957-hkvkn | Running | 10.42.2.12 | ReplicaSet | agent2 | 0.02m | 9.21Mi | pcap-capture-container:0 | pcap-capture:23.04.1 | -nginx-proxy-deployment-69fcc4968d-m57rz | Running | 10.42.2.10 | ReplicaSet | agent2 | 0.91m | 22.72Mi | nginx-proxy-container:0 | nginx-proxy:23.04.1 | -htadmin-deployment-6fc46888b9-vpt7l | Running | 10.42.2.7 | ReplicaSet | agent2 | 0.16m | 30.21Mi | htadmin-container:0 | htadmin:23.04.1 | -opensearch-deployment-75498799f6-5v92w | Running | 10.42.2.13 | ReplicaSet | agent2 | 139.2m | 10.86Gi | opensearch-container:0 | opensearch:23.04.1 | -zeek-live-deployment-64b69d4b6f-fcb6n | Running | 10.42.2.9 | ReplicaSet | agent2 | 0.02m | 109.55Mi | zeek-live-container:0 | zeek:23.04.1 | -dashboards-deployment-69b5465db-kgsqk | Running | 10.42.2.3 | ReplicaSet | agent2 | 14.98m | 108.85Mi | dashboards-container:0 | dashboards:23.04.1 | -arkime-deployment-56999cdd66-xxpw9 | Running | 10.42.2.11 | ReplicaSet | agent2 | 208.95m | 78.42Mi | arkime-container:0 | arkime:23.04.1 | -api-deployment-6f4686cf59-xt9md | Running | 10.42.1.3 | ReplicaSet | agent1 | 0.14m | 56.88Mi | api-container:0 | api:23.04.1 | -netbox-postgres-deployment-5879b8dffc-lb4qm | Running | 10.42.1.6 | ReplicaSet | agent1 | 141.2m | 48.02Mi | netbox-postgres-container:0 | postgresql:23.04.1 | -pcap-monitor-deployment-594ff674c4-fwq7g | Running | 10.42.1.12 | ReplicaSet | agent1 | 3.93m | 46.44Mi | pcap-monitor-container:0 | pcap-monitor:23.04.1 | -suricata-offline-deployment-6ccdb89478-j5fgj | Running | 10.42.1.10 | ReplicaSet | agent1 | 10.42m | 35.12Mi | suricata-offline-container:0 | suricata:23.04.1 | -suricata-live-deployment-6494c77759-rpt48 | Running | 10.42.1.8 | ReplicaSet | agent1 | 0.01m | 9.62Mi | suricata-live-container:0 | suricata:23.04.1 | -netbox-deployment-cdcff4977-7ns2q | Running | 10.42.1.7 | ReplicaSet | agent1 | 830.47m | 530.7Mi | netbox-container:0 | netbox:23.04.1 | -zeek-offline-deployment-844f4865bd-7x68b | Running | 10.42.1.9 | ReplicaSet | agent1 | 1.44m | 43.66Mi | zeek-offline-container:0 | zeek:23.04.1 | -filebeat-deployment-6ff8bc444f-pdgzj | Running | 10.42.1.11 | ReplicaSet | agent1 | 0.78m | 75.25Mi | filebeat-container:0 | filebeat-oss:23.04.1 | -file-monitor-deployment-855646bd75-nbngq | Running | 10.42.1.4 | ReplicaSet | agent1 | 1.69m | 1.46Gi | file-monitor-container:0 | file-monitor:23.04.1 | -upload-deployment-586568844b-9s7f5 | Running | 10.42.1.13 | ReplicaSet | agent1 | 0.14m | 29.62Mi | upload-container:0 | file-upload:23.04.1 | -logstash-deployment-6fbc9fdcd5-2hhx8 | Running | 10.42.1.5 | ReplicaSet | agent1 | 3236.29m | 357.36Mi | logstash-container:0 | logstash-oss:23.04.1 | +netbox-redis-cache-deployment-5f77d47b8b-jr9nt | Running | 10.42.2.6 | ReplicaSet | agent2 | 1.89m | 7.24Mi | netbox-redis-cache-container:0 | redis:23.05.0 | +netbox-redis-deployment-5bcd8f6c96-bkzmh | Running | 10.42.2.5 | ReplicaSet | agent2 | 1.62m | 7.52Mi | netbox-redis-container:0 | redis:23.05.0 | +dashboards-helper-deployment-69dc54f6b6-ks7ps | Running | 10.42.2.4 | ReplicaSet | agent2 | 12.95m | 40.75Mi | dashboards-helper-container:0 | dashboards-helper:23.05.0 | +freq-deployment-cfd84fd97-5bwp6 | Running | 10.42.2.8 | ReplicaSet | agent2 | 0.11m | 26.33Mi | freq-container:0 | freq:23.05.0 | +pcap-capture-deployment-7c8bf6957-hkvkn | Running | 10.42.2.12 | ReplicaSet | agent2 | 0.02m | 9.21Mi | pcap-capture-container:0 | pcap-capture:23.05.0 | +nginx-proxy-deployment-69fcc4968d-m57rz | Running | 10.42.2.10 | ReplicaSet | agent2 | 0.91m | 22.72Mi | nginx-proxy-container:0 | nginx-proxy:23.05.0 | +htadmin-deployment-6fc46888b9-vpt7l | Running | 10.42.2.7 | ReplicaSet | agent2 | 0.16m | 30.21Mi | htadmin-container:0 | htadmin:23.05.0 | +opensearch-deployment-75498799f6-5v92w | Running | 10.42.2.13 | ReplicaSet | agent2 | 139.2m | 10.86Gi | opensearch-container:0 | opensearch:23.05.0 | +zeek-live-deployment-64b69d4b6f-fcb6n | Running | 10.42.2.9 | ReplicaSet | agent2 | 0.02m | 109.55Mi | zeek-live-container:0 | zeek:23.05.0 | +dashboards-deployment-69b5465db-kgsqk | Running | 10.42.2.3 | ReplicaSet | agent2 | 14.98m | 108.85Mi | dashboards-container:0 | dashboards:23.05.0 | +arkime-deployment-56999cdd66-xxpw9 | Running | 10.42.2.11 | ReplicaSet | agent2 | 208.95m | 78.42Mi | arkime-container:0 | arkime:23.05.0 | +api-deployment-6f4686cf59-xt9md | Running | 10.42.1.3 | ReplicaSet | agent1 | 0.14m | 56.88Mi | api-container:0 | api:23.05.0 | +netbox-postgres-deployment-5879b8dffc-lb4qm | Running | 10.42.1.6 | ReplicaSet | agent1 | 141.2m | 48.02Mi | netbox-postgres-container:0 | postgresql:23.05.0 | +pcap-monitor-deployment-594ff674c4-fwq7g | Running | 10.42.1.12 | ReplicaSet | agent1 | 3.93m | 46.44Mi | pcap-monitor-container:0 | pcap-monitor:23.05.0 | +suricata-offline-deployment-6ccdb89478-j5fgj | Running | 10.42.1.10 | ReplicaSet | agent1 | 10.42m | 35.12Mi | suricata-offline-container:0 | suricata:23.05.0 | +suricata-live-deployment-6494c77759-rpt48 | Running | 10.42.1.8 | ReplicaSet | agent1 | 0.01m | 9.62Mi | suricata-live-container:0 | suricata:23.05.0 | +netbox-deployment-cdcff4977-7ns2q | Running | 10.42.1.7 | ReplicaSet | agent1 | 830.47m | 530.7Mi | netbox-container:0 | netbox:23.05.0 | +zeek-offline-deployment-844f4865bd-7x68b | Running | 10.42.1.9 | ReplicaSet | agent1 | 1.44m | 43.66Mi | zeek-offline-container:0 | zeek:23.05.0 | +filebeat-deployment-6ff8bc444f-pdgzj | Running | 10.42.1.11 | ReplicaSet | agent1 | 0.78m | 75.25Mi | filebeat-container:0 | filebeat-oss:23.05.0 | +file-monitor-deployment-855646bd75-nbngq | Running | 10.42.1.4 | ReplicaSet | agent1 | 1.69m | 1.46Gi | file-monitor-container:0 | file-monitor:23.05.0 | +upload-deployment-586568844b-9s7f5 | Running | 10.42.1.13 | ReplicaSet | agent1 | 0.14m | 29.62Mi | upload-container:0 | file-upload:23.05.0 | +logstash-deployment-6fbc9fdcd5-2hhx8 | Running | 10.42.1.5 | ReplicaSet | agent1 | 3236.29m | 357.36Mi | logstash-container:0 | logstash-oss:23.05.0 | ``` View container logs for the Malcolm deployment with `./scripts/logs` (if **[stern](https://github.com/stern/stern)** present in `$PATH`): diff --git a/docs/malcolm-iso.md b/docs/malcolm-iso.md index 9f29b5615..f2946d534 100644 --- a/docs/malcolm-iso.md +++ b/docs/malcolm-iso.md @@ -41,7 +41,7 @@ Building the ISO may take 30 minutes or more depending on your system. As the bu ``` … -Finished, created "/malcolm-build/malcolm-iso/malcolm-23.04.1.iso" +Finished, created "/malcolm-build/malcolm-iso/malcolm-23.05.0.iso" … ``` diff --git a/docs/quickstart.md b/docs/quickstart.md index 57253e0ae..40e5d49dd 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -54,25 +54,25 @@ You can then observe that the images have been retrieved by running `docker imag ``` $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE -ghcr.io/idaholab/malcolm/api 23.04.1 xxxxxxxxxxxx 3 days ago 158MB -ghcr.io/idaholab/malcolm/arkime 23.04.1 xxxxxxxxxxxx 3 days ago 816MB -ghcr.io/idaholab/malcolm/dashboards 23.04.1 xxxxxxxxxxxx 3 days ago 1.02GB -ghcr.io/idaholab/malcolm/dashboards-helper 23.04.1 xxxxxxxxxxxx 3 days ago 184MB -ghcr.io/idaholab/malcolm/file-monitor 23.04.1 xxxxxxxxxxxx 3 days ago 588MB -ghcr.io/idaholab/malcolm/file-upload 23.04.1 xxxxxxxxxxxx 3 days ago 259MB -ghcr.io/idaholab/malcolm/filebeat-oss 23.04.1 xxxxxxxxxxxx 3 days ago 624MB -ghcr.io/idaholab/malcolm/freq 23.04.1 xxxxxxxxxxxx 3 days ago 132MB -ghcr.io/idaholab/malcolm/htadmin 23.04.1 xxxxxxxxxxxx 3 days ago 242MB -ghcr.io/idaholab/malcolm/logstash-oss 23.04.1 xxxxxxxxxxxx 3 days ago 1.35GB -ghcr.io/idaholab/malcolm/netbox 23.04.1 xxxxxxxxxxxx 3 days ago 1.01GB -ghcr.io/idaholab/malcolm/nginx-proxy 23.04.1 xxxxxxxxxxxx 3 days ago 121MB -ghcr.io/idaholab/malcolm/opensearch 23.04.1 xxxxxxxxxxxx 3 days ago 1.17GB -ghcr.io/idaholab/malcolm/pcap-capture 23.04.1 xxxxxxxxxxxx 3 days ago 121MB -ghcr.io/idaholab/malcolm/pcap-monitor 23.04.1 xxxxxxxxxxxx 3 days ago 213MB -ghcr.io/idaholab/malcolm/postgresql 23.04.1 xxxxxxxxxxxx 3 days ago 268MB -ghcr.io/idaholab/malcolm/redis 23.04.1 xxxxxxxxxxxx 3 days ago 34.2MB -ghcr.io/idaholab/malcolm/suricata 23.04.1 xxxxxxxxxxxx 3 days ago 278MB -ghcr.io/idaholab/malcolm/zeek 23.04.1 xxxxxxxxxxxx 3 days ago 1GB +ghcr.io/idaholab/malcolm/api 23.05.0 xxxxxxxxxxxx 3 days ago 158MB +ghcr.io/idaholab/malcolm/arkime 23.05.0 xxxxxxxxxxxx 3 days ago 816MB +ghcr.io/idaholab/malcolm/dashboards 23.05.0 xxxxxxxxxxxx 3 days ago 1.02GB +ghcr.io/idaholab/malcolm/dashboards-helper 23.05.0 xxxxxxxxxxxx 3 days ago 184MB +ghcr.io/idaholab/malcolm/file-monitor 23.05.0 xxxxxxxxxxxx 3 days ago 588MB +ghcr.io/idaholab/malcolm/file-upload 23.05.0 xxxxxxxxxxxx 3 days ago 259MB +ghcr.io/idaholab/malcolm/filebeat-oss 23.05.0 xxxxxxxxxxxx 3 days ago 624MB +ghcr.io/idaholab/malcolm/freq 23.05.0 xxxxxxxxxxxx 3 days ago 132MB +ghcr.io/idaholab/malcolm/htadmin 23.05.0 xxxxxxxxxxxx 3 days ago 242MB +ghcr.io/idaholab/malcolm/logstash-oss 23.05.0 xxxxxxxxxxxx 3 days ago 1.35GB +ghcr.io/idaholab/malcolm/netbox 23.05.0 xxxxxxxxxxxx 3 days ago 1.01GB +ghcr.io/idaholab/malcolm/nginx-proxy 23.05.0 xxxxxxxxxxxx 3 days ago 121MB +ghcr.io/idaholab/malcolm/opensearch 23.05.0 xxxxxxxxxxxx 3 days ago 1.17GB +ghcr.io/idaholab/malcolm/pcap-capture 23.05.0 xxxxxxxxxxxx 3 days ago 121MB +ghcr.io/idaholab/malcolm/pcap-monitor 23.05.0 xxxxxxxxxxxx 3 days ago 213MB +ghcr.io/idaholab/malcolm/postgresql 23.05.0 xxxxxxxxxxxx 3 days ago 268MB +ghcr.io/idaholab/malcolm/redis 23.05.0 xxxxxxxxxxxx 3 days ago 34.2MB +ghcr.io/idaholab/malcolm/suricata 23.05.0 xxxxxxxxxxxx 3 days ago 278MB +ghcr.io/idaholab/malcolm/zeek 23.05.0 xxxxxxxxxxxx 3 days ago 1GB ``` ### Import from pre-packaged tarballs diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index 009feb698..0e4e8c315 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -256,25 +256,25 @@ Pulling zeek ... done user@host:~/Malcolm$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE -ghcr.io/idaholab/malcolm/api 23.04.1 xxxxxxxxxxxx 3 days ago 158MB -ghcr.io/idaholab/malcolm/arkime 23.04.1 xxxxxxxxxxxx 3 days ago 816MB -ghcr.io/idaholab/malcolm/dashboards 23.04.1 xxxxxxxxxxxx 3 days ago 1.02GB -ghcr.io/idaholab/malcolm/dashboards-helper 23.04.1 xxxxxxxxxxxx 3 days ago 184MB -ghcr.io/idaholab/malcolm/file-monitor 23.04.1 xxxxxxxxxxxx 3 days ago 588MB -ghcr.io/idaholab/malcolm/file-upload 23.04.1 xxxxxxxxxxxx 3 days ago 259MB -ghcr.io/idaholab/malcolm/filebeat-oss 23.04.1 xxxxxxxxxxxx 3 days ago 624MB -ghcr.io/idaholab/malcolm/freq 23.04.1 xxxxxxxxxxxx 3 days ago 132MB -ghcr.io/idaholab/malcolm/htadmin 23.04.1 xxxxxxxxxxxx 3 days ago 242MB -ghcr.io/idaholab/malcolm/logstash-oss 23.04.1 xxxxxxxxxxxx 3 days ago 1.35GB -ghcr.io/idaholab/malcolm/netbox 23.04.1 xxxxxxxxxxxx 3 days ago 1.01GB -ghcr.io/idaholab/malcolm/nginx-proxy 23.04.1 xxxxxxxxxxxx 3 days ago 121MB -ghcr.io/idaholab/malcolm/opensearch 23.04.1 xxxxxxxxxxxx 3 days ago 1.17GB -ghcr.io/idaholab/malcolm/pcap-capture 23.04.1 xxxxxxxxxxxx 3 days ago 121MB -ghcr.io/idaholab/malcolm/pcap-monitor 23.04.1 xxxxxxxxxxxx 3 days ago 213MB -ghcr.io/idaholab/malcolm/postgresql 23.04.1 xxxxxxxxxxxx 3 days ago 268MB -ghcr.io/idaholab/malcolm/redis 23.04.1 xxxxxxxxxxxx 3 days ago 34.2MB -ghcr.io/idaholab/malcolm/suricata 23.04.1 xxxxxxxxxxxx 3 days ago 278MB -ghcr.io/idaholab/malcolm/zeek 23.04.1 xxxxxxxxxxxx 3 days ago 1GB +ghcr.io/idaholab/malcolm/api 23.05.0 xxxxxxxxxxxx 3 days ago 158MB +ghcr.io/idaholab/malcolm/arkime 23.05.0 xxxxxxxxxxxx 3 days ago 816MB +ghcr.io/idaholab/malcolm/dashboards 23.05.0 xxxxxxxxxxxx 3 days ago 1.02GB +ghcr.io/idaholab/malcolm/dashboards-helper 23.05.0 xxxxxxxxxxxx 3 days ago 184MB +ghcr.io/idaholab/malcolm/file-monitor 23.05.0 xxxxxxxxxxxx 3 days ago 588MB +ghcr.io/idaholab/malcolm/file-upload 23.05.0 xxxxxxxxxxxx 3 days ago 259MB +ghcr.io/idaholab/malcolm/filebeat-oss 23.05.0 xxxxxxxxxxxx 3 days ago 624MB +ghcr.io/idaholab/malcolm/freq 23.05.0 xxxxxxxxxxxx 3 days ago 132MB +ghcr.io/idaholab/malcolm/htadmin 23.05.0 xxxxxxxxxxxx 3 days ago 242MB +ghcr.io/idaholab/malcolm/logstash-oss 23.05.0 xxxxxxxxxxxx 3 days ago 1.35GB +ghcr.io/idaholab/malcolm/netbox 23.05.0 xxxxxxxxxxxx 3 days ago 1.01GB +ghcr.io/idaholab/malcolm/nginx-proxy 23.05.0 xxxxxxxxxxxx 3 days ago 121MB +ghcr.io/idaholab/malcolm/opensearch 23.05.0 xxxxxxxxxxxx 3 days ago 1.17GB +ghcr.io/idaholab/malcolm/pcap-capture 23.05.0 xxxxxxxxxxxx 3 days ago 121MB +ghcr.io/idaholab/malcolm/pcap-monitor 23.05.0 xxxxxxxxxxxx 3 days ago 213MB +ghcr.io/idaholab/malcolm/postgresql 23.05.0 xxxxxxxxxxxx 3 days ago 268MB +ghcr.io/idaholab/malcolm/redis 23.05.0 xxxxxxxxxxxx 3 days ago 34.2MB +ghcr.io/idaholab/malcolm/suricata 23.05.0 xxxxxxxxxxxx 3 days ago 278MB +ghcr.io/idaholab/malcolm/zeek 23.05.0 xxxxxxxxxxxx 3 days ago 1GB ``` Finally, we can start Malcolm. When Malcolm starts it will stream informational and debug messages to the console. If you wish, you can safely close the console or use `Ctrl+C` to stop these messages; Malcolm will continue running in the background. diff --git a/kubernetes/02-opensearch.yml b/kubernetes/02-opensearch.yml index e2f70e54c..6cf5af14e 100644 --- a/kubernetes/02-opensearch.yml +++ b/kubernetes/02-opensearch.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: opensearch-container - image: ghcr.io/mmguero-dev/malcolm/opensearch:development + image: ghcr.io/idaholab/malcolm/opensearch:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/03-dashboards.yml b/kubernetes/03-dashboards.yml index cfbb8b422..8db23880b 100644 --- a/kubernetes/03-dashboards.yml +++ b/kubernetes/03-dashboards.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: dashboards-container - image: ghcr.io/mmguero-dev/malcolm/dashboards:development + image: ghcr.io/idaholab/malcolm/dashboards:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/04-upload.yml b/kubernetes/04-upload.yml index 19915597c..bb978dbd2 100644 --- a/kubernetes/04-upload.yml +++ b/kubernetes/04-upload.yml @@ -34,7 +34,7 @@ spec: spec: containers: - name: upload-container - image: ghcr.io/mmguero-dev/malcolm/file-upload:development + image: ghcr.io/idaholab/malcolm/file-upload:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/05-pcap-monitor.yml b/kubernetes/05-pcap-monitor.yml index b8b76084b..04be6b978 100644 --- a/kubernetes/05-pcap-monitor.yml +++ b/kubernetes/05-pcap-monitor.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: pcap-monitor-container - image: ghcr.io/mmguero-dev/malcolm/pcap-monitor:development + image: ghcr.io/idaholab/malcolm/pcap-monitor:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/06-arkime.yml b/kubernetes/06-arkime.yml index ef24eb44b..786961836 100644 --- a/kubernetes/06-arkime.yml +++ b/kubernetes/06-arkime.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: arkime-container - image: ghcr.io/mmguero-dev/malcolm/arkime:development + image: ghcr.io/idaholab/malcolm/arkime:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/07-api.yml b/kubernetes/07-api.yml index 3f611da69..d318a8c46 100644 --- a/kubernetes/07-api.yml +++ b/kubernetes/07-api.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: api-container - image: ghcr.io/mmguero-dev/malcolm/api:development + image: ghcr.io/idaholab/malcolm/api:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/08-dashboards-helper.yml b/kubernetes/08-dashboards-helper.yml index 529647210..bdf8cb767 100644 --- a/kubernetes/08-dashboards-helper.yml +++ b/kubernetes/08-dashboards-helper.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: dashboards-helper-container - image: ghcr.io/mmguero-dev/malcolm/dashboards-helper:development + image: ghcr.io/idaholab/malcolm/dashboards-helper:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/09-zeek.yml b/kubernetes/09-zeek.yml index f7fd93b7f..edfc8a64d 100644 --- a/kubernetes/09-zeek.yml +++ b/kubernetes/09-zeek.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: zeek-offline-container - image: ghcr.io/mmguero-dev/malcolm/zeek:development + image: ghcr.io/idaholab/malcolm/zeek:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/10-suricata.yml b/kubernetes/10-suricata.yml index da525b557..d2208ab26 100644 --- a/kubernetes/10-suricata.yml +++ b/kubernetes/10-suricata.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: suricata-offline-container - image: ghcr.io/mmguero-dev/malcolm/suricata:development + image: ghcr.io/idaholab/malcolm/suricata:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/11-file-monitor.yml b/kubernetes/11-file-monitor.yml index 39a29b65d..05b4227f1 100644 --- a/kubernetes/11-file-monitor.yml +++ b/kubernetes/11-file-monitor.yml @@ -33,7 +33,7 @@ spec: spec: containers: - name: file-monitor-container - image: ghcr.io/mmguero-dev/malcolm/file-monitor:development + image: ghcr.io/idaholab/malcolm/file-monitor:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/12-filebeat.yml b/kubernetes/12-filebeat.yml index 7676193c0..aa073b19d 100644 --- a/kubernetes/12-filebeat.yml +++ b/kubernetes/12-filebeat.yml @@ -31,7 +31,7 @@ spec: spec: containers: - name: filebeat-container - image: ghcr.io/mmguero-dev/malcolm/filebeat-oss:development + image: ghcr.io/idaholab/malcolm/filebeat-oss:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/13-logstash.yml b/kubernetes/13-logstash.yml index 081c47ed3..56b9c256c 100644 --- a/kubernetes/13-logstash.yml +++ b/kubernetes/13-logstash.yml @@ -47,7 +47,7 @@ spec: # topologyKey: "kubernetes.io/hostname" containers: - name: logstash-container - image: ghcr.io/mmguero-dev/malcolm/logstash-oss:development + image: ghcr.io/idaholab/malcolm/logstash-oss:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/15-netbox-redis.yml b/kubernetes/15-netbox-redis.yml index 984110449..9fceac45d 100644 --- a/kubernetes/15-netbox-redis.yml +++ b/kubernetes/15-netbox-redis.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: netbox-redis-container - image: ghcr.io/mmguero-dev/malcolm/redis:development + image: ghcr.io/idaholab/malcolm/redis:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/16-netbox-redis-cache.yml b/kubernetes/16-netbox-redis-cache.yml index 22f1be5a4..1096ca615 100644 --- a/kubernetes/16-netbox-redis-cache.yml +++ b/kubernetes/16-netbox-redis-cache.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: netbox-redis-cache-container - image: ghcr.io/mmguero-dev/malcolm/redis:development + image: ghcr.io/idaholab/malcolm/redis:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/17-netbox-postgres.yml b/kubernetes/17-netbox-postgres.yml index 1497c1bea..5d5ad21a0 100644 --- a/kubernetes/17-netbox-postgres.yml +++ b/kubernetes/17-netbox-postgres.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: netbox-postgres-container - image: ghcr.io/mmguero-dev/malcolm/postgresql:development + image: ghcr.io/idaholab/malcolm/postgresql:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/18-netbox.yml b/kubernetes/18-netbox.yml index 5a613fc8e..d22b3f7ac 100644 --- a/kubernetes/18-netbox.yml +++ b/kubernetes/18-netbox.yml @@ -36,7 +36,7 @@ spec: spec: containers: - name: netbox-container - image: ghcr.io/mmguero-dev/malcolm/netbox:development + image: ghcr.io/idaholab/malcolm/netbox:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/19-htadmin.yml b/kubernetes/19-htadmin.yml index 716a46363..0bfc8348a 100644 --- a/kubernetes/19-htadmin.yml +++ b/kubernetes/19-htadmin.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: htadmin-container - image: ghcr.io/mmguero-dev/malcolm/htadmin:development + image: ghcr.io/idaholab/malcolm/htadmin:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/20-pcap-capture.yml b/kubernetes/20-pcap-capture.yml index 63360e5a4..5c9b21f3f 100644 --- a/kubernetes/20-pcap-capture.yml +++ b/kubernetes/20-pcap-capture.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: pcap-capture-container - image: ghcr.io/mmguero-dev/malcolm/pcap-capture:development + image: ghcr.io/idaholab/malcolm/pcap-capture:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/21-zeek-live.yml b/kubernetes/21-zeek-live.yml index 3eb2cf92f..f67b32625 100644 --- a/kubernetes/21-zeek-live.yml +++ b/kubernetes/21-zeek-live.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: zeek-live-container - image: ghcr.io/mmguero-dev/malcolm/zeek:development + image: ghcr.io/idaholab/malcolm/zeek:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/22-suricata-live.yml b/kubernetes/22-suricata-live.yml index fe0ee1d21..d0fa77305 100644 --- a/kubernetes/22-suricata-live.yml +++ b/kubernetes/22-suricata-live.yml @@ -16,7 +16,7 @@ spec: spec: containers: - name: suricata-live-container - image: ghcr.io/mmguero-dev/malcolm/suricata:development + image: ghcr.io/idaholab/malcolm/suricata:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/23-freq.yml b/kubernetes/23-freq.yml index b9dc580df..c515bd917 100644 --- a/kubernetes/23-freq.yml +++ b/kubernetes/23-freq.yml @@ -30,7 +30,7 @@ spec: spec: containers: - name: freq-container - image: ghcr.io/mmguero-dev/malcolm/freq:development + image: ghcr.io/idaholab/malcolm/freq:23.05.0 imagePullPolicy: Always stdin: false tty: true diff --git a/kubernetes/99-nginx-proxy.yml b/kubernetes/99-nginx-proxy.yml index 5c2f49b4c..ccd1d5124 100644 --- a/kubernetes/99-nginx-proxy.yml +++ b/kubernetes/99-nginx-proxy.yml @@ -37,7 +37,7 @@ spec: spec: containers: - name: nginx-proxy-container - image: ghcr.io/mmguero-dev/malcolm/nginx-proxy:development + image: ghcr.io/idaholab/malcolm/nginx-proxy:23.05.0 imagePullPolicy: Always stdin: false tty: true From b6c2136445685fc386727185aa839f96968193e3 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Fri, 28 Apr 2023 18:45:48 -0600 Subject: [PATCH 231/239] buildalo --- .trigger_workflow_build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.trigger_workflow_build b/.trigger_workflow_build index 3e6f3d089..ee028e549 100644 --- a/.trigger_workflow_build +++ b/.trigger_workflow_build @@ -1,2 +1,2 @@ # this file exists solely for the purpose of being updated and seen by github to trigger a commit build action -5 +6 From 69fb3a708a68e534ebfa692e594683a2305256d7 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 1 May 2023 07:13:00 -0600 Subject: [PATCH 232/239] fix missing import --- shared/bin/configure-interfaces.py | 1 - 1 file changed, 1 deletion(-) diff --git a/shared/bin/configure-interfaces.py b/shared/bin/configure-interfaces.py index de9a344b4..ee03ee749 100755 --- a/shared/bin/configure-interfaces.py +++ b/shared/bin/configure-interfaces.py @@ -28,7 +28,6 @@ aggressive_url_encode, isipaddress, check_socket, - test_connection, ) From e83244fcca90a2107ed940ad427bbd61222d9e5f Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 1 May 2023 07:23:11 -0600 Subject: [PATCH 233/239] use run_subprocess instead of run_process in ISO configure-capture and configure-interfaces --- shared/bin/configure-capture.py | 12 ++++++------ shared/bin/configure-interfaces.py | 26 +++++++++++++------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/shared/bin/configure-capture.py b/shared/bin/configure-capture.py index e05c4b789..e43e3bead 100755 --- a/shared/bin/configure-capture.py +++ b/shared/bin/configure-capture.py @@ -25,7 +25,7 @@ NIC_BLINK_SECONDS, test_connection, ) -from malcolm_utils import run_process, remove_prefix, aggressive_url_encode, isipaddress, check_socket +from malcolm_utils import run_subprocess, remove_prefix, aggressive_url_encode, isipaddress, check_socket class Constants: @@ -494,7 +494,7 @@ def main(): capture_filter = capture_filter.strip() if len(capture_filter) > 0: # test out the capture filter to see if there's a syntax error - ecode, filter_test_results = run_process( + ecode, filter_test_results = run_subprocess( f'tcpdump -i {selected_ifaces[0]} -d "{capture_filter}"', stdout=False, stderr=True ) else: @@ -960,13 +960,13 @@ def main(): os.chdir(Constants.BEAT_DIR[fwd_mode]) # check to see if a keystore has already been created for the forwarder - ecode, list_results = run_process(f"{Constants.BEAT_CMD[fwd_mode]} keystore list") + ecode, list_results = run_subprocess(f"{Constants.BEAT_CMD[fwd_mode]} keystore list") if (ecode == 0) and (len(list_results) > 0): # it has, do they wish to overwrite it? if d.yesno(Constants.MSG_OVERWRITE_CONFIG.format(fwd_mode)) != Dialog.OK: raise CancelledError - ecode, create_results = run_process( + ecode, create_results = run_subprocess( f"{Constants.BEAT_CMD[fwd_mode]} keystore create --force", stderr=True ) if ecode != 0: @@ -1183,7 +1183,7 @@ def main(): # it's go time, call keystore add for each item for k, v in sorted(forwarder_dict.items()): - ecode, add_results = run_process( + ecode, add_results = run_subprocess( f"{Constants.BEAT_CMD[fwd_mode]} keystore add {k} --stdin --force", stdin=v, stderr=True ) if ecode != 0: @@ -1191,7 +1191,7 @@ def main(): raise Exception(Constants.MSG_ERROR_KEYSTORE.format(fwd_mode, "\n".join(add_results))) # get a final list of parameters that were set to show the user that stuff happened - ecode, list_results = run_process(f"{Constants.BEAT_CMD[fwd_mode]} keystore list") + ecode, list_results = run_subprocess(f"{Constants.BEAT_CMD[fwd_mode]} keystore list") if ecode == 0: code = d.msgbox( text=Constants.MSG_CONFIG_FORWARDING_SUCCESS.format(fwd_mode, "\n".join(list_results)) diff --git a/shared/bin/configure-interfaces.py b/shared/bin/configure-interfaces.py index ee03ee749..2e71806c7 100755 --- a/shared/bin/configure-interfaces.py +++ b/shared/bin/configure-interfaces.py @@ -23,7 +23,7 @@ ) from malcolm_utils import ( eprint, - run_process, + run_subprocess, remove_prefix, aggressive_url_encode, isipaddress, @@ -107,7 +107,7 @@ def network_stop(selected_iface): else: command = f"cat /sys/class/net/{selected_iface}/operstate" - return run_process(command, stderr=True) + return run_subprocess(command, stderr=True) ################################################################################################### @@ -122,7 +122,7 @@ def network_start(selected_iface): else: command = f"cat /sys/class/net/{selected_iface}/operstate" - return run_process(command, stderr=True) + return run_subprocess(command, stderr=True) ################################################################################################### @@ -213,12 +213,12 @@ def main(): # system hostname configuration ###################################################################################################### # get current host/identification information - ecode, host_get_output = run_process('hostnamectl', stderr=True) + ecode, host_get_output = run_subprocess('hostnamectl', stderr=True) if ecode == 0: emsg_str = '\n'.join(host_get_output) code = d.msgbox(text=f"{Constants.MSG_SET_HOSTNAME_CURRENT}{emsg_str}") - code, hostname_get_output = run_process('hostname', stderr=False) + code, hostname_get_output = run_subprocess('hostname', stderr=False) if (code == 0) and (len(hostname_get_output) > 0): old_hostname = hostname_get_output[0].strip() else: @@ -235,11 +235,11 @@ def main(): break # set new hostname - ecode, host_set_output = run_process( + ecode, host_set_output = run_subprocess( f'hostnamectl set-hostname {new_hostname.strip()}', stderr=True ) if ecode == 0: - ecode, host_get_output = run_process('hostnamectl', stderr=True) + ecode, host_get_output = run_subprocess('hostnamectl', stderr=True) emsg_str = '\n'.join(host_get_output) code = d.msgbox(text=f"{Constants.MSG_SET_HOSTNAME_SUCCESS}{emsg_str}") @@ -310,7 +310,7 @@ def main(): break # test with htpdate to see if we can connect - ecode, test_output = run_process( + ecode, test_output = run_subprocess( f"{Constants.TIME_SYNC_HTPDATE_TEST_COMMAND} {http_host}:{http_port}" ) if ecode == 0: @@ -334,8 +334,8 @@ def main(): raise CancelledError # stop and disable the ntp process - run_process('/bin/systemctl stop ntp') - run_process('/bin/systemctl disable ntp') + run_subprocess('/bin/systemctl stop ntp') + run_subprocess('/bin/systemctl disable ntp') # write out htpdate file for cron with open(Constants.TIME_SYNC_HTPDATE_CRON, 'w+') as f: @@ -388,9 +388,9 @@ def main(): print(line) # enable and start the ntp process - run_process('/bin/systemctl stop ntp') - run_process('/bin/systemctl enable ntp') - ecode, start_output = run_process('/bin/systemctl start ntp', stderr=True) + run_subprocess('/bin/systemctl stop ntp') + run_subprocess('/bin/systemctl enable ntp') + ecode, start_output = run_subprocess('/bin/systemctl start ntp', stderr=True) if ecode == 0: code = d.msgbox(text=f"{Constants.MSG_TIME_SYNC_CONFIG_SUCCESS}") else: From 7640a4db6dec50f76546dc1b4326e55139162cc3 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 1 May 2023 07:36:29 -0600 Subject: [PATCH 234/239] fixes for aggregator ISO init --- .github/workflows/api-build-and-push-ghcr.yml | 4 +++- .github/workflows/arkime-build-and-push-ghcr.yml | 4 +++- .github/workflows/dashboards-build-and-push-ghcr.yml | 4 +++- .github/workflows/dashboards-helper-build-and-push-ghcr.yml | 4 +++- .github/workflows/file-monitor-build-and-push-ghcr.yml | 4 +++- .github/workflows/file-upload-build-and-push-ghcr.yml | 4 +++- .github/workflows/filebeat-build-and-push-ghcr.yml | 4 +++- .github/workflows/freq-build-and-push-ghcr.yml | 4 +++- .github/workflows/htadmin-build-and-push-ghcr.yml | 4 +++- .github/workflows/logstash-build-and-push-ghcr.yml | 4 +++- .github/workflows/netbox-build-and-push-ghcr.yml | 4 +++- .github/workflows/nginx-build-and-push-ghcr.yml | 4 +++- .github/workflows/opensearch-build-and-push-ghcr.yml | 4 +++- .github/workflows/pcap-capture-build-and-push-ghcr.yml | 4 +++- .github/workflows/pcap-monitor-build-and-push-ghcr.yml | 4 +++- .github/workflows/postgresql-build-and-push-ghcr.yml | 4 +++- .github/workflows/redis-build-and-push-ghcr.yml | 4 +++- .github/workflows/suricata-build-and-push-ghcr.yml | 4 +++- .github/workflows/zeek-build-and-push-ghcr.yml | 4 +++- shared/bin/agg-init.sh | 2 ++ 20 files changed, 59 insertions(+), 19 deletions(-) diff --git a/.github/workflows/api-build-and-push-ghcr.yml b/.github/workflows/api-build-and-push-ghcr.yml index 20c86a2dd..939007b3b 100644 --- a/.github/workflows/api-build-and-push-ghcr.yml +++ b/.github/workflows/api-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'api/**' - 'Dockerfiles/api.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/arkime-build-and-push-ghcr.yml b/.github/workflows/arkime-build-and-push-ghcr.yml index 120e014b3..32016cab4 100644 --- a/.github/workflows/arkime-build-and-push-ghcr.yml +++ b/.github/workflows/arkime-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'arkime/**' - 'Dockerfiles/arkime.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/dashboards-build-and-push-ghcr.yml b/.github/workflows/dashboards-build-and-push-ghcr.yml index 442da4eef..1452ed146 100644 --- a/.github/workflows/dashboards-build-and-push-ghcr.yml +++ b/.github/workflows/dashboards-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'dashboards/**' - 'Dockerfiles/dashboards.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/dashboards-helper-build-and-push-ghcr.yml b/.github/workflows/dashboards-helper-build-and-push-ghcr.yml index aeaf743fb..7f8bf5804 100644 --- a/.github/workflows/dashboards-helper-build-and-push-ghcr.yml +++ b/.github/workflows/dashboards-helper-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'dashboards/**' - 'Dockerfiles/dashboards-helper.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/file-monitor-build-and-push-ghcr.yml b/.github/workflows/file-monitor-build-and-push-ghcr.yml index 37af5c097..bafd62550 100644 --- a/.github/workflows/file-monitor-build-and-push-ghcr.yml +++ b/.github/workflows/file-monitor-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'file-monitor/**' - 'Dockerfiles/file-monitor.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/file-upload-build-and-push-ghcr.yml b/.github/workflows/file-upload-build-and-push-ghcr.yml index b457b86ee..f1a5b3113 100644 --- a/.github/workflows/file-upload-build-and-push-ghcr.yml +++ b/.github/workflows/file-upload-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'file-upload/**' - 'Dockerfiles/file-upload.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/filebeat-build-and-push-ghcr.yml b/.github/workflows/filebeat-build-and-push-ghcr.yml index bb744d177..e52a73691 100644 --- a/.github/workflows/filebeat-build-and-push-ghcr.yml +++ b/.github/workflows/filebeat-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'filebeat/**' - 'Dockerfiles/filebeat.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/freq-build-and-push-ghcr.yml b/.github/workflows/freq-build-and-push-ghcr.yml index 12281f7a9..84a3254e0 100644 --- a/.github/workflows/freq-build-and-push-ghcr.yml +++ b/.github/workflows/freq-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'freq-server/**' - 'Dockerfiles/freq.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/htadmin-build-and-push-ghcr.yml b/.github/workflows/htadmin-build-and-push-ghcr.yml index 3af590976..eff564dee 100644 --- a/.github/workflows/htadmin-build-and-push-ghcr.yml +++ b/.github/workflows/htadmin-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'htadmin/**' - 'Dockerfiles/htadmin.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/logstash-build-and-push-ghcr.yml b/.github/workflows/logstash-build-and-push-ghcr.yml index a8d69eb27..6e8ed88cd 100644 --- a/.github/workflows/logstash-build-and-push-ghcr.yml +++ b/.github/workflows/logstash-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'logstash/**' - 'Dockerfiles/logstash.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/netbox-build-and-push-ghcr.yml b/.github/workflows/netbox-build-and-push-ghcr.yml index cbb2fa980..a8ecbc443 100644 --- a/.github/workflows/netbox-build-and-push-ghcr.yml +++ b/.github/workflows/netbox-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'netbox/**' - 'Dockerfiles/netbox.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/nginx-build-and-push-ghcr.yml b/.github/workflows/nginx-build-and-push-ghcr.yml index 77e379f70..4489ce37a 100644 --- a/.github/workflows/nginx-build-and-push-ghcr.yml +++ b/.github/workflows/nginx-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'nginx/**' - 'Dockerfiles/nginx.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/opensearch-build-and-push-ghcr.yml b/.github/workflows/opensearch-build-and-push-ghcr.yml index 177c8d1e1..c6faee5d6 100644 --- a/.github/workflows/opensearch-build-and-push-ghcr.yml +++ b/.github/workflows/opensearch-build-and-push-ghcr.yml @@ -5,10 +5,12 @@ on: branches: - main - development - - kubernetes paths: - 'Dockerfiles/opensearch.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/pcap-capture-build-and-push-ghcr.yml b/.github/workflows/pcap-capture-build-and-push-ghcr.yml index 2d4dc0c6e..057d4cfc9 100644 --- a/.github/workflows/pcap-capture-build-and-push-ghcr.yml +++ b/.github/workflows/pcap-capture-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'pcap-capture/**' - 'Dockerfiles/pcap-capture.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/pcap-monitor-build-and-push-ghcr.yml b/.github/workflows/pcap-monitor-build-and-push-ghcr.yml index 14d37084e..6a69b2bad 100644 --- a/.github/workflows/pcap-monitor-build-and-push-ghcr.yml +++ b/.github/workflows/pcap-monitor-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'pcap-monitor/**' - 'Dockerfiles/pcap-monitor.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/postgresql-build-and-push-ghcr.yml b/.github/workflows/postgresql-build-and-push-ghcr.yml index 6326f9fbc..703730e6d 100644 --- a/.github/workflows/postgresql-build-and-push-ghcr.yml +++ b/.github/workflows/postgresql-build-and-push-ghcr.yml @@ -5,10 +5,12 @@ on: branches: - main - development - - kubernetes paths: - 'Dockerfiles/postgresql.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/redis-build-and-push-ghcr.yml b/.github/workflows/redis-build-and-push-ghcr.yml index 29796936d..ed496a575 100644 --- a/.github/workflows/redis-build-and-push-ghcr.yml +++ b/.github/workflows/redis-build-and-push-ghcr.yml @@ -5,10 +5,12 @@ on: branches: - main - development - - kubernetes paths: - 'Dockerfiles/redis.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/suricata-build-and-push-ghcr.yml b/.github/workflows/suricata-build-and-push-ghcr.yml index 8086860d8..764a2737f 100644 --- a/.github/workflows/suricata-build-and-push-ghcr.yml +++ b/.github/workflows/suricata-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'suricata/**' - 'Dockerfiles/suricata.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/.github/workflows/zeek-build-and-push-ghcr.yml b/.github/workflows/zeek-build-and-push-ghcr.yml index 8dbebdb59..a48fe1e7c 100644 --- a/.github/workflows/zeek-build-and-push-ghcr.yml +++ b/.github/workflows/zeek-build-and-push-ghcr.yml @@ -5,11 +5,13 @@ on: branches: - main - development - - kubernetes paths: - 'zeek/**' - 'Dockerfiles/zeek.Dockerfile' - 'shared/bin/*' + - '!shared/bin/agg-init.sh' + - '!shared/bin/common-init.sh' + - '!shared/bin/sensor-init.sh' - '!shared/bin/configure-interfaces.py' - '!shared/bin/configure-capture.py' - '.trigger_workflow_build' diff --git a/shared/bin/agg-init.sh b/shared/bin/agg-init.sh index e3127b3c9..52263091d 100755 --- a/shared/bin/agg-init.sh +++ b/shared/bin/agg-init.sh @@ -13,6 +13,7 @@ if [[ -r "$SCRIPT_PATH"/common-init.sh ]]; then CleanDefaultAccounts MAIN_USER="$(id -nu 1000)" + MAIN_GROUP="$(id -ng 1000)" if [[ -n $MAIN_USER ]]; then # fix some permisions to make sure things belong to the right person @@ -25,6 +26,7 @@ if [[ -r "$SCRIPT_PATH"/common-init.sh ]]; then /usr/bin/env python3 "$MAIN_USER_HOME"/Malcolm/scripts/install.py --configure --defaults --restart-malcolm fi rm -f "$MAIN_USER_HOME"/Malcolm/firstrun "$MAIN_USER_HOME"/Malcolm/.configured + chown -R "$MAIN_USER":"$MAIN_GROUP" "$MAIN_USER_HOME"/Malcolm fi # make sure read permission is set correctly for the nginx worker processes From 5dc198723b2a4c36d475fe41585344df3a16507b Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 1 May 2023 07:37:44 -0600 Subject: [PATCH 235/239] fixes for aggregator ISO init --- shared/bin/agg-init.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shared/bin/agg-init.sh b/shared/bin/agg-init.sh index 52263091d..1fea56360 100755 --- a/shared/bin/agg-init.sh +++ b/shared/bin/agg-init.sh @@ -13,7 +13,6 @@ if [[ -r "$SCRIPT_PATH"/common-init.sh ]]; then CleanDefaultAccounts MAIN_USER="$(id -nu 1000)" - MAIN_GROUP="$(id -ng 1000)" if [[ -n $MAIN_USER ]]; then # fix some permisions to make sure things belong to the right person @@ -26,7 +25,7 @@ if [[ -r "$SCRIPT_PATH"/common-init.sh ]]; then /usr/bin/env python3 "$MAIN_USER_HOME"/Malcolm/scripts/install.py --configure --defaults --restart-malcolm fi rm -f "$MAIN_USER_HOME"/Malcolm/firstrun "$MAIN_USER_HOME"/Malcolm/.configured - chown -R "$MAIN_USER":"$MAIN_GROUP" "$MAIN_USER_HOME"/Malcolm + chown -R 1000:1000 "$MAIN_USER_HOME"/Malcolm fi # make sure read permission is set correctly for the nginx worker processes From 4d8c309196182a337fa3ff19234e43ae1a04da71 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 1 May 2023 11:29:19 -0600 Subject: [PATCH 236/239] fix sensor build --- .github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml | 2 +- sensor-iso/build_via_vagrant.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml b/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml index bd68526d0..af40ba3f8 100644 --- a/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml +++ b/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml @@ -91,7 +91,7 @@ jobs: name: Build image run: | cp -r ./shared ./docs ./_config.yml ./_includes ./_layouts ./Gemfile ./README.md ./sensor-iso - cp ./scripts/malcolm_utils.py ./sensor-iso/shared/ + cp ./scripts/malcolm_utils.py ./sensor-iso/shared/bin/ cp ./scripts/documentation_build.sh ./sensor-iso/docs/ cp -r ./arkime/patch ./sensor-iso/shared/arkime_patch pushd ./sensor-iso diff --git a/sensor-iso/build_via_vagrant.sh b/sensor-iso/build_via_vagrant.sh index 563f53c4d..73aa5b738 100755 --- a/sensor-iso/build_via_vagrant.sh +++ b/sensor-iso/build_via_vagrant.sh @@ -86,7 +86,7 @@ cp -r "$SCRIPT_PATH"/../shared \ "$SCRIPT_PATH"/../Gemfile \ "$SCRIPT_PATH"/../README.md "$SCRIPT_PATH"/ cp "$SCRIPT_PATH"/../scripts/documentation_build.sh "$SCRIPT_PATH"/docs/ -cp "$SCRIPT_PATH"/../scripts/malcolm_utils.py "$SCRIPT_PATH"/shared/ +cp "$SCRIPT_PATH"/../scripts/malcolm_utils.py "$SCRIPT_PATH"/shared/bin/ YML_IMAGE_VERSION="$(grep -P "^\s+image:\s*malcolm" "$SCRIPT_PATH"/../docker-compose-standalone.yml | awk '{print $2}' | cut -d':' -f2 | uniq -c | sort -nr | awk '{print $2}' | head -n 1)" [[ -n $YML_IMAGE_VERSION ]] && echo "$YML_IMAGE_VERSION" > "$SCRIPT_PATH"/shared/version.txt From cb8229fe80a061199408666068610ec779d322c1 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 1 May 2023 11:59:15 -0600 Subject: [PATCH 237/239] fix mkdir for zeek_intel_setup.sh --- shared/bin/zeek_intel_setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/bin/zeek_intel_setup.sh b/shared/bin/zeek_intel_setup.sh index e27181a34..c02955e02 100755 --- a/shared/bin/zeek_intel_setup.sh +++ b/shared/bin/zeek_intel_setup.sh @@ -25,7 +25,7 @@ function finish { rmdir -- "$LOCK_DIR" || echo "Failed to remove lock directory '$LOCK_DIR'" >&2 } -if mkdir -- "$LOCK_DIR" 2>/dev/null; then +if mkdir -p -- "$LOCK_DIR" 2>/dev/null; then trap finish EXIT # create directive to @load every subdirectory in /opt/zeek/share/zeek/site/intel From 418456d7ae4386b13df8f0f050358caaebd9abcf Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 1 May 2023 12:24:24 -0600 Subject: [PATCH 238/239] fix sensor build --- sensor-iso/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/sensor-iso/build.sh b/sensor-iso/build.sh index 151fe59f0..3438477eb 100755 --- a/sensor-iso/build.sh +++ b/sensor-iso/build.sh @@ -115,6 +115,7 @@ if [ -d "$WORKDIR" ]; then rsync -a "$SCRIPT_PATH/shared/bin/" ./config/includes.chroot/usr/local/bin/ mkdir -p ./config/includes.chroot/opt/zeek/bin/ mv ./config/includes.chroot/usr/local/bin/zeekdeploy.sh ./config/includes.chroot/opt/zeek/bin/ + ln -s -r ./config/includes.chroot/usr/local/bin/malcolm_utils.py ./config/includes.chroot/opt/zeek/bin/ chown -R root:root ./config/includes.chroot/usr/local/bin/ ./config/includes.chroot/opt/zeek/bin/ # write out some version stuff specific to this installation version From c3cda80de1130d45bfaff9e4f4905d0daec993d7 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 1 May 2023 14:53:26 -0600 Subject: [PATCH 239/239] update sha256 sums --- docs/download.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/download.md b/docs/download.md index d2b26d4b1..a9c6b154f 100644 --- a/docs/download.md +++ b/docs/download.md @@ -16,7 +16,7 @@ While official downloads of the Malcolm installer ISO are not provided, an **uno | ISO | SHA256 | |---|---| -| [malcolm-23.05.0.iso](/iso/malcolm-23.05.0.iso) (5.3GiB) | [`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`](/iso/malcolm-23.05.0.iso.sha256.txt) | +| [malcolm-23.05.0.iso](/iso/malcolm-23.05.0.iso) (5.3GiB) | [`e9e00694f25b9d0dcc286496490e184930611ddbed6c52dfab77a935d2afa850`](/iso/malcolm-23.05.0.iso.sha256.txt) | ## Hedgehog Linux @@ -26,7 +26,7 @@ While official downloads of the Malcolm installer ISO are not provided, an **uno | ISO | SHA256 | |---|---| -| [hedgehog-23.05.0.iso](/iso/hedgehog-23.05.0.iso) (2.3GiB) | [`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`](/iso/hedgehog-23.05.0.iso.sha256.txt) | +| [hedgehog-23.05.0.iso](/iso/hedgehog-23.05.0.iso) (2.3GiB) | [`f850ecd3b62731b46ac0366bdcdd62437da30220c23f94013873c6c92cbddff7`](/iso/hedgehog-23.05.0.iso.sha256.txt) | ## Warning

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 literal 0 HcmV?d00001 diff --git a/docs/images/screenshots/iso_install_malcolm_iso_menu_1.png b/docs/images/screenshots/iso_install_malcolm_iso_menu_1.png new file mode 100644 index 0000000000000000000000000000000000000000..1494f689978a240bc2e9c9719ee74ea3103c322a GIT binary patch literal 91882 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∨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 literal 0 HcmV?d00001 diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md new file mode 100644 index 000000000..f5b8f20e5 --- /dev/null +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -0,0 +1,124 @@ +# End-to-end Malcolm and Hedgehog Linux ISO Installation + +This document outlines how to install [Malcolm]({{ site.github.repository_url }}) and [Hedgehog Linux](hedgehog.md) using the project's installer ISOs. These instructions apply to installing this software both on a "bare metal" system or in a virtual machine environment using VMware, VirtualBox, QEMU/KVM, etc. + +The Malcolm and Hedgehog Linux installers as described in these instructions are intended to be used to **replace** the existing operating system, if any, of the respective systems onto which they are installed, and, as such, are designed to require as little user input as possible. For this reason, there are NO user prompts and confirmations about partitioning and reformatting hard disks for use by the operating system. The installer assumes that all non-removable storage media (eg., SSD, HDD, NVMe, etc.) are available for use and ⛔🆘😭💀 ***will partition and format them without warning*** 💀😭🆘⛔. + +In contrast to using the ISO installer, Malcolm can also be installed "natively" on any x86_64 platform that can run Docker. See the [installation example using Ubuntu 22.04 LTS](ubuntu-install-example.md#InstallationExample) for that method of installation and configuration. + +### Table of Contents + +* [Obtaining the Installation ISOs](#ISODownload) +* ["Burning" the Installation ISOs to USB Flash Drive](#ISOBurning) +* [Booting the Installation Media](#BootUSB) +* [Malcolm Installation and Configuration](#MalcolmInstallAndConfig) + - [Malcolm ISO Installation](#ISOInstallMalcolm) +* [Hedgehog Linux Installation and Configuration](#HedgehogInstallAndConfig) + - [Hedgehog Linux ISO Installation](#ISOInstallHedgehog) + +## Obtaining the Installation ISOs + +Malcolm can be [packaged](malcolm-iso.md#ISOBuild) into an [installer ISO](malcolm-iso.md#ISO) based on the current [stable release](https://wiki.debian.org/DebianStable) of [Debian](https://www.debian.org/). This [customized Debian installation](https://wiki.debian.org/DebianLive) is preconfigured with the bare minimum software needed to run Malcolm. + +Similar instructions exist for generating the [installer ISO](hedgehog-iso-build.md#HedgehogISOBuild) for [Hedgehog Linux](hedgehog.md), Malcolm's dedicated network sensor appliance OS. + +While official downloads of the Malcolm installer ISO are not provided, an **unofficial build** of the ISO installer for the [latest stable release]({{ site.external_download_url }}) is available for download here. If downloading the unofficial builds, be sure to verify the integrity of ISO files against the SHA256 sums provided on the download page. + +## "Burning" the Installation ISOs to USB Flash Drive + +Various methods can be used to write the contents of an installer ISO image to a USB flash drive. One simple free and open source application for doing so [Etcher](https://www.balena.io/etcher), which can be used on Windows, macOS and Linux platforms. + +Alternatively, specific instructions may be provided by your operating system (e.g., [Arch Linux](https://wiki.archlinux.org/title/USB_flash_installation_medium), [Debian Linux](https://www.debian.org/releases/stable/amd64/ch04s03.en.html), [Ubuntu Linux](https://ubuntu.com/tutorials/create-a-usb-stick-on-ubuntu#1-overview)). + +Using one of these methods, write the Malcolm and Hedgehog Linux installer ISOs to two 8GB or larger USB flash drives, respectively. + +Alternatively, the ISO images could be burned to writable optical media (e.g., DVD±R). For the Malcolm installer you'll likely have to use DVD±R DL ("dual layer" or "double layer") DVD media as the installer ISO exceeds the 4.7 GB storage provided by standard DVDs. + +![Etcher on macOS](./images/screenshots/iso_install_etcher_macos.png) + +*Using Etcher on macOS* + +![dd on Linux](./images/screenshots/iso_install_dd_linux.png) + +*Using dd on Linux* + +## Booting the Installation Media + +The ISO media boot on systems that support EFI-mode and legacy (BIOS) booting. Configuring your system's firmware to allow booting from USB or optical media will vary from manufacturer to manufacturer. Usually manufacturers will provide a one-time boot options menu upon a specific keypress (e.g., F12 for Dell, F9 for HP, etc.). If needed, consult the documentation provided by the hardware manufacturer on how to access the boot options menu and boot from your newly-burned USB flash media or DVD±R. + +![EFI Boot Manager](./images/screenshots/iso_install_boot_menu_efi.png) + +*An example of an EFI boot manager in QEMU* + +![BIOS Boot Manager](./images/screenshots/iso_install_boot_menu_bios.png) + +*An example of a BIOS boot options menu in QEMU* + +## Malcolm Installation and Configuration + +### Malcolm ISO Installation + +Upon Booting the Malcolm installation ISO, you're presented with the following **Boot menu**. Use the arrow keys to select **Install Malcolm**, and press Enter. + +![](./images/screenshots/iso_install_malcolm_iso_menu_1.png) + +*The first screen of the installer* + +The next screen of the installer presents the following options relevant to installation: + +* **Quick Install** - Installs Malcolm without full disk encryption using default partitioning. +* **Encrypted Quick Install** - Installs Malcolm with full disk encryption using default partitioning. You will be prompted for a password for full disk encryption during installation which must be entered each time the system boots. +* **Expert Install** - Allows you to configure the options of the [Debian](https://wiki.debian.org/DebianInstaller)-based installation system. Only recommended when needed for expert Linux users. +* **Virtual Machine Single Partition Quick Install** - The same as **Quick Install** except that all system files are stored in a single partition. Use this option when installing Malcolm onto a virtual machine. + +![](./images/screenshots/iso_install_malcolm_iso_menu_2.png) + +*The **Install Malcolm** menu* + +After making your selection for the type of Malcolm install to perform, the installer will ask for several pieces of information prior to installing the Malcolm base operating system: + +* **Hostname** - the name of the Malcolm system used to identify itself on the network +* **Domain name** - (optional) the name of the local network domain +* **Root password** – (optional) a password for the privileged root account which is rarely needed; if unspecified, the non-privileged user account will be added to the `sudo` group +* **User name** the name for the non-privileged service account user account under which the Malcolm runs +* **User password** – a password for the non-privileged user account +* **Encryption password** – (optional) if the encrypted installation option was selected at boot, the encryption password must be entered every time the system boots + +![Example of the installer's password prompt](./images/hedgehog/images/users_and_passwords.png) + +After the passwords have been entered, the installer will proceed to format the system drive and install Malcolm. + +![Installer progress](./images/hedgehog/images/installer_progress.png) + +At the end of the installation process, you will be prompted with a few self-explanatory yes/no questions: + +* **Disable IPv6?** +* **Automatically login to the GUI session?** +* **Should the GUI session be locked due to inactivity?** +* **Display the [Standard Mandatory DoD Notice and Consent Banner](https://www.stigviewer.com/stig/application_security_and_development/2018-12-24/finding/V-69349)?** *(only applies when installed on U.S. government information systems)* + +Following these prompts, the installer will reboot and the Malcolm base operating system will boot. + +The Malcolm installer does not require an internet connection to complete successfully. If the installer prompts you to configure network connectivity, you may choose "do not configure the network at this time." + +## Hedgehog Linux Installation and Configuration + +## Hedgehog Linux ISO Installation + +The Hedgehog Linux installation ISO follows the same process as the [Malcolm installation](#ISOInstallMalcolm) above. + +The installer will ask for a few pieces of information prior to installing Hedgehog Linux: + +* **Root password** – a password for the privileged root account which is rarely needed (only during the configuration of the sensors network interfaces and setting the sensor host name) +* **User password** – a password for the non-privileged `sensor` account under which the various sensor capture and forwarding services run +* **Encryption password** – (optional) if the encrypted installation option was selected at boot, the encryption password must be entered every time the sensor boots + +At the end of the installation process, you will be prompted with a few self-explanatory yes/no questions: + +* **Disable IPv6?** +* **Automatically login to the GUI session?** +* **Should the GUI session be locked due to inactivity?** +* **Display the [Standard Mandatory DoD Notice and Consent Banner](https://www.stigviewer.com/stig/application_security_and_development/2018-12-24/finding/V-69349)?** *(only applies when installed on U.S. government information systems)* + +Following these prompts, the installer will reboot and Hedgehog Linux will boot. + From 55ce7eb2cf86b94af23158a5512bd4f8b9c42a08 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 10 Apr 2023 14:27:36 -0600 Subject: [PATCH 116/239] documentation wip --- docs/images/screenshots/malcolm_desktop.png | Bin 0 -> 173600 bytes .../screenshots/malcolm_first_boot_config.png | Bin 0 -> 57811 bytes docs/malcolm-hedgehog-e2e-iso-install.md | 26 +++++++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 docs/images/screenshots/malcolm_desktop.png create mode 100644 docs/images/screenshots/malcolm_first_boot_config.png diff --git a/docs/images/screenshots/malcolm_desktop.png b/docs/images/screenshots/malcolm_desktop.png new file mode 100644 index 0000000000000000000000000000000000000000..a93168d43d0c0c6f052b5e9679938b9ab10dbf41 GIT binary patch literal 173600 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 jW;SgI?a~%P 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 GWBv!=UpMjq literal 0 HcmV?d00001 diff --git a/docs/images/screenshots/malcolm_first_boot_config.png b/docs/images/screenshots/malcolm_first_boot_config.png new file mode 100644 index 0000000000000000000000000000000000000000..f207485b8be41b7e60105b837bff6ff48a93d845 GIT binary patch literal 57811 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 literal 0 HcmV?d00001 diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md index f5b8f20e5..e20f47106 100644 --- a/docs/malcolm-hedgehog-e2e-iso-install.md +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -12,7 +12,10 @@ In contrast to using the ISO installer, Malcolm can also be installed "natively" * ["Burning" the Installation ISOs to USB Flash Drive](#ISOBurning) * [Booting the Installation Media](#BootUSB) * [Malcolm Installation and Configuration](#MalcolmInstallAndConfig) - - [Malcolm ISO Installation](#ISOInstallMalcolm) + - [ISO Installation](#ISOInstallMalcolm) + - [System](#MalcolmSystem) + - [Configuration](#MalcolmConfig) + - [Setting up Authentication](#MalcolmAuthSetup) * [Hedgehog Linux Installation and Configuration](#HedgehogInstallAndConfig) - [Hedgehog Linux ISO Installation](#ISOInstallHedgehog) @@ -101,6 +104,27 @@ Following these prompts, the installer will reboot and the Malcolm base operatin The Malcolm installer does not require an internet connection to complete successfully. If the installer prompts you to configure network connectivity, you may choose "do not configure the network at this time." +### Malcolm System + +The Malcolm base operating system is a [hardened](hardening.md#Hardening) Linux installation based on the current [stable release](https://wiki.debian.org/DebianStable) of [Debian](https://www.debian.org/) [running](https://wiki.debian.org/Xfce) the [XFCE desktop environment](https://www.xfce.org/). It has been preloaded with all of the [components](components.md#Components) that make up Malcolm. + +[NetworkManager](https://wiki.debian.org/NetworkManager) can be used to configure networking for Malcolm. NetworkManager can be configured by clicking the 🖧 (networked computers) icon in the system tray in the upper-right corner of the screen, or right-clicking the icon and selecting **Edit Connections...** to modify the properties of a given connection. + +Display resolution should be detected and adjusted automatically. If you need to make changes to display properties, click the **Applications** menu and select **Settings** → **Display**. + +The panel bordering the top of the Malcolm desktop is home to a number of useful shortcuts: + +![Malcolm Desktop](./images/screenshots/malcolm_desktop.png) + + +### Malcolm Configuration + +The first time the Malcolm base operating system boots the **Malcolm Configuration** wizard will start automatically. This same configuration script can be run again later by running [`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning) from the Malcolm installation directory. + +![Malcolm Configuration on first boot](./images/screenshots/malcolm_first_boot_config.png) + +### Setting up Authentication for Malcolm + ## Hedgehog Linux Installation and Configuration ## Hedgehog Linux ISO Installation From db7adfe6fabc687ae3e97f0f6b427aa671672e82 Mon Sep 17 00:00:00 2001 From: SG Date: Mon, 10 Apr 2023 15:32:35 -0600 Subject: [PATCH 117/239] documentation wip --- docs/malcolm-hedgehog-e2e-iso-install.md | 84 +++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md index e20f47106..977bc4102 100644 --- a/docs/malcolm-hedgehog-e2e-iso-install.md +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -119,10 +119,92 @@ The panel bordering the top of the Malcolm desktop is home to a number of useful ### Malcolm Configuration -The first time the Malcolm base operating system boots the **Malcolm Configuration** wizard will start automatically. This same configuration script can be run again later by running [`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning) from the Malcolm installation directory. +The first time the Malcolm base operating system boots the **Malcolm Configuration** wizard will start automatically. This same configuration script can be run again later by running [`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning) from the Malcolm installation directory, or clicking the **Configure Malcolm** 🔳 icon in the top panel. ![Malcolm Configuration on first boot](./images/screenshots/malcolm_first_boot_config.png) +The [configuration and tuning](malcolm-config.md#ConfigAndTuning) wizard's questions proceed as follows. Note that you may not necessarily see every question listed here depending on how you answered earlier questions. Usually the default selection is what you'll want to select unless otherwise indicated below. + +* Malcolm processes will run as UID 1000 and GID 1000. Is this OK? + - Docker runs all of its containers as the privileged `root` user by default. For better security, Malcolm immediately drops to non-privileged user accounts for executing internal processes wherever possible. The `PUID` (**p**rocess **u**ser **ID**) and `PGID` (**p**rocess **g**roup **ID**) environment variables allow Malcolm to map internal non-privileged user accounts to a corresponding [user account](https://en.wikipedia.org/wiki/User_identifier) on the host. +* Should Malcolm use and maintain its own OpenSearch instance? + - Malcolm's default standalone configuration is to use a local [OpenSearch](https://opensearch.org/) instance in a Docker container to index and search network traffic metadata. See [OpenSearch instances](opensearch-instances.md#OpenSearchInstance) for more information about using a remote OpenSearch cluster instead. +* Forward Logstash logs to a secondary remote OpenSearch instance? + - Whether the primary OpenSearch instance is a locally maintained single-node instance or is a remote cluster, Malcolm can be configured additionally forward logs to a secondary remote OpenSearch instance. See [OpenSearch instances](opensearch-instances.md#OpenSearchInstance) for more information about forwarding logs to another OpenSearch instance. +* Setting 16g for OpenSearch and 3g for Logstash. Is this OK? + - Two of Malcolm's main components, OpenSearch and Logstash, require a substantial amount of memory to be set aside for their use. The configuration script will suggest defaults for these values based on the amount of physical memory the system has. The minimum recommended amount of system memory for Malcolm is 16 gigabytes. For a pleasant experience, I would suggest not using a value under 10 gigabytes for OpenSearch and 2500 megabytes for Logstash. +* Setting 3 workers for Logstash pipelines. Is this OK? + - This setting is used to tune the performance and resource utilization of the the `logstash` container. The default is calculated based on the number of logical CPUs the system has. See [Tuning and Profiling Logstash Performance](https://www.elastic.co/guide/en/logstash/current/tuning-logstash.html), [`logstash.yml`](https://www.elastic.co/guide/en/logstash/current/logstash-settings-file.html) and [Multiple Pipelines](https://www.elastic.co/guide/en/logstash/current/multiple-pipelines.html). +* Restart Malcolm upon system or Docker daemon restart? + - This question allows you to configure Docker's [restart policy](https://docs.docker.com/config/containers/start-containers-automatically/#use-a-restart-policy) for Malcolm (ie., the behavior used to restart Malcolm should the system be shut down or rebooted, or should one of Malcolm's components should crash). Possible options are: + + no - do not automatically restart the container + + on-failure - restart the container if it exits due to an error, which manifests as a non-zero exit code + + always - always restart the container if it stops + + unless-stopped - similar to always, except that when the container is stopped (manually or otherwise), it is not restarted even after Docker daemon restarts; this is usually a good choice +* Require encrypted HTTPS connections? + - Malcolm uses [TLS](authsetup.md#TLSCerts) encryption for its web browser-accessible user interfaces. Answering **Y** to this question is almost always what you want. The only situation in which you might want to answer **N** is if you are running Malcolm behind a third-party reverse proxy (e.g., [Traefik](https://doc.traefik.io/traefik/) or [Caddy](https://caddyserver.com/docs/quick-starts/reverse-proxy)) to handle the issuance of the certificates for you and to broker the connections between clients and Malcolm. Reverse proxies such as these often implement the [ACME](https://datatracker.ietf.org/doc/html/rfc8555) protocol for domain name authentication and can be used to request certificates from certificate authorities like [Let's Encrypt](https://letsencrypt.org/how-it-works/). In this configuration, the reverse proxy will be encrypting the connections instead of Malcolm. **Make sure** you understand what you are doing and ensure that external connections cannot reach ports over which Malcolm will be communicating without encryption, including verifying your local firewall configuration, should you choose to answer **N** to this question. +* Will Malcolm be running behind another reverse proxy (Traefik, Caddy, etc.)? + - See the previous question. If Malcolm is configured behind a remote proxy, Malcolm can prompt you to *Configure labels for Traefik?* to allow it to identify itself to Traefik. +* Specify external Docker network name (or leave blank for default networking) + - This allows you to configure Malcolm to use [custom Docker networks](https://docs.docker.com/compose/networking/#specify-custom-networks). Leave this blank unless you know you want to do otherwise. +* Authenticate against Lightweight Directory Access Protocol (LDAP) server? + - Answer **N** to use Malcolm's own built-in [local account management](authsetup.md#AuthBasicAccountManagement), or **Y** to use [Lightweight Directory Access Protocol (LDAP) authentication](authsetup.md#AuthLDAP). +* Select LDAP server compatibility type + - This question allows you to specify Microsoft Active Directory compatibility (**winldap**) or generic LDAP compatibility (**openldap**, for OpenLDAP, glauth, etc.) when using [LDAP authentication](authsetup.md#AuthLDAP) +* Use StartTLS (rather than LDAPS) for LDAP connection security? + - When using LDAP authentication, this question allows you to configure [LDAP connection security](authsetup.md#AuthLDAPSecurity) +* Store PCAP, log and index files locally under /home/user/Malcolm? + - Malcolm generates a number of large file sets during normal operation: PCAP files, Zeek or Suricata logs, OpenSearch indices, etc. By default all of these are stored in subdirectories in the Malcolm installation directory. This question allows you to specify alternative storage location(s) (for example, a separate dedicated drive or RAID volume) for these artifacts. +* Compress OpenSearch index snapshots? + - Choose whether OpenSearch [index snapshots](https://opensearch.org/docs/2.6/tuning-your-cluster/availability-and-recovery/snapshots/snapshot-management/) should be compressed or not, should you opt to configure them later in [OpenSearch index management](index-management.md#IndexManagement). +* Delete the oldest indices when the database exceeds a certain size? + - Most of the configuration around OpenSearch [Index State Management](https://opensearch.org/docs/latest/im-plugin/ism/index/) and [Snapshot Management](https://opensearch.org/docs/latest/opensearch/snapshots/sm-dashboards/) can be done in OpenSearch Dashboards. In addition to (or instead of) the OpenSearch index state management operations, Malcolm can also be configured to delete the oldest network session metadata indices when the database exceeds a certain size to prevent filling up all available storage with OpenSearch indices. +* Automatically analyze all PCAP files with Suricata? + - This option is used to enable [Suricata](https://suricata.io/) (an IDS and threat detection engine) to analyze PCAP files uploaded to Malcolm via its upload web interface. +* Download updated Suricata signatures periodically? + - This option is used to [enable automatic updates](https://suricata-update.readthedocs.io/en/latest/) of the Suricata rules used by Malcolm. +* Automatically analyze all PCAP files with Zeek? + - This option is used to enable [Zeek](https://www.zeek.org/index.html) (a network analysis framework and IDS) to analyze PCAP files uploaded to Malcolm via its upload web interface +* Perform reverse DNS lookup locally for source and destination IP addresses in logs? +* Perform hardware vendor OUI lookups for MAC addresses? +* Perform string randomness scoring on some fields? +* Expose OpenSearch port to external hosts? +* Expose Logstash port to external hosts? +* Expose Filebeat TCP port to external hosts? +* Select log format for messages sent to Filebeat TCP listener +* Source field to parse for messages sent to Filebeat TCP listener +* Target field under which to store decoded JSON fields for messages sent to Filebeat TCP listener +* Field to drop from events sent to Filebeat TCP listener +* Tag to apply to messages sent to Filebeat TCP listener +* Expose SFTP server (for PCAP upload) to external hosts? +* Enable file extraction with Zeek? +* Select file extraction behavior +* Select file preservation behavior +* Expose web interface for downloading preserved files? +* Enter AES-256-CBC encryption password for downloaded preserved files (or leave blank for unencrypted) +* Scan extracted files with ClamAV? +* Scan extracted files with Yara? +* Scan extracted PE files with Capa? +* Lookup extracted file hashes with VirusTotal? +* Enter VirusTotal API key +* Download updated file scanner signatures periodically? +* Should Malcolm run and maintain an instance of NetBox, an infrastructure resource modeling tool? +* Should Malcolm enrich network traffic using NetBox? +* Specify default NetBox site name +* Should Malcolm capture live network traffic to PCAP files for analysis with Arkime? +* Capture packets using netsniff-ng? +* Capture packets using tcpdump? +* Should Arkime delete PCAP files based on available storage? + - OpenSearch index state management and snapshot management only deals with disk space consumed by OpenSearch indices: it does not have anything to do with PCAP file storage. This option can be used to also allow Arkime to prune old PCAP files based on available disk space (see https://arkime.com/faq#pcap-deletion). +* Should Malcolm analyze live network traffic with Suricata? +* Should Malcolm analyze live network traffic with Zeek? +* Should Malcolm use "best guess" to identify potential OT/ICS traffic with Zeek? +* Specify capture interface(s) (comma-separated) +* Capture filter (tcpdump-like filter expression; leave blank to capture all traffic) + - `not port 5044 and not port * 8005 and not port 9200` +* Disable capture interface hardware offloading and adjust ring buffer sizes? +* Enable dark mode for OpenSearch Dashboards? + ### Setting up Authentication for Malcolm ## Hedgehog Linux Installation and Configuration From 81fa4e80afd10462d6e92277bc7cab510865657e Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 11 Apr 2023 11:57:58 -0600 Subject: [PATCH 118/239] documentation wip --- docs/malcolm-hedgehog-e2e-iso-install.md | 51 ++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md index 977bc4102..a697517ce 100644 --- a/docs/malcolm-hedgehog-e2e-iso-install.md +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -162,48 +162,91 @@ The [configuration and tuning](malcolm-config.md#ConfigAndTuning) wizard's quest * Automatically analyze all PCAP files with Suricata? - This option is used to enable [Suricata](https://suricata.io/) (an IDS and threat detection engine) to analyze PCAP files uploaded to Malcolm via its upload web interface. * Download updated Suricata signatures periodically? - - This option is used to [enable automatic updates](https://suricata-update.readthedocs.io/en/latest/) of the Suricata rules used by Malcolm. + - If your Malcolm instance has internet connectivity, answer **Y** to [enable automatic updates](https://suricata-update.readthedocs.io/en/latest/) of the Suricata rules used by Malcolm. * Automatically analyze all PCAP files with Zeek? - - This option is used to enable [Zeek](https://www.zeek.org/index.html) (a network analysis framework and IDS) to analyze PCAP files uploaded to Malcolm via its upload web interface + - This option is used to enable [Zeek](https://www.zeek.org/index.html) (a network analysis framework and IDS) to analyze PCAP files uploaded to Malcolm via its upload web interface. * Perform reverse DNS lookup locally for source and destination IP addresses in logs? + - If enabled, this option will perform reverse [DNS lookups](https://www.elastic.co/guide/en/logstash/current/plugins-filters-dns.html) on IP addresses found in traffic and use the results to enrich network logs. Answer **Y** if your Malcolm instance has access to a DNS server to perform these lookups. * Perform hardware vendor OUI lookups for MAC addresses? + - Malcolm will [map MAC addresses](https://standards.ieee.org/products-programs/regauth/) to hardware manufacturer when possible. You probably want to answer **Y** to this question. * Perform string randomness scoring on some fields? + - If enabled, domain names observed in network traffic (from DNS queries and SSL server names) will be assigned entropy scores as calculated by [`freq`](https://github.com/MarkBaggett/freq). You probably want to answer **Y** to this question. * Expose OpenSearch port to external hosts? + - Answer **Y** in order for Malcolm's firewall to allow connections from a remote log forwarder (such as Hedgehog Linux) to TCP port 9200 so that Arkime sessions can be written to Malcolm's OpenSearch database. * Expose Logstash port to external hosts? + - Answer **Y** in order for Malcolm's firewall to allow connections from a remote log forwarder (such as Hedgehog Linux) to TCP port 5044 so that Zeek and Suricata logs can be ingested by Malcolm's Logstash instance. * Expose Filebeat TCP port to external hosts? + - Answer **Y** in order for Malcolm's firewall to allow connections from a remote log forwarder (such as Hedgehog Linux for resource utilization metrics or other forwarders for other [third-Party logs](third-party-logs.md#ThirdPartyLogs)) to TCP port 5045. * Select log format for messages sent to Filebeat TCP listener + - Possible choices include `json` and `raw`; you probably want to choose `json`. * Source field to parse for messages sent to Filebeat TCP listener + - The default choice (and the one Hedgehog Linux will be sending) is `message`. * Target field under which to store decoded JSON fields for messages sent to Filebeat TCP listener + - The default choice (and the one that corresponds to Malcolm's dashboards built for the resource utilization metrics sent by Hedgehog Linux) is `miscbeat`. * Field to drop from events sent to Filebeat TCP listener + - You most likely want this to be the default, `message`, to match the field name specified above. * Tag to apply to messages sent to Filebeat TCP listener + - The default is `_malcolm_beats`, which is used by Malcolm to recognize and parse metrics sent from Hedgehog Linux. * Expose SFTP server (for PCAP upload) to external hosts? + - Answer **N** unless you plan to use SFTP/SCP to [upload](upload.md#Upload) PCAP files to Malcolm; answering **Y** will expose TCP port 8022 in Malcolm's firewall for SFTP/SCP connections * Enable file extraction with Zeek? + - Answer **Y** to indicate that Zeek should [extract files](file-scanning.md#ZeekFileExtraction) transfered in observed network traffic. * Select file extraction behavior + - This determines which files Zeek should extract for scanning: + + `none`: no file extraction + + `interesting`: extraction of files with mime types of common attack vectors + + `mapped`: extraction of files with recognized mime types + + `known`: extraction of files for which any mime type can be determined + + `all`: extract all files * Select file preservation behavior + - This determines the behavior for preservation of Zeek-extracted files: + + `quarantined`: preserve only flagged files in `./zeek-logs/extract_files/quarantine` + + `all`: preserve flagged files in `./zeek-logs/extract_files/quarantine` and all other extracted files in `./zeek-logs/extract_files/preserved` + + `none`: preserve no extracted files * Expose web interface for downloading preserved files? + - Answering **Y** enables access to the Zeek-extracted files path through the means of a simple HTTPS directory server at `https:///extracted-files/`. Beware that Zeek-extracted files may contain malware. * Enter AES-256-CBC encryption password for downloaded preserved files (or leave blank for unencrypted) + - If a password is specified here, Zeek-extracted files downloaded as described under the previous question will be AES-256-CBC-encrypted in an `openssl enc`-compatible format (e.g., `openssl enc -aes-256-cbc -d -in example.exe.encrypted -out example.exe`). * Scan extracted files with ClamAV? + - Answer **Y** to scan extracted files with [ClamAV](https://www.clamav.net/), an antivirus engine. * Scan extracted files with Yara? + - Answer **Y** to scan extracted files with [Yara](https://github.com/VirusTotal/yara), a tool used to identify and classify malware samples. * Scan extracted PE files with Capa? + - Answer **Y** to scan extracted executable files with [Capa](https://github.com/fireeye/capa), a tool for detecting capabilities in executable files. * Lookup extracted file hashes with VirusTotal? + - Answer **Y** to be prompted for your [**VirusTotal**](https://www.virustotal.com/en/#search) API key which will be used for submitting the hashes of extracted files. Only specify this option if your Malcolm instance has internet connectivity. * Enter VirusTotal API key + - Specify your [**VirusTotal**](https://www.virustotal.com/en/#search) [API key](https://support.virustotal.com/hc/en-us/articles/115002100149-API) as indicated under the previous question. * Download updated file scanner signatures periodically? + - If your Malcolm instance has internet connectivity, answer **Y** to enable periodic downloads of signatures used by ClamAV and YARA. * Should Malcolm run and maintain an instance of NetBox, an infrastructure resource modeling tool? + - Answer **Y** if you would like to use [NetBox](https://netbox.dev/), a suite for modeling and documenting modern networks, to maintain an inventory of your network assets. * Should Malcolm enrich network traffic using NetBox? + - Answer **Y** to [cross-reference](asset-interaction-analysis.md#AssetInteractionAnalysis) network traffic logs your NetBox asset inventory. * Specify default NetBox site name + - NetBox has the concept of [sites](https://demo.netbox.dev/static/docs/core-functionality/sites-and-racks/). Sites can have overlapping IP address ranges, of course. This default site name will be used as a query parameter for these enrichment lookups. * Should Malcolm capture live network traffic to PCAP files for analysis with Arkime? + - Malcolm itself can perform [live analysis](live-analysis.md#LocalPCAP) of traffic it sees on another network interface (ideally not the same one used for its management). If you are using Hedgehog Linux you probably want to answer **N** to this question. If you want Malcolm to observe and capture traffic instead of or in addition to a sensor running Hedgehog Linux, answer **Y**. * Capture packets using netsniff-ng? + - Answer **Y** for Malcolm to [capture network traffic](live-analysis.md#LocalPCAP) on the local network interface(s) indicated using [netsniff-ng](http://netsniff-ng.org/) (instead of tcpdump). These PCAP files are then periodically rotated into Arkime for analysis. netsniff-ng is Malcolm's preferred tool for capturing network traffic. * Capture packets using tcpdump? + - Answer **Y** for Malcolm to [capture network traffic](live-analysis.md#LocalPCAP) on the local network interface(s) indicated using [tcpdump](https://www.tcpdump.org/) (instead of netsniff-ng). * Should Arkime delete PCAP files based on available storage? - - OpenSearch index state management and snapshot management only deals with disk space consumed by OpenSearch indices: it does not have anything to do with PCAP file storage. This option can be used to also allow Arkime to prune old PCAP files based on available disk space (see https://arkime.com/faq#pcap-deletion). + - Answering **Y** allows Arkime to prune (delete) old PCAP files based on available disk space (see https://arkime.com/faq#pcap-deletion). * Should Malcolm analyze live network traffic with Suricata? + - Answering **Y** will allow Malcolm itself to perform [live traffic analysis](live-analysis.md#LocalPCAP) using Suricata. If you are using Hedgehog Linux you probably want to answer **N** to this question. See the question above above about "captur[ing] live network traffic." * Should Malcolm analyze live network traffic with Zeek? + - Answering **Y** will allow Malcolm itself to perform [live traffic analysis](live-analysis.md#LocalPCAP) using Zeek. If you are using Hedgehog Linux you probably want to answer **N** to this question. See the question above above about "captur[ing] live network traffic." * Should Malcolm use "best guess" to identify potential OT/ICS traffic with Zeek? + - If you are using Malcolm in a control systems (OT/ICS) network, answer **Y** to enable ["Best Guess" Fingerprinting for ICS Protocols](ics-best-guess.md#ICSBestGuess). * Specify capture interface(s) (comma-separated) + - Specify the network interface(s) for [live traffic analysis](live-analysis.md#LocalPCAP) if it is enabled for netsniff-ng, tcpdump, Suricata or Zeek as described above. For multiple interfaces, separate the interface names with a comma (e.g., `enp0s25` or `enp10s0,enp11s0`). * Capture filter (tcpdump-like filter expression; leave blank to capture all traffic) - - `not port 5044 and not port * 8005 and not port 9200` + - If Malcolm is doing its own [live traffic analysis](live-analysis.md#LocalPCAP) as described above, you may optionally provide a capture filter. This filter will be used to limit what traffic the PCAP service ([netsniff-ng](http://netsniff-ng.org/) or [tcpdump](https://www.tcpdump.org/)) and the traffic analysis services ([Zeek](https://www.zeek.org/) and [Suricata](https://suricata.io/)) will see. Capture filters are specified using [Berkeley Packet Filter (BPF)](http://biot.com/capstats/bpf.html) syntax. For example, to indicate that Malcolm should ignore the ports it uses to communicate with Hedgehog Linux, you could specify `not port 5044 and not port 5045 and not port 8005 and not port 9200`. * Disable capture interface hardware offloading and adjust ring buffer sizes? + - If Malcolm is doing its own [live traffic analysis](live-analysis.md#LocalPCAP) and you answer **Y** to this question, Malcolm will [use `ethtool`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/nic-capture-setup.sh) to disable NIC hardware offloading features and adjust ring buffer sizes for capture interface(s); this should be enabled if the interface(s) are being used for capture **only**, otherwise answer **N**. If you're unsure, you should probably answer **N**. * Enable dark mode for OpenSearch Dashboards? + - Answer **Y** if you prefer dark dashboards, **N** if you prefer light ones. ### Setting up Authentication for Malcolm From 82a984532dc440bf82f04359e7f1a3a71946973c Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 11 Apr 2023 12:30:02 -0600 Subject: [PATCH 119/239] documentation wip --- .../screenshots/iso_install_auth_setup.png | Bin 0 -> 93194 bytes docs/malcolm-hedgehog-e2e-iso-install.md | 28 ++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 docs/images/screenshots/iso_install_auth_setup.png diff --git a/docs/images/screenshots/iso_install_auth_setup.png b/docs/images/screenshots/iso_install_auth_setup.png new file mode 100644 index 0000000000000000000000000000000000000000..8fea885551e7e49a619e23047655e89942385a38 GIT binary patch literal 93194 zcmXt819W6fw@oIR*w)0(#I|kQww+9DXJXs7ChXX@Z9A{Of33H=>vpfI>U+EUR-LoY z-aA4;P8=Qv2L=QL1YS}?Lej|HZG=M1dpai4$QcfH|Jz5pDbKWx_SI|#OA zk6)*!kL#xU`2Axc%&P`H`fz)LVE^{2xy(v_3#4X`RJMK8kS%7tbXseDp;PRbob8zsa z^>>#Szi%7Bin8Yw8!fO*-`$4@Zai2@hFlRA@2R$>&3oJJnqBvBYTU8J9j=7Z@0E2M z|AW1^7A(7P!BuXG^x+EGpf+n5PE^jHlV;1y`-MgKdrSWwXQ`DCf(@z9Wm6~Jpmm>d zP~wWNwLivrr^}?!v;43j2W*GO!%1Rz z#>xs~_#|_&%`-gzYMN*IzM^9s_TEvOEiS5U+a*bHUp3v+cV4x;6D`Ye+!iItb3QiB z>lZ#np8?9+Q(DU$+mcg>vUW(R%tR){#RsE^1%$kZCe$zs)eq2^ z*TD@&QCU;bvJTN0iLqMmPurGPx<#*29hs@V3fo?DiOtbEzKNa0^1a2btT%ch*3McB zgK>YU=KEL;fUd}`P&h#ObXRO{T&y(6>*)aC?=|y1br=J4)BJD*_AU`#7*`jK$0pw` zh7CKIB4WebkJGJlCS@XAT(Zj_8Q7~i7Ma(z%;Ea|GN$nE4>tFXj9HVfY$l4$m3S|e z|12f=fIoOSx8X0`rw!UjK?fAUZ3rL#-58#-G}3q&er+D@njx3q7$<06=H=v&3`7=` z$#uf_H1pw6f`0K?$@#lkIo%^YR{u0F0|s`sWj)o-kWp@P)pqp6`v+yGUddnu{lm(< z+0Q#`+Z(|wweAMQxg57-7r=!9^^m<*Md6O90I*!AKA6&KBqbJC30voWN1m6m^<>BO zd&oMIwkMa1Y;^k01~|6M8Ju23xHJF6^x4Y#=N~nAQ8+YvRy2;d94A{DpY2_x7pJ)y zHjz@QsN?B`slOhtzx;gw5XQJ>Wk+<2w zis-|$iqZ1*CFnKVQ_Olj1}9*9kVi5r$lrR}x=8Mjv%8eB(6| zT$p>RG;UFmn0Tqe$X_Yaa(=?peWw6w)c!UQDr#_x(muhsA$d%6-yZ^`O}C~Lr8b9e zO>8EGQxinLp;r(A%DJ%fYU`&jQxY%W24zsm zk3oc7_Rb)S*i<31^5oVFqA-gM>QbGF-Lvk?R`k{bg#?Tg#0Ec7Cy^VsW~57o>j(}8 zD`ePAUuMZCtYfn=3^+@zp|)+LsduEYICosHR3u-#wlV&2hMS99gn@4xUI>|8Lr8JV zp)qhWyLS?&YmS`f?2Tw1CV(#18zqYOiSe}RZ4Y|XZ5a@-GQnsOmE(ePwbbBnco~6E z3KOK4g>H&T9t>!a-mMV~k|ZhJd{Qn_YIPy$$^5;Hk)SwBj85l-Pw9s?!Eu<1=^BGd zT~9Bl&IVsWsG|!lU)jY%8OZ(ZH=y3gQEAKX0}3fj4UNLnTo%nMdaipQ|AzwPyb^>nwRfU~I zsOiEwLBBdfUc4w@<)9?H&3|T`Bh6kBanrP8fEl#gSjD5h&<$D(Yd;qW(jiUW#3)xk z?+ti%7whor4NqH=JkaLB0sa`znw{KRy42DCplkAIm9=w_lt7(8LIQ;&;YsK`i^+4C z6+2ydsd|fT!JEu~qtJugpm(Bsduf1l+IL6&L$g^78TQZ&4y77ahvoq5gS(Ln-p3gbltbhMU(&;Mh_}hI<%$W+S7@tIuN3Ex1t#?$iiWq~;HT94XWj z%EsD6z+i}^)R8<`2fTYHCuGPjw?WfSQQ;}vIVel*q~RZ+W-I72f67e{82ik(ICr*0 zEB^gNpoG6mq;elT_5h6)F{X9lJR#{O7?WFuHQRSiyUg%FA>TZi!S%G2R|LVvdB1nH zq5^cs_VBIZPL$#bkml=pV2Y+HB8P0vrqxeR>mWR*^z72S%3H5LC@Ucb7eUQRY{y`Z zcUrgv4r#Eb(_C?!upB*KfwVY{1gisDt@0Bho=^5y)HAGW^etIGAq68Oxjk_iQ0vflQ!YzScGr$Xr!(PU$d0VECi12L!H@$~X_APF zL0nacGX6^mJJtbUK2o%ob8h+IF(0IV^~hWLYp`N$8KZJzZ)b5`m}@j4kNs z3RWeN7%a{Ykp5G~LXHT0yCGabSgzn;od8+XCgntM&KN`IT#4#MvCjDhRaeo<8Ufl0 zI=9<(O(vK>v5ib9gSXA~|Pr)yn8i z--If#VPH93DKGhKt&7ypU5%=P^_PhS0!FR#i$jx-#BLW1z&_(da5m>l^LENosBis3 z7fA6zsg~zNmgx`5sZ6k9T;Q9;`0U&w?wcc9n@~_d$;6{5?72QG|5YV8tf!9tv&ati zli`JjEKu+3Yp;YbXcuq2IU{+wn)P1TEsF?eC=&DP6D-m#sx6eHA#=!%5ZG;O{@k+vp1IS8XY9$Hb#NfrFZ z*1${|(*?rkn%OQ$HJ>!#FBZuoGLmPT?|O8kYXLwZ1KfEB4iG24iEM4}y&c9(-7G)$ z)I@156Jsp>r_dZjQc|i(fy&0mYqu^o@Q#<0`O_6QjA!iA&l;wwVNkblvAUf}#wQ7VlFN%aVV^&*WTieJczT&o#*sGR_-qj+;f2wh!tayFWtWEJ~ zw#yJCAsPpD+&!Caxm!}bh|`6@WC0woL}R#P!fj9dK1P}VXvR5v5%ZQh=VRC%HBkpl zNbVSKlFk^SG5pHpYKn&OnJZ2&Awc*yY}^9MB_-^~-Hz!~Oc8)a-Gx%+EGJ%6eZ8XIE&+KrU4xqyDtu_!M=c|R|l^|y70yfX|?ijwrPiaQyyG0v*z1P z5uf+dD(*ifH=LEkr}H9tVSzpWj32(%o@L-zlJ?dKYqF|dsoH0r)@uTcA%Kn9PkdR7 z;~LaSuvJM$mFeZaAU_eo!zE_;e6HZK#zyyQomjEApDsj5@L29NwkH(u-Sk8p&b=+Bb`&sa-6aAjrHU5 z_*aa=&{RSJgPoHq_N2bt*|HPdlVVvxpo?}i!`KU0=))l&>CUHoSbR4PPkPF1PD5~c zXxrf;^_VAackvfAC0P^k)RAjC&(6%bn_ax&U`Y(|?|46|9(|xaj{Zp>(+XJEnxgvtc?W8;2djpssZiwH9zt?4= z+$;@6?kPT=azNfCb`^JNlr7+KK%~0|h;gFXi`jY^HI-X3<{M<@u*hRJTk&_a=*5ef zkmQAo5~DU*IV2C-5g!sIsHaSjtG2jx1?Z768-*ynB+e2Up_*{2h9-zR!h4y?0J7ux zufW%fu$$yuS@xrX!=wa$N?)Yw2PoF+bS`~i)hbk_2%|;iIF#Y!cSLoNOhaII4n**yx_6#0WBJ2*zHSBa+(o2UHXB042v#)JpcQuzU4fA-4_X9I8r zOSLO8rF7%YJF3_`!@LlW@z|Y{|7gmpf4%#A=pU*f5oHx4DT89wrxhVx7pTTMHJI|* zb7DTA0wP0CwaNqUtqkNMO?H==mM0RnQ1gUU!9N0>gDgIcmjJGal(T!}Yo;g=#H`2x zQ!sc8+`?oPUk#Fn`(6f-lQr>36nHIz(3 zdAG)-*SLn;N(YEe(Vlm=m_~g55t0g%y7Ar)Wpvh+H4o^NzIEq<&!5TZpVPR~Rb+Vl`o)sG9q}x2jlfkJm2S?mU(`%~48CgSNBrSeTO4`Z>>l)ZcE)ECe zyTEeh6>G7^xdp`|(2J*$GBrXhz&Qxvst7^YA|aVyh%6F{6svmMNP z@nc`lnl*WT_qKwf{iU@gm$0)L#0C*LtJ7z_Vl0B?vBsLHp~gK4f+j8SG@~TC@b$MO z#ELH1W`4OdQ|sqec0E1(^**M*``_2bc|R_iJ_CXfLlx|`u%G`)S!+t7D z`EM`2*TUS{gFK~pDG&%c^p;mej4li;9Wb%*;i&leip?056ubp6I~pR1k;-z$jpMh< z>+))Zc<$wW+B3Q8c9~4vyoWq!K}8$rxL7Fv9wcb}OisH@nVEed%9LU|n^5zF%yP%1 zPIf3(-ZFtsq=&AO8hm$T*TYjGi@Ne^-dh&JvgRBZ7OW=E8YlR2D_*KU(Igk~$!m;E z+&kpw3C1c157z{E0yL0(XAtROcjJ?n43dJELwRL?Dlg*HP|6ExmVBDp(w^`GYakZm zk+?m19@b`*jc*L+I_Q4}s0fg62Ivs1CH$muh>ycXI8%snJgirN(kgE;;~GBQ#4eyb zzeVy(0x>=1`2DMwJL2}<-iAd#=EK5x@d)kh5Y9W#fwI38`kJ;4c7LsrbE$Un_@K;O zwku09m&P6T_1P-uX_0RND{c}r?e-qrVM}+go3H6wcZ76eKDre7plQP6ezz2w=i+-R zn`Na@&^y2`aFM8j}nbs=#aWTGKS~wqgsK9Qm zf5`p{t>49=>rAaDBTO!a#mV6aO8Afezg7`qE&5%F?8zEc_&l_R<0dtB=B|G9QS-Io zdP)|N1tjhDfqbr}Z@K#W5spHN`Z&Kn?h!UADu8LTE4?*;VaugV5B13}e{T2=5MQ4M z7qr5EI~zzJW;Np(L`av!bUbZVzcKHU94$T^bGQh*WvxRBdDUJAgF+5wXi|r)nj4Y; zA}rSI`D!N3s7+BCaAJu!qMCUI77w11Lxx!`uNLYXe;P=vMz+j`^$~8Ix?k5p^=jDt zx&Mtn9@#^Av~PXvgCdPODnL;FL3L8`+{*6BkrFcqp8FeI6p4{i)QpeT%7#4SavT~e zn0HRXoHY=QF+_I1z=g873jdL0ck;PZLrRySxsvWS9Xr26$9ul})-}wrnqu~=dp@fG zw&CPRwPwVXZrsS5a=8rP)Y=*MK`E!-9O>R9|qv@v#rZHUYoK>}SMNWZ$`3SJ$|Y z1ApEJtJzHPIqqdUh{94SwwIYYJAI%dK@Vp z)wmjxHKnnF!;?T2=?Lyg)n67GMZ8uH+;*k5od$}Fc1Cva^qzYh2{*TPO^_BK#=jsz z7MBD}W_P6}-L>NK(<20?X0j>j&1gr@M(x3EnQm|cM7apU7t|zYq#a~=+u(CmdHADYPN>RI-o@ZVN@wq zQ5 zeM{8NbGZ|lR=ErazRK}}Otj4HSwg^7Rp(L4`wR*+kj%1JKUo*x+~v&J$`M&9(Q0LG zVUrOQOH)hvDlB9!4diLLk^eb)Be%eAHZ3wG5+yOMAftZiz5h-99j7OoNc!VzjU-F^ z+r8gcd>QuBkzAK=8$zslY9mDKj(v0ds+?7d6Zj^&OlFks7shKBC5YL*HG&NyL-PJ{rSq9d@NfLMnfv!bGa5~hOskE`Ry ztSf#TK-kfd5hE((MbCO4#li0jb*=3ujM9|cj6{!kXREWGxt&M)KpT~T_@ zdK?wSXwcmM2TniJmN{RWrBI$XILrFe@!R+s#KUgBVd? z(tO(T{&+pGqAW+wL5kJ58a-+uqKM{`f!Mk(tZGU&o?316T*E8gl)JNIFdU69Y9azo zsi33Nf2L|(UQxldXDo5Hm|1Dx1);6sA&^Vcn96jV?VptHmnV8&tJ{fDIJ$Acr5BgE zu&}^Z{d3JGpY&0Rn06v`)kcUFOFHr^E# zPKt5hN>3Mu$(E_8IV<>4#a<7mV--?Tq4)+f{qifb6zR3Gc{hZ-9sKC@B-kmN3bNFK z$S@u}MfS8pzv;*Z^~TnktvPptQ6(fLch{_I4->=*IN5$6@^&~MX`0`J!F~r!Vhm45 zrN*bPvVn#ZNN!@YlBXmAqRH6U5Tb?xS55j%nDQ&A`6k8n^ynii_S`x->-f7>-+_zy z=)$?wX>=%crc&MB1^?+%nuLoh(!S^ID;x}IzP2}FH%OkCiHA0CI%Vb3VPLZBW?`RB zYug6iY%hr>L-!a^3NU2BS+Dwsi;O%~mL$$(*>AG9kNUF#(wKqrg#~}V@9PtU)3g2K zx-m|((F(t~_(|=OhG$|hMA<7I$()RQO~^#8(mw25;Nx; z>gjr0UQ`q!B_)L;TW-USy`ti7jK+V+fZ;7DwxdQ_PjAXNj$ca9&oJtm-}mj2n1iEC z)v~Ip%IG==4DWYAVWA^e_E*VunD^#lY-^*f{C|5soAyP4;eZxFrUzSBan-qx5S!ze zk=7Jq(Vm*xd%8R7j)3Av-F{>#$L{_yV@fKXGn75sNt>^$fA;5mjGEDas}Tv@hPyJu z@9)mL@E?fJA}c2eE7|q_-%wJ5pdGW}>$eD6%xp{SNMkz#izIk}@5VopZTBt!1 zIz5;bQI#blS$7-+EX!jHWgy}*IRJJbkO+7Z-(R1S($f2z?%TGDjHYqk*pqm>{?#Wq zn`{$8&06)jpG=ft^i<6a@nsP6u{<{nyKr`GS7L#If$h5;xS(saAdDs}22<%y^!4@i zSTSZ3X3S#y`D5uNmJb)JQH`Zp(x#%Kl|hIUxlBg}J4CNL1_tq28>!Aye5rc{Mt)zG zrwOWD10+8XctAu#!;E3$Ebg!WR#ezx`22)zW%Qg=Q4)_XC@9FPsGxRoa$0TmCyaoO zASES7K}8)O8N3P(3#m6=uTg^_94--aep}Hv^8Y*a6@3;twaH$>u)E;lMGEOWHF(v< z`#UE0?{V`wFO`V`GLi6I*&3?kYX`U%Y7`s-!mw5I-u>%L)7eRRp8`j>C)7%?4s^&o$_(j;}g>_t|B z95IsG?kB6pbDr-HSf0lPWm?^iWJJC+uz$c%xpHy-Bqy&8VV60l2AJ1dNBu7mP(bsY zbi{UVOFYTJtareAm4RBbZ znDcQ44Ke4KSSccJW1vHI)kac-4Q*fiTkQVrNQF#^u1EdMEm0*UE-VTmLyl^_RMmI> zn3$fvTH&n1eFKt5L(S*RA*CRZ{SPbvW21WV2O@b=dP9R!V36=S3rIdS48i0;@AemF zAt2glKUDT>C;t15Ii2G>5SCG!wt6kBfKo(E1PLEs*3gh3Ki@DwPBT5*Zg#eM%kI;6 z;i>;@^W(6x_$6zxuW`5DaD$l^8*QXn%n9()C+pXu4DplB4DS$~_QQgYM!jkf$E4WX z2(d&aUB>Fnf`mgI4()LkB+h+)wJvM*rRSJVGk%zi191#4IeyGcyX`?W%|^ zYrqhX_SFzv9mk6h*}w98lL z%<8KLh$b)g{|;#BwSKl!tJQN}d34b=pDh^rT0YM4Mwp#d^ttKN`DO|$%zEDT&)wK4 z0aFX}?k(2J$az{)1N@gU!s!Wo9K3n3TEC<;w#ky=dFOk(m`W(Tr`wa_C-Bi$_85D( z!LH$%LF-Iy3YYi3nNUA2if+yKp>P=v%X zT)$%|1smHv-K{8T?y4`gS<@UtXD_mj3)jQ9!}05JRZ_f5w6VR2KQ6nSU?R1*ph)Pc z6H%WoOeduMhghvKdba(;^tx9#tubDcrUm9*P%yrQ#v&;8w3}gCYnZ>}{R>RiSe8g1 zi$;(&dLNyfNw@MC0-4?R9n%=q+QaWpG(xikZkK2asF_D5CI%0k9LK0ik?`UBk68yPJ~ZM<{*xwvnL-Gho>k*nLJUzMfB8;d7{jLlFjzn(JK0ry!@3aqT(^MEjs} zrvcwz&-~x#fCpZ*TqwTsIoBfs#rFtMxHLYGkCT#F14!FhGTit`}>LgzFy6{^lHDob22idrgJ1Y z@DP@&G^PFg`2GXiCwqNRQLE7q;ol#uUD%c?Io@EDi@o6~4}`Lk(|u`#T$q7Ej8!j@ zdEr0bzHZ1XT5Vx=(a}?)v6IH;^woA;2;Lqw6)XMIxgVSAY2Ut%A#igxc*JhJ zhqSXbLB|c$m>?=HSt1ej0r7SD<;X-={<8W|rHUC9PId+2q@72J^DP`>CV? zTo|>W>QMEXn(?2o>E4$ty3AGuJ;l)wOq~@o(4VycX-P>cq80@y=}R~q4kWachX=Tx zue;2k5MyRh(V-Kqb10C}sVTGf*vr9T5oIkcAz)Eq`HtZ5)qXtRXkN=k2-d&k{}VkU zUk~YEC&h@N$*Z%XVon49F)Y0_|9tVa@U^Sz+t!!_h;j3UAzMB4fu%?5`nWv*_aMZv zen6kWFIw6x)A7GduT}G>tDXD==Ged-`jP15qQ$N^wrWmxGrEio%!@-+JCA2CO=n{- zD+!8lgm|MPM2a?>il4k0-D$akezf#HBbGBx^ zq5xmUyUrb|q+m|)$}1v))s6v!0aN;n58qzzMJLTUB*0 zqo@i|*r#xuuH?*P=e`b^a@BdgW!4G5;Md#Lm8EUpoj#gG!DKAv8t{EunhOMsX(=@7 zKOAO7F&!sa=i6WQ{+WScvsfTCn)mh{O=gZxPVSdv+8vzN_UMa0A$?uHTW<#=Bi4}`)i z3H=97I-G9u#XREE^O_DXbQA)AdlPC1&+K~(OAy)(h)_u(1*GNZ(oa50q5{by|h9K;8mxj zrlmC&Pb6Hoep#ha|D&`K-kf^YwJB1njMqDq=H?PGJnXQBy^jb%(7^oi&?v9@Hty8W z=tN0LIe2{UbSsLPl$JIKzq}bfeQbJLkUKgK`?1UfthKlKd`~g(Lq<)8O_+??GO_rt zRDAS)>*ZqRE{i)jFwm&cTdOXCLqH(o<4*=c5HdRMrHhD)HMoC+PH`Xecs*Tg(i;nO z@$qDMw{nfRseO-qWYA#vE<&pOA}zPCY<-{V{66R^?*@X2X1Q*;mn1uK-uAB5=`SX0 zr+@E_scWweU;fzMcN#6%D`K?-k`^^e(YU$d>lsnAOQ_m&Dl#QK$@3lcG;a?uWW1Gu z>bV4RIxo8r(v6bllF;)fXR6>J;4B`g?)_1$-%Sjhg6m|}TkzbhuM;j<( z-JO~a4o8Ve1;ne%BoP-FZlD#LcYQX^ce4Q4_V=33wxhRm$*b(>@yq;*ipa4sn5-J= zii!#bs4zS@KD^C@XaA2~^ThY3Va1~xeq{wTAa!A__O13Q^XjTwdg6ER=;e8mFRoFy zBRs|HPG@Ln2#3#=Y&lPVf9wvd+c-Hn|LFQf_D5sO-&}Qm?{+_L@q5(995zn#9cJ41 zm@e1of&e!IgYDYkmm21LsXV}}(Q#3A{?@BI8karR|6`wZ%kx+_$MZ-Nguw5Gh`#rO z$^Ryw5g9o_$J6wYZEt`7^(eFUWWE&9`}yi{lp?nZ^``yn?Y!;tX}#_6uXb@T`G1TH zVjmPpV<*h{*za;``;`xUDw_U_8UbePdM!HJj9;RPtF7#x2A;Z>H{WZEy{f%-rhs`;IX7dz| zW@cF!aL|Ri7kgJqii&P7TepEE5;{7%2Tu-Lrqw()Yg$2{`_i~|%$2kqLKX4K?{sPU z9<-!U88tO7Lf@|VlyUWhB<0OGer}pDeLuZWz`p4kS!!b13Xm4!jH{};0G4DMc8Gkr zaq@hh!4aqwbvCasI*)-&W{n}0wH-LqeP;cCPPRxA=H>ZQq`$6};2=q^yWZJw{l5u~ z#p3K3AcF@K#bjFTY?&Yj4>VxcjnC~%<#7koT$q><@+kAls4EJT?hD(E7+PN8shOw) z%acl#tbU8mvqs)AoovyB45ibDK4XjOh*^xdo!3AfO|i2zC2{YO=01VUpzm@=eSZjU|Eo08QWkxb`NlTJ~)2#oVxeuzA;Xj z1v*o8LpqKYWxjaK-Y47CL;A?DRuihI4w7}H$U=ALF-(pG7NsEy~=VT-21EuriePIH_K4s7> z7wg_25DhsZ2y+&>77Z zh=STO6A}GMPDb1E{{|b5!BtP9J2*OG^0<-w+8HPi<$nW1WcB;moQXRR4X=YM#jdc>#d0YVJeQ; zKtzz5ijqzitWJvg^^?9Cv6N-uLy_XX?Z^rS!?|)eY&Bhm8eI_?xhw3IC&ig4;R^i= z#d4*tuBHdYjRams?SVRM>YuY&FHY482V%b!h>Bn$Z9p#p=eGrxd^`l~kU_|`)AJ(6 z@c6Rx3TWtYfSBdup?&DCL~FQarkx3JYV;JQ&e$Jmbb0D3C|atzRPOCM|Dm9^O*V zbUFfuEI{Lhct{A@F!iryFkPrr@k0Ar1-T1Cv6OiLV(&qNxxeV=n~lNtbWVuN^?WwE zb!x*W6x7wF?`$@_HeZ_jMEzFLCX;0S?{vaIZjnV;cT&NI4y4?Zu|g#(=B$Jp(U1Rn5j4K*O{>%-nX}o zAkl>UJyUJu+-?^|Z7&MXZF6&Tkh#^HthGlQDXD}wZFBp#27DZpF3MK5`rpj*mu_L6=GF2aI=80AGSiq6;Q-<%RTmz9qF~*z9tqjYTO@|)!3WnX+ zh^_FtIS+S(5}m+lZV{u^3uI(+Dt|#Quz+%BKxuD3C3x;1%GQ!-A@^p#68EI6?&IZOm`X!_J7d~SCnTvS<8)1f}8I;piS z-)gHq#clVf)H=s;HUYpnugWTO=46)Z8HZ9ByxpRkzupJzcxugqKD+ytqjk4~!8${_ z3=k+goYS=`SZ&EJ7_du6ZvC2XNzz@_cNkjmznNaOU&#L3k}|sDo)^eSVvNPek+{v4 zdN-=R$lS-{0f3Ii!jm;g3lWvRpLskRYb;P{rG%keL01ed)@C`iZn!Oav}OU)Ny8YZ zC`*}fy>IbVtT;Kg-CC|X&7HbP_%WXs&KloCm~hbWEV9wYXpVU97vKn7S*}yHFX|b|rS*!8FDH z@|&%skt)q+oY5S58C9mtWrZaxhbqP(S=C$xB~-R>f=ki+3G<^+FuzR$pp+r+aGL)I ztFf^$o!y?mJj)SM%=kQGq*BfLu_=S~k#`e{%ywCQ&F;7Hz}q%1?RGa!45m3R;7Zr> z%XDan%r%1qYVXqhOE?7p*a7uDo#l-Pzj25rClM=zE}R`1K`xwOfd|STrIx|qVLNW^ zuk!=)s;xd2`FD3tPZz5ZLq>wC@W9ZE7t7S&-w#B4B;@ir0=Wk!EV9POHJh|wr{*Y~ z6!F>VG2@4Ju6$oTuH2|o9O$B9WaH{gHwMmLae#qPI&E!ypR!fRf2_;;l7^NLC4lEd$#4YuZ&IoA?2I-_;@!mD*I$OA%=2*U(g zn7$g#J@JYI9rf0=VAX2=k%7dR}%C3v{#%A8A?!}ctJZ-u}}~Xdi92g zrz9HT)lb@Ab^FhgMRXbx-xN86t=}MTi2OHUty{!?`(BB&f?<9bG^H{*fzOq-v#GRw z4ojLQ-EY*`R8*jZNws4^RxC(X%t}gCg%*5O-e+oiK(IbpXE*@dq6hESz4p2eI~VF_ z6BeK<(@u(qHp{TC_a$^TTTR82Ev25_8fZXbrHz-KntI%k11~r%Oj$!kLdOR0)$?_b zf)pqf%JlYpI<{|Wpmh)F(t`l8#Ow3=wd%F<{7=|MMw=6b5`9lrvBC(%qpoT9?cYL* zXmUKeB02t7qC9q(xqeU3Q9b$_XKF}5^%k#6e6dC@U54C?jI3FbV9N7aIPXpU3Gt|B zk~1kO6VA%BTd(Krp4$K2dR!|SD@N??>2&;9`+9iGo2m~Fr?)_WYOfSbb7SUx8m)HX z_<$(C42-a-p~00oxJpZX@H<<3HE*x|V%dj0I*w#S%eOn1i#)W`j^j@tF~u`q4`1E2 zxf;8ZyBU*<6Q0@Gv-r4JgH!bmH^TBf8`YpgEs@j>bAx0q|d1-DT_OOJ}x_Mx-KhgYD$`# zk};c2m>f-DU#!$;M@P#fCV4X(_6G+5At;d8z+yJ%8$#swkq6R!2?@w(7#L2++3vG~ z^-z&fQLoRv+eS|p{)ySy!V2*z^8BBK%X;21$y5KasM`5)FAm}EkFMWCylRZd@>TQC00}< zr=%d`;!gMYdY>-W60Wz}9~TJ*t<*bHfI-5EsG{r0sqZXn88V87gh@S_=eXjuZaHxS zHBL>4(*_&QXp4C$kIuut-- zl&mdL{f5c$Gx%L$C7@af^`wmORZG3t>jQOH*bp1rXCN@EddCzQ5!sB4gCXLniJpQ^ zU(po;P7zl?d1ZoEnk^@2N``yXEp8^ZQyscN5Ca6dL7g?%)8qD6gO}xBrhd;oPJG{d zQPR`$MmG_LgMbhP&C}i5##Y5cxT+u3m|=W23PLSDIOqmug_%3b3EvvMH?moOpg z=vs_9mW}9kg|p#{RznsniGTl!#T5}mdtK}L`CLKBv=c@9?M)}Es{nEOu3P7M)7wwd zmpb?7t$>>JLe8V0MXT!h%Q7x!ARED>_xpT#(YFK~LQ^}X#yr=zX7-SM)5SSTzgD|7 zhtYU=$P+c3vRt6>`bn z5e7JG6+z28cX0ONL2i)>6w*nXBtTmXTQ1$J_*t?+Fx6}9^upKV zD*KMfNqutYmf!nKD5mEJQF9`}bkBVLfrgKb8+7TvWo6Yu7-7B-fap0N`@n_qH&*Nk2 z2GgUi->6UACa!<+L^mQ$0x1@hqugDL&(n~3a&q#`s`+4?XyM%c7yt^*rlg}hQ`g}e{ z+N7h-n+8VAW+!{DUXi0ls=+DGA?WC_DAFlwF+rl~9Yf#npxTb>q8XjVv;A4u?AA$) zshW zsv?K?S$jbLX=!!@VBacfNBENQR|OcNn16`wGOIsGLEf+4zMr>~jw{ks_3IIEh={Zn zs#c{{O%LI5=V90W2f+5AzC+;qASfj*^>TdCs^uz+iJ4O8^;ip}M4Z;jF_}5`oW{oS zFZdlmCQEOJ#t4Cm9|eDH-Q?TpwUqPChqX4Rvksnr*CTjS(7LK>NG*nGy@w|s{c?h8 z*5e+7&yZo0`lGfY8jx@h6%A?Ywo?K`|1)9Qd-AwR{>$POclXxSrc(pbse8inwn9=Z zd#LFBulsRneGmZ1x1<|)RAr6N%%mU#`)@mkKT%f7Wpk|lSwL08 zJ`&oesmMEN#7y65gF{3_)b)Pl&*dt1q9Wf4l}DY|bo=vbS5T1jQJS2OD5tLO%#wS2 z4DaCRAhF(Dzp_J%MKd5a5+n$^IxlbPj?edv!R183>*1vDVztq+a}#vhZFX^R(2azY z<)sdtOH`54Jvk{?-9zL(>Iy@&jIL=F;|FU>zmvFUHa+g->1-pW`nChJA#ws;?~yBh zSa&k#b&sgo+aI=3`Nq|WLv`If|4XNK&718w?QY(vMWO#pRf~Ptvdgoq_+f=zs+$HK zeA%0nYHGfe}41doV8rS!#VrxXYV_&`?~k@ zwDR&9^Zw|*D2X)S7cdJ0Kzh+&sMThA)Zl!6h0{Z?Gd04F3fN2&7O^OpV4zoce~EjJ zZ~zPe^dLGqI#39bbkbBH=SBWY?0Q06%QdLPo~$&1gwH5vyu}j(XbT14<&G^FJxzei zQZVz8ni0%?MG7AZaA^uQ-7nqpi;BcmRsH<^SqUO(!~6;goHl%HcMiLC0TU10As8W< zT~2I_y-}B0RaF(-fuiDPq0f3_k>wAMRaK4&+|AbwI<8H+L}&=h%gY)JIDp|`Q$wgT zb^mCAOtnP@_3S_6cv$2VaN?nWpmx={;T1D84J~cnPy|nX$q(AF$HyBe_-C9U9jxsc ztzz%qy?_836sjLQ2d<_=X%Z4=Zy@bIv%yGFk)wA7mbcHCf}cNmbW~YXOiW32f;2r( zr^Zs6SYgLElao>y;PIgDd*#v03*%VDzVxwH-@nr1@9luK9~xdgzS0u;78i#_Krpky zgnPaix8$EUh^Ws_pri_5zp(-zF^6K3walk#6!_i{qu?BK5CNmW!;nNH#Pq$(?SG?E zOqR7klV}862KH_K0N%FRQ5(U0n(W_c>I6^ic3E+*+SlC~39;k2auYq13L)s8!F^*i zPh)ZO2og*WLA{FCIw93cuRB5jvB!+P`f6jzxnYETKQAS&Bw7CHn;lb-zJ2?W6+xxx z9SQCRzIc&v*J9+IqNpEwrpQr7K16&ll7)c*1#p833I^dx>g|O^r<{IPRBSIaxi=gD zSqF&~S};ud5N$;yAa??w9ey*f$!Wq72@IcU`I+tOw=d$et(ZkbL^O)kkdTlRUu5^0Rf3ybJ;uPoS{0I% zr1aF`Nck%<1Yp7+sO;8uqE8hY;KWrJiL%!H&BA6hey49hgIRz`J$;uqG3R>GlCE7< zh+uMp!a(xW)go~&FffpVmqZe^W>(JC)iuSzk=?24zo**yrSKWsc%@g~{C;Nkn5~Su zr_zt<-#NpZr_ok3f4UrM-!l>%op|1&vWZ0c8( z()iMqDcxQgHdc4RIXiFd z$1L2fr(tbwFZLsuNLloSb7eNmzoz}OKR`#pN&^L0J$D0o4{XP1u@;vsH5jwgP6yX~ zk6^pEWIGcXmJ|(F!z`?Sy?y!Pk|Xa3d*s1DQnW}kSUd9ge` z)$?|gK*#H#ZW9nKIG<>Ke+;&Zq{Y%_Ci{{6Qsr`J*C0E`R@AWwnh8v_`gL zP>V;)ZEPg+2_|v-{Cb$*-)6pENkgOe>&q4v_lu+_)mon@(X4)v$rCA8^QqRHYn=dh z5T4XRkCxHA4^XrQr%e^svtr~asHoyw9`Bv#=;_hjmvvp{ow9OsD?qWIazWR0GBx(5 z+M{)Uu?5%1VTh-+W3Xid=f(dz)qu6C+x>G#q)R$f(A z3){mi9B7Cp69>n6Sqn5>SbM5~If#yw!E1Nw^UmR?{X;{eUE=*plKopzcM=DqPYj*M zRG)Vj49kx;k{+o5du*a`ip6$;e{}4%l*~?>DoC^YXrc`+_PF9X_fOQ|Ud`4ssH>Fl zCkywdC`<4fKR#@n*18-n5GOV_xnCMqX@-V}pVpvVUVE&quLD;1HYjc;#iayt)3pVE z*Zz!*76F%cAF)%_sT*vl;7}`DNxK z{-f4=P>&7pyuAR~knAHcFXbPXMF&ifx3{;enm19AU!PCyPFK)?Q-ECgZ8)v|APO5B z!`|QKLdwj?sDYUjaC4+#E$0JX&T65tZo(WD4b9|iYXsD66*V>6OIu31ZE9{8Nja_Z zggimj*m+OGd4FaQ@FwsDJ@5d%@a)VHI8`Y*Vrdf->XwIk0##LfAul2&C8Z+0N39eO zGX`6WL&+(p?npj3z?L{!r_U=WfF@&Q^?kTk*_vb|_%R}`q(p8xRZjwYvM8JJCp6Ap&)pf-Vj1y>C+7xuT;NCL!ZGGm)03G0&u+tJw zx$NZUx|k7E{AAN;aAvGo^g=MHfP>_Y_082e;PG9oIZNvq%`<4u?2DF2j+nFs=P>>1 z;5i>ZzPs5iwXSX=xLkacENOOg3!l7S^5+>yx?Ge$J=)Dfurp0Iy3rWWB9Ee}q8|Vr z8Uzfp`v!~6o=%hQcgI3lq@=?_56AGQo6Ke>?z-LrI#aF})49*k&^AJ8Ov;S=o~OF) zypo6_Y8%b&*QhW-g@8ns6M!SAV2`vvt)Ho7JAoA+NWGUxnZyKjVgBPzlt(X%(Hg@p z8NeU`4)wLm0sm8)7Na020$@TyPAlWy&utIFJpfb{0RXwQ9PzVv=q8t~Fy1RQi=6BU zTpazAB|802Bn*uLvr zIV&7-ihsT7?=N&fVs?5jUGr?@l|MS;y)ti#Jhfb6=W4Xj^hRtypj>9q{xX2U*vEJM zQ}1N?qes#EI$gw{_sx;PuWuDlSIfbNka+%27P)AG;|DktT%zQ>lH|p8)LrL`mVG|~ z8f7wggZuD)w9JUBI=CfS`bbX^yI!?V`b25_B(98jpABx z^)D*20_p(Z;0FBHCMY!vZpfGOF5ZPFZ$ehz*VwQ=*ce6vZA5*e1Z~4OtGg3NlQfFC zK2UO|J;q)vg6xM)|J^C1zwzP55`~ES^QY5|%w1j>1T-odnqZ;(ZE_ypl|Lwg zu)Nk_IqFNeZl^)a0|G9ow{IZ+^>9cdy)`Ws-r6UMtCMItFcQLPY1Gn6~?(A48C8O5-kcTz_ zc+$Fg`ea;)(1N{FhJ_ye^t^RI6~Jmj&>|W^Nm=<9U|9i90u*4GMHqAh{RCS3c+($O zs=oma44Iz(h1^oS>qjs-)Ff^t==}-5f^HpQ(Hc zu(gNbhqJL^*#x#HCKnct2_+jFQo7vj719WHWu+CsgT+Nfp*gi^o!#I2Uv_kKKxpac zc3ynQ%K4RWUiF4Y#Zev(4sH(6N*XU_7V%m1TibRi!W#~ogBu&~oNooCg|&$6jxx## zb=}q1p6m%Wy;}9IpoJguIShuw#=yEi`UcS{`f#V5YGnTea!XJ9}UsFm*K{oI_ky@P{R z%48W!(&PDl4HprFhmEaZ=m8XJXVzFZ*4EN@>%Fal65G>Db|3T$`i!lV1dWZ2ui`*w zk`*T*sAh(QAPQbB2~bc{_6-l4f);}KL23wKZSt-qFm;v_IOdf*@AER%E_v8AovR(x z@7fp|8a_qm+Dsr`y}Di0gVR6X;Yg`xn8N%tHBp+DbMoWIY2W7QmxKfn)rkzKYaJ$B zeEfvGf}O($m&#K)KdhCqw&3b?a{|G%VIksl2{79hxhkhj+NhfZ6q zeAlwcDJqHpJgnI8HzAatDS%uu=m0_0?9$+sSRYob=B$Se7DE_@IF|}rcSl_ zPv)4?tc4h`r3UvmRRam9P>@BhTh!gD(w7_@RbP^%o#Mi0?ysYb-5D{r@X#|I64}jV zQp~*Xr$j?aR!845F9Vm;VN-KD=fGSB46Dg-YGfuY|HIX)4DqI*^W#W}6C`6hAJwk1ZV!8zvZ*Y>;X$Lya!e~KNVf;K_Mo2i5< z<6jaZm2LhChEpZ;z)sA+z{LfOfq@j4g~5Y)ckXQ}e6x3sqlfq7`6}o4x0hV5VpC2) zfJVb zu;IT1;0p!Beu^JWx>EZIILLI`b8hhV5duFl=E~sw#S;Er7r@WR|J3J{=yvG(j*pa= zLLn;F!I2?X<>`+)g>%#&s9sS%Bj-gozy1%NAR6NQ66oN+=z?fvp@~0wSZ43sGO_Gf zrAcnZHp6CKVb(tb`2Woe1GcmiQ3dk~=u`}ySpbi?pdjUe$SL-pY&!gfefCq1PA2$h(;~t9L%mS~KK4gZ5lY&y z%bdyhh*i+W=`}4qJtZR}s;cV!W=2KIkCN~ICe8Lq|4zsFawA_QcLzjZmS=;B`ObiP z2|m-Y?@W4GOa?4GTJR5@OF-;2sYoa*V*#;4BC40;8qZGH_6xAw;!|W#s}fob<$i#H;ws*j6Rj}zKLRgWbq$KNb>YfY^<*)nLi>g5#`F#uN4bKcm(@d*k88++p5 zlhSSfr&^IP-V>t#1uDeO|Ai_*KNON+ElQ*GQh9UeZC)*QVuDp%jZ5N z==Hih^1q~%C7z2qq8PnZ|0ZR&=~9$TS@-?#o_s)m+Oa5RwzR+5{NHOcv9P57*)RYF zQeFMmW`2O>{#0}v>yrOJZvun=@22*Dm9tf*D-3*m z>5m^j=FX@7=kp^Icrf?E%F54a()feC?r#h#0#8QYdbioska|f9w+Z$3 z^;u8Bc#=8i9GN?iqsK|{#32{LMds;mY{jYBR8f|?^ySE9`MW* z6Cb~=htzvVPfb^M>Mfu%3aUeoqW#6>!qWPTb1iVZG@$F@B-%s+rNZF6SCL8g>I z$*rvo0Mz&8b2lhZuLj!~a4?Srx|j7kf+E52eX&n%Qi$Z8c0{hVI|cMfI;ID6pCmct z7QG(<*UoAg^lfKXjvtij2|Qe-;qy8os^lay(#R8OwFP%1uzf&^)3#^jTJ*3KP6KTe z%YnG5MmHUg3w7pS(HprFbXhUo=E(u9w)E7Zuhw$Huc-xyz^AR`=RY!D1(kt5THkri@`fw zOslx=mG?loF8SU*L$3G|8!PAN$o>?;k=aDV{m~~zo;UQ#pG%T_dZ81HtLvHytnKG7U&v`>9GjNJK-vBJ`Wnhp zLg~dp%cJ1NuwH9>ASuif%ENcq*tOCnRBpQ{`1o_yrU9mA7PYktS5Fo=4S)qaael|g z-=%t9m_L1L@$rEG;xcE)2fo5r=<0WTS=mPr5t0BDo0XLnG_$#bM$s6MvFmzWf0(n0 z_MQjv!}?BP?&(oSG;A^#M_T}h>Zp$X(b&#%OxJj>!xpFSa%oyXlh5n8L)&{h7!1ptW*pvmfaz2ET`Vl1xb5OVcd9@GswbJ>~vXFJH1+{rOGFyO0Bo0rFs@q2xMy)hB!z zNIUe#xf^JHbK2pk(y~Lgn?oQ)LM}x3m@MQCb$fYu$ew@&B=u;yqxNjHA9w?pf}Tn< zWH1PD3l6K1TvvK;m;SYGBLtu#J&<@#A>e*-7JdBr+qX{8+}a9KU|DJDP~)##ilAQ* zRK)-gZ*(IsR0*8zMExE`l~G#U~WWdgFW8LL}T&q|Dn={@xSSluY%uuR84cw|lQ2 zT06iTP+IjKPM%9kOY>jORm|D-q}aE2mScNxuYW7&zuC%Y!_o6@a?|et`w+kAy(q4% zjNTnhN~(ASwG@b}y8=QzQ-C=ED}hNuA|WmP%=_VV)lqTJ7t}X425*^%1uhw`ui-R` zahmTh_?YU}9%rmbpF94$!$kSO1phQuAcz)v2xy6Yn$7??CgJVV-uR0LtK`V16yRcG zJ0-$`90X)fujngoH*@z<%)>%L$hf$0qa|--|5j=4gxp<77TzUPL){5Z&t%C>IWqqi zHP6^ngf)(H5`Uv@GWXd|Bpy{rNfP11942;x9Vs3hE|F8WlB6)~H169Ewi&;bX|2t- zo4KSbyu7nuMdOmQq-j4smV}2>e{vXdSh(b&;ZYb8bsRjD;;GBt$gWvxR0ce7G>fYG z^&AK^)$`E%!|=zH!S)1$x`&ole$Um>f z&@KefP4x6Sp~E5l_iAsUh6mX{S1C#jkA461=CJ>PHycAh?_qoDzr!Q=k(FWfQW`_Z zVr8bW{+Z=kt6}-yi<^%j$<+Z(B7ZMCqS7tahNKAVc3BEyfbtZ0eNivJ68`5i;<{Zc zF#lY|{Dd8j{_oW_Rfhln&Hw*u{J+aaU%iEXA@0TM)?d;3GR$9ghsWpJ`_^^s|0}6_ z*`N#^8NSm!@{gX?$i}-F6>bVXTEX!J^-3D6E0=YBctgbc_pkQT*?~dsro%?L7}ppI z4gaI6iT|%HKx83uo2496LV4dhL{$$P{oe+lt-n%}@z*c9--?UBcQ^{W!bX20fr_s4 zw{U_TLPagDsr8i|3!`5|luxtGELlo?C5RV6?Qz*cT6R_X_UH;L{P^al%g3va-={WA zIg^fO2yIutTq7UHnd-K4EtXhLcpau&5R!wjeWV% z=5R{lko%!`2xV}zbHe&_tZ3$*%EB(OzXoMI)pErzrolOz^41mYd-G4cr@jau%qdjs zQ^m8H8Q$^933AaNBEmkumN~+^pbICE^52D>oFHiw3ma#_`;l>1vL-MBM=dG!kp&tW zMxP~-EJlXx%O48?wL)x(7<}afl-H(1KHAjQTo+%zDobTO7ngif?KWsw4)O7<_t|H0 zr!Oi_eVripxqtol_BkzcX#h2U(ZtDiCGR)YcVyhOco`XOHs?;}@8QD2-`Z{N*XYlq zCxPZ!vY0;YuxWkvX}#B40~bB^`7iv$%{YcHx2!rztXT!HZs*ltNzu(x$zUgodUwt1X2 zV_)&dCZ(h_5?Vvz8K8b`GRKYWa&PjzX;89oGCeaB2}uYpibMbn5c;5Cl#j=;koWjJ zNsBf8_xUdVES|RddqW_yctQjYq|KxdfX@2mKAdDW4ZdY&5*_X&K`ZCrN))USl=}uZ?FQ>}oeGUkWd6Zi^PO@f9Tx)?JKHq8wxKUVuRhgkeP^;YNw1 zCQ`|f?VMUH={jDm!($HUzRC;IR1XOB^+RxA=D zkL(>tnurQ7o8|@sO2BA{ex|F(?}o7cdbIK@NP1a1;#4%rh(VH^khP@AB zRueardrbEiZ(x&>1`i8q`R?ox+-;BK&PHq|*-m9tSEG6BJs`0!FJDdJ>KQ%ycGz6{ zF6g)B49*_!Wd??XhXZH;6B^p8C9hPc1{0j*)Ls!zA}J&DT&Dlm@vpDze=0xm*(3{2 zXI~R>yTXEf5)<}8YI37qT56+2>q0PnM~D4n<{stP>Vh8aFpuTM6+#rm0>iJ7gTd`h3hu?_H1}#`BTH9S{Rwm!(}s={kC1BZ?-4!GPbGy2f zAzK<*SW%AApxsO`2I|tkIk#E74~X*o$00s>d596UHhE2Q78Xd#Dk>ln%9!H%p;e>d zc!(36JjY+UrlJ1tZPmN!U`Yznt|7%U^C~nmf;?BnN}%42^~S91i60UDd~kI= zh}D?8##HT7|N03HkJ=Sfg5RWC)%$+(rM6C{zNZuv?6h@jiYF^ymBqy^JV;7M!HIBf zBHSt;aXQAVg4d=dCo{@3Mlw=l^2Md4w*&7*!m%eNCYT24rHX@irBLg%qd%6cJkJTgo{`zq0!3PEb-DtA-wRY5d>bN#5&Iau0-0%G=#}MI~D-EmpYUFb# zR74~j&oloC!#4+gDz1)gfZX%vWJsSqqjrsGrJ?H+e`?9M)4vAS(W!yDS69!9qZbqs(l9WN`ujJL zX5$t*VQ(;CWz*O&cPDMdCr4JxG>ZX1!~MnKIiK?bgB$PkG!Jk^cw8ngzJ7ha|0ofK z7xmgSOi@K;*M0yTeZ%d#g=k^1lRm{YYdf~xzyAD5y@`3)PzxY0)jY1AfdB&X#|A60 z*`!w`YZ2NWe>Qq=1(Ts0A&Y2%=@H0YY4WO6qG1auQXqQRSIx(@VeZS}%{g#uR$ zN3-mhx;zf&T?{97I5e)VDDWb3)M{qVy6i(EB?saQ-qG>jZ%URMcJ;(R2GcA7{}9s` z0sO=KLo)(tBl=;>Bj$z4UhuS4TgYxurfr&WY3FKxGR@yO%QA_@^TyHA*_o3fmvLL! zw^(&?*Cl=B8QvF)98oK*Rs^HMjkzv|ryai}A<~Oddn$>i4 z{DEGcy_NGNLj)!B*Hp^Sly9kPG(&%6Qt-1W)QLu2U2&H(C(#%k$n>ux$+_N2=Y8(S zz$IL*(5RRB$MlR$yzOS%N(Axif|u#b#H1whFEZ(9D4l5!EVvd}6f9FQg9v@@GF)*-7#A6mhW+Z;}AEKk5 z_{-9XypJ5RWG#3>9Z-0F(L-GUyJ?}yRFhGX5^NyirPvG0PFS2*FvZs>=q$`m(!_|J zkZWzq{fAHK?b>_4Ap zwGnNWXwUZypT1ef5YKKJdqM743a9aEi5vE=Xuo~)iA)p;2{HVp+`UZFc-6h6z8 z4~OKSE(3Rkovt@qIRN2W48lUj`eH1&hp+PXVN-MqmHN)Wq+n`g;MKEkge3T-Bt18$ z0@TootLC<5E5r!%alo9xsP8?sb4Eyhaw3PsOK zV4Vfky5gC8RIJg-Zw(Y_q@IjMx=Z-q79!#f#N*TgJ8N{`uHp2qo~x2X+@0v7>?noK zmheqn;4)%b_tuTv|6rlxcy1DoedrYzsq_9r$XFgrV|0${EK3z>yWb1i!pR>Zo36j2 zv`q>T5(m}t?mi%*pNL7v;z-0~t0whgyoBg`N?@`@sdLLpQecwBBGML^wQp(>ot_yY zCHMrGas0A$%Clt?p~Hr>3bur=mTMG$mVuR`f@Z*>AHXFZ`SJN#nW#^nm2aAM*q@Ks z1v^PfQ^p_T^~};q+1tGGiYz7Mj4IrDm}AK(I?*k%iJaslf;)aA{~}OQoD6w*mQibN zT*JA@oD-S5f;krKr=Qe)?Tp*fV*w{?^0x33Jl(qWMo_$$^LHb|Rm>!#%~|JgL?2nY z^ekoM`Iotoj^n`#A-NU1=^80I%-Vgb3TGLA&c_v)-Xu!|I;hUbmkKS5cp<9N|4!$~ zXjGfpsI9_{j$c19eh3*vHJw#e|DFP5tSi`ZE{h!GooN-s=$@Pjj2aK;N>+RxXv{(^ zD3H&i0*Ec%mgr&hTHeyPb=g-e=4M4&UBP;>#NBsygNg}E9{By}A)R`+3!%!77--+n zHK|b>DuWWpoKMqdt+_y$s zS6$BcK5z)BGmRj4OClo{KLdKm;e?23+e%BqCBY~yp7YLTmqNHvZ>(ycE0o;9Ny@9P zrV_dtTzsP{8m4X6TGX!X?`cU*2mv}=$d%e@OHC!ZPUGKY*!AP^@Qu6@vEMtSyK62O zyZJX=@t!;&cKAR|P`Of?z~<@7zp*%nK-YqtRB|^8&wtrR{!Pu&Q}DoW#cndVY1Qxe zAWdR=k#vc<<3>GarUrA^+wHeOs#pxiRlk7$%m6Cu{T@S4XE!Bv4@7^!hxTrJ@?8M@ z&9qCa;2k-_;=wD7iQv?T9u=^&Iv<)aW`hk^@V~x@0_H&?T{#Lq1(Wa+x1s6)d}R5W zN9=daZQyXhj07c#n?tUr?$8Qmg?8!yDQg|MNduk9bZAIOi3I#UABSUrDd?~oN5?K*R|{%q--#l z+#<2YdHqn63sF5E3_(>l><&S0amz7{GXh$-Qw4t;cYKcj25kz8TV>J*{6vgXg)<=Y z25e=n3_+QVEoKf%_`ajiK*HC6*V(9l%1RMGy*xismx6*Q%yU4d&PSGd^DT-r;{3%v zsXp1yYp!!U#?6jz+=+VO=-$0;KfO)K1saG#M=bcc1+ITaG|;T%TeCl@yfYDx=^~F_ zno1h7QA*oED%FHt0bLO)4d;r=zZeLd1V z1Z7PY_>c^|c4a+G^NF6bObkqjrI7heeE1#=VdO3U^1EPi z&}`4km0cZEq8abHd-jJ4O43L-ERy(1Lc2uBt>xoacanF zm?l;Z^=X>MzS^ZhAeuxd_uR^N<*!3a)q5Pm#T+_Z+lb?I>^m2gB@GLu5flyNjzofw zfm0|M5RO@Zt>_BNH!snfrKad~rjj*}B)OIUCT7aAnlx{!9VR(o|A{RvWj*N6enG_t z5T%3K2?X-%dxxgeU=F9AUKKemx-7Hbb!r~9 zRmP+nFAf4m_i8%|*|vEU1t+a4iGLQK*t*AtMI-u%V|=Xs+2_SqUvy`oQCxlM73;+o zoXk6A-|HpTKZ5#zH2gq;gS}&$rXbh9+&&GL(z5$2YCt)-2R;T{Aak6tpRCJNA-bjP z_zMySEE%VE;MgXCxSaRKxd4l)rY$Y=%=T{HV$;5Tr^=|jP3_F^!Vs&LY&9hjQxnVK z^$%x{>EKjdgtWVJNT;MWt<;{NW*DvzpGQ{-uHi~3VzrLxhO9YE;#qa`FJ6^ZwPSG3_D__ta$D-D~fOqyDcrQMO2xZ$|^EHzfEaY`1sS?f~D#q`@%z> zgK&`BEvJ5B*N}M)7ptVRG3iDa!>80Gwas@+sfyv7^d`eZ!2b(t^njVV=3PSzPq-MHW+Lr!X06e=GIY)>7_xg#TnHr&*=VI=b zEFUz8fF5QRNg#xh>3>w_MoTT~oJN|(>j@~tAX$qpa1(&13KGGv%3Z8u$5;d#ke{_>fA*uEx*8LbS%vDAa{o*8G^!e+1dJ9fL7L$!Z(rj@}8m z(~-0~4<#nRgc6B56SRNBg(L~U}Fa38VNrY~dIDHkb z`_ipB4TKHzwECH25xG4I2!a)+k&rJHuVWy+CmDbOM^FlvH7QR}kF^Q;sN*EhVFsxmI_KilEdL`wTm{~@I(C5)V_ zI0r$dqH=aeTTCqQrDfN`&vJ6Qmd_=Fj21(YWUcBfe@*cr^OFn%Z$K7<6!NS>$1=;^ zm<(rOgcS1R(}2($>@GXR^RwghZ|DU7;uQcEzu4suUD9Wa;%=BRHzQYj*zU7v$6e%W z-AUa3?9_b$rGmJ8CzOpAiTXS{#R+zdY z#(JDgXD$pvt@34=#)saI$*-(Cvoa2J@-7~24iW0!h5axs7|G%?5cb<|?~iqIi@TK^ zs9z1ev&$*zBeQ7OvHBaEs5MspBpiD5&PvuM;nT{T!OQ2`s?j2Ec#p=}FWCs-?J-AK8GB5qdkwsMe~zs{cY=ZR(dS&V^V zDT|rky;RMmFimCZ1IbkA!JMiP)c87cOsyleNr zWAUuruloa@V7Tnk(%;>EZ>ltFCT#r!iqyJn-%*PJH|o`cy_O(>ql6=PhDV+k=AVT3 z8*2P-ADm!;Xi^ALKrZEX0vo6=I@42^xpiqDc(Jl4G>f$3VEp^SByD#5g&y4?&P3+7 z>Y_jT{NkpFB)rUFp8uGL2gjnjGfwIH4%P+Gq4lfGADrO?-sbc)FyIw7CHHaxe z!0p>2>UXO*yFFQ#o2c*YaWl@nH(cDVCnWfL`KQZ^=usrS3 z`XRJG%*hP6*;LFP>NOoxsoC=PMP>DGXvd8V-QxQ@g*6U%kS)%t z0z|SLkVfhy_IcET1bVEdNVoPnb~nX)<`Ld<4>Jp;H1UL`LYXP?@PpI)D^ zqdm3)eic~c!N4qSZrYT?))uwxO?k$tt9T=?J>XPBcg@D?U_W3dyRQvQBfP<1xDvk zFY4~!SfL&*cC=zOrvD37o6d^nAGXE_0e?at^?) zer8gEU)prcNs`cl;r(gjrN`-heg97sKz_n0R4Z*OEWHPGdE5Fu&z(?LK%G49PC7K) zuO}r=eKeaOcq*9FG8S%qy&s$C_6rHo@Hi@9ZnGEY2URI?+48A9PO0*|BEe)hfDOOA%`J=sVo`pz z#F71By?5YOcYxiLiv{m$w{EoctV@R(OT7?g1>9tNMH!J06nmv-v`Y+Jo(*)w#$@UH z;_4sxMQZ61$Qnh}ExV4@EzH)qMU~j3ZWfvf{q`f3C?@F}U(K}#LH}X|K9_tOam}J% zrGXeYRTkBCxi0rh@?8Hyw?2EDK6@2P()7ch83~$+$;o9|5rYQFZVg5?yvXXt5m0xh z<)1atN>K}b4HpQ?Fb?k1ML6{T6euxxi%aI6H-};~D~lQ+=Ky^&FX;}BfVBQ*au&0#Xw@bvfl0mJV3_d?-=VlI;@hb@aCBK|j&f`SL_ zIn!3)u{J?nhHcXoU3o=C2je#{bJAuljllC)w7XAFxpeGd({k;LnymfkKbf&4f&=iQ zJ}>FR!os(LTd!ki)?n6NW+1s8^8SDgib0~eQw=GEDKF#tzx);;0Zj%FpR+ShJpPY9 zQ>)u%(>mK7oISH$=kqFQ{)XS*$`Z29rVUwPg@iES>Ylct3W29;7#d~*8tO#;HVRRE z*oFxVnyHC_>x|;mE*c?M$K70i87HUkk$QWLIetE>o;MVDf@@`_fSx+p=|nx3CM@x&!BU1dF6L|tkR31wy;o> z8EUX8#vfWEynIgRgw~r&N}2~Py+L&nQtc4CIpjX@a7UBo6$*~JeGWM~602afYsb5l zZ%3dPl#uB5>czH?mopitwWB+@8Dt3FwgKh3$Lkxu0q~rtE3@;K-*a;)39><5!~DKk zS>j-0JdP*0u8w;c6=~Og?Gv)@&@wT- zQFTHsX|Tcb5_*J!2!r;E)SLeBi_5076^b+2y=9Ey|0kLSnH22E7&swAqx{5ZPx=wg z7YfSEViZ_xqW-w1y;0Xtd}LG4t=&0{)gP*siaILcxvARv-ZsalZ-2Mw6?l3L-5hxk zvVPiPFkxLiC`#+v$SB;E2ja?hvEQ@uMuD5Z#c-VlwBrWJ=SoVOzlwmTl!%b(AoYI4 z0G!0F^S4$)F5{qw^)W2THZiI%rl%(Cado&^_`?T|RFAu8l73dxS=e$x%_{g=m%VZ! zucbC22!uLwTkKI^_NSOaJ=T$PdDw(VO+Efb{Q2?!JpE6@&_9%%*`xzJck?K^>vW=FLY*5CgAZJV?aU@ zhuXvvq!t@UOBp|r*x8M+5JYmj()O<(KYxugbn|S|=+iMH26T{^Zr}?w}hRjd`5+WqPiTf4u2o$Vp47l9rcl&mX&v zkC^NNZJHhtjW+R;4zdHBA`$;L3e4=B-Hyd>V7w~`HNH1L;J8s!FD2xX`twT7pA@=% zc{{7CAoG?mg1PyM`Q#MYfE|B?G9$P@4bCv+y|a3EC8gn{i-IR0b~CYBcUpP#m&=h+ z)N8xW1QP!0r^CR(+4*7~7#IZlf<~L#2w91~Vs=0!p!~KVmzMYYJWBW-CKv*l;MFP@ zri1c|MT*#kI~%PODDM22F4^Ce8N)7o`F(5+=)>cPNb7m1ux;dNEqo`m`MfWnK?IHO zmOdoM-yRWVWutaKPTI6)n(B7@J>DX7T3zK2igob)bWrE*jvz32%=Q9CHj9eJpl3*+ zc!uDA$6+yEBoB)U>&znTHW)rYiCdADlM?}a6u_}LyD@Rx#R&(3`Z*?P6XWz-A6EKE zfC$Bxr3*eA^V7FF+Exsp|3(uDwmc*>%5U0A!jmr-xr&BO)c@J4q!1_n%g8$S{fL9yp*0B=e>lafuEGaAn zN0SPzeoxA&nwCsFVv$YdCjopN(_vT?1;Pj&_lx=$6`&F_NuBNTTz+F61~Qb&*uD7h z00k<&2JCoB8m>5febOX@tmU(?3R{2odvxqlY&NUouFhf4Wj`b=>?7W51#7NOBosv9 z(kyY0s}WN?9*V)@VQAJ)h6Z}0od_C17w}X3`tvafz@5XIg{8>s50n(F70I3ISd5&t ztnOOW^Sud!uUW8UWc2^E&8Fd;+fgtDqx?#r4eH){Q;eNsb|$a@@~K}+ve37YlS!BH zi*}y)Gib6({*S9t?_sZJAJlv2(z;`x$W7O$B!HGb2b#+H?~eOPQqL-X5Av-&}c3VrB$%3BUYLIeGB^{mcof*xz?}b1o zEd?LB*j`tRSg{Dx#kN~;SVnMSo>_o{nU@!d#$Ntu z>$IIFsb9W)M(TY72lNn_?T8;eQIvdq2z0SX&qHAav^F+FiLe^vj=q&t>xP7GTG>4V zlpW#)rg4>7RP_iVRupl5iVq2 zr3njP@3QEy8WT>|yZ+hx5<8$)Ll{H9akHlNnsj#dYGi&w<;{GZZWgY*WRoG_L!jV& z4g-wEk-4HCW_$!~I>E|mU%+6hJb0Jgq8HpkjYh~>J5hzEeSd!3bSdN~OWjfU7Dw#I zvtTkr*X6D$gY&IGah;@iak_2COnA7$;~PunrL7SoZo3sK&WB3WU9P);DLqdTkv5CC zxbz?8M`omMv|MurtXlIsV&zY6*K~E^+N&C@mwax|1|#3m?CqiRezFU$5zg4~&G+xei;HqVtB$I=+qQ(1 z(jZ+5f=DBIXpxkZlJ4&A5TsL(?(XhxP>_=D?(T-S`M!JayYIa_o@4Nj9N?Va-h1t} z=9+V^rK@Z42uR+*D7~Id{C(ejY*4^(+FGO>V$dgX42*nh?G_V2egiyKJZ8PL&Q_4Q z20xk@l)t`QqXvR~=qK`>Hz0x+)9QL4@0q?uK=$W%fTltmliAQPlg)c3PvVl9%2R$|w$K0z zE&is2l(orc-!;CIEv>2w8T_?aU7V;w;_C0B6kGF+R25;wB7%bCIT9&ey3?E-_B$+q zG{~Kx+p<7;Wz$@S%k)EhD-p$_ca)A7XMH_7uhjHNlowzag|l5G^Q+AS;g$N1_7XhR zd&%=P0m}4-=4#Uqb`FXBAG=mMUsABL!qrnrJGwwrig7l@`-e+4NhcR+qxl0uy!2na5u9r`=) zlJARer+?+})YvmHHWPS9>^(R$+*}^c7MR=FVWW~jyb}^2Tjv9)wf6+<4%iO4hD(6v z1*HYDik|}mombZ2_I!;9lOr7;V86@$dUwkjT=9U#m-Le}keJI8hmZK5%q-(p$@zq# zRulW6&Z%8=2^Gb;uEZ@WWIXGBO z>YW(tNC0N>$;uMM_VmlSZxzm=)6nzc+MX z22ivduZF8yt(NB4U43%VS75C%n??d;B|u#SpP&YZR%CWZ}cC6bor8knbbDKoKxm#J+?-^zCNksYe_jWP_%}${Nk(k7V2H@Tc^W z4Q#84s(}$`M%^Ms(Mz^<*Px;yDxFVbReSrf>l;);o2i>nWdG3w7Y$I~oB3*&oyD|NFP@MK43s6$FZmmma5-&V4X*G`~2%dndfrtKH}uU``GQ!+N-H z7K?6;%x52;$dleMvv=XKGNY*0?stGB``Ij_W?FjANsw78`!d#rHv&E|F%^lkwCu z+?--y+x|s#I>sh|tXYD4;VhNn)#3fR>EcEelwrq9mWS1g>B17+;5w0hyp}Eca?G^x zxQujldkf~AuBJ3v{M#y%8%(R?|Gmn86|tmX&wwHp;r~LNf|T?HghBm55T1E}`Bg4X zl)P8;xS|Fqz(%#THf4E=s|U4kh=lEZ0u*jJ8=+UF;nxT)#N}o48YX44+9vs1SZ0#l z6;{e9Bn1Ulz{CR#yEK`%b191M>aZreI~eImKA+bgikCno1t_zxVriZdu6NN5+$bO- z1_eET1`iL&{(18b+4?U5@uYNL&nW>ouL}vil%Rb7`Za`I1HkYgqZ&02{&!Aa0UFPD z|CWQz3!R!Tv`!~OL)yRNzQ@^{-U|Hy5d_Y?k|dnxh6?}{@SdKnY=@B)lm>v2@XPH& zKJ3~iajEdUWAqpd*$^xpeWiwln%&wuc~W0ktT`CdTo{^9(|=(0*WEF2cFvYgbF$&l z4q#ws3gPYuI3=~#7)!OWgUtw)Vm~rOz>UNV?0UHDeuS#Jnbqz+RcT=)f?oQLX3=P2c)>6{yBSh#UuT{;*jf;_*C9saHhvCq)gHMntR};`aGS%wsX5i2tDZnG^WlzlH4?; z&IT)&POjD{zh~6@$v<+BM@TrI|Fq4hPN(HKH~P)JODA4P_hurYor>`~R-gAzS2$KP z+3Qay)5XTI4c5eiy8s>FR95l6e(+d! z9Qaam#{)c)1=D4;?xw7Jm}roZuwk?b3@Uxocl)^~VwZl`B7yl77$!1{9C z$sR05?5>b2EzXODyV=`1?G+AWLYHMJszf$(V2Hs);hSl%&i7!4fubc;LZuC1aHF zC1p47mXmJvx@myl2oDDAbU`BHYn_F0o|dD_FDHFEXFKs*Rr?RHB33}TvEaPRT6A+{ zUV5NK9W6cwITilq_sz;@wVNAzbO^wFv8&_b6AU245p%k6yjXwv5_RY$07a&bv7o#} zB{?u%>;ylATO3`U_AYlSBch>I^WhQ^Q52wP(deXdJ3j|+DNWUlsIRZ@FYy)7c}#EK zkH+j@X?2^N0i`_x+&8EJF&C>m|QG6*hVX`l}Dqt=nUEJ)zAc zG!NHI2X*^6KVQ;oB>mQc(qrcTBYgkU44T$pDdR=Tc8J#mR35fhg^%)fXid#4aekxK z5BrKVGP5>8!V#oou{9Q}W*P>;`<{Va7mm$?6BC-KU!cFxhUI!CSu!BjkiVkOVTFQV zEm6V2+*wVR(jC&Wf4r?(J#C`AHp%Y<;3=eD7 zo&p~PY|Q*;BeLK3d*()(zy>K&qyZ&tUq6jjH4G3oA(~V8SH>0N(12~bX2WUc!UMjQ zj)Z2*Vbf_+p3#(n328=^#lVm$_z`Ao$)f%P*WgySUjF%V@zYYrvOGZNuJTx@) z{sZRRw6iiTPTX%CVAoDg!GQ<;6&ld%BgEc-P7g`wv`KY`5wqamNN+@c*1lA3=H0QL z1yozTZ8kau@n*r7=x7C_KRf97`CtDmF0R(Uqx^e^JL)$j(KVJ%GBE9iq_a$KC=VI8IUq)kWddq-+1XbI!6 z4sB@4&_sd@6Cw?BM~{C$s=%)pXx_^=j21V6i`cNmB`Clekzz?h59K8flVy+aS+(VT zVDe-u+RO~(DDc*iD@!Ffp$h0WDUPlFTvJ~=MA91{3jP+>(1EB zq=T@3K>&nHP(Hw;?uC1IRK~Gb7v0M^FrD6jE{eRoyufm9+eqfqta2tT{&y)ZI$zxl zKbrl1%3(ZP9#AU}Z&`U&?ipCv*##-1w2$9QlTlM+;I|TW6)2bvXN`7fF8dq!K#%4w zcmV@>SZ^}^e+b4#Z-H3s_*bqb446=7#6~HFw`xNQnLSqikErZ~ll>Y%W6RuoNp4pW zNbX>vv^uUv0P1s35?WtZ_e}bJUFdeT26YuTiZ7)v^z;;Bzk>`CwwrTl-)pz-5g~9h zTrH6n%F4?ldwTML6mQOUaz74YpV4c>%El%xPZG3k$h1mt0MUt=kKK)p<31a60iXG} zJt+M9_i|DjUm}n$zPCsUYV>&DudOmz!>8oEwnnixb^RVlh0rn51=Q8u6La0gvp>h? z?9#G#ZN%k*eFR{$HN2<(&G0%gkX&YIBtKTXj_n6GA-f*wkbV6X88RV^sR7O~n3-g= z_7Iy`J_>{n!M7cUb3KO`1p z4nUR%4Hfs;=v#Lhrpjder%v&HN1U6O)9EeNw(I3;^_wAbdU`OV3IhR6-&tSdNizBL zWjgr~V9w>xcn_x{@f)Zau7FA)ucV|smBhVaU_0O!sr0&^xyJd`?a3R&7cbg#Q$2hI z{h8G@9Euf#vrAijQv#8kHnw}yP(K5MA{fKxL30G97d8rJ|7o+7Z|Cm+{0%W+=-Zq1 zM7L~i#&o}>)E#~^DY9jBORgv@Ti)3KB&GY6L$PyFxj@y1frIlL>i@#uv)m^?@eS)= zvGsCSqXs>_MX-OaaZ17Eb5imA*5A|cA3*B)S3=0M7{FFh+5N?h_gIHMybb04zKF9P z-49jRXQAelAr@Nh2I^kNi<-KI97jrFhJMGX{MB*WIS&Ii(Q{LVq>~6a0~&5SG`O70 zYT{$YY&6EK#C?0EO*p80*_upZ;mcH}}`Wn;AYx3CJ~% zurK-D0?a+w&kwJDM3zJlx_UgoSvFnMYq&Y%FQ`3`ShN^JdcvC*9&UO(`#~;I7SbaX9E1GKNbRl zLfWP)H|K(;K>;+l*En2h2y=IL7m<|oEe>xs{e6BuwWy@zSNV6oK(lHwuQSrDN@QcB z-gx9+=suNl;|C9P!_V?z_qtPnGWoo(iCakGx6VJ;{@<`$>s@(Z;_|Hvm+2{=eXxR^g8GKH_7PC86RpLU~za(nQ`tGHNiX|=`t1d!j9lyC`_Ci`Svmr z4_C~+0Rca4L8e6rF6F!Vx=tb0yZe`UrB?UeK9Sl!+AiA)Y3a}#1G8FULtbxmNz)Wz zF_9=>Z_;yqO-$&)xA596gD(00%=2>y*C4+lmp2&|70-Qr zg|-%W7Fy(#lr+N|Zd`~UPS0|sQ_J1IR{axc9l0I_&`Yx7=25mv{%45V{am!OF-ur- zf&8^#;b{HKN{PRb+Su&>49H&5+%}H-?~4&K<1KnVKJFzuyUCAF_qICL?74pU*E$ zKnI>PJ9)x=KU2EIB&mI*Y-yN3Bciiruy8MG8 zTv#K2#E&4<_@$4@+3KfIq-Wrh+VLhS0I##W_mifPG1F$098;%orCC|QWE^~BGJY@~+Po+n2#IF!}we=kSQt{SprX8JLA^t8#27|$^ZJCl~t zxk_R>^7LcqiDl%oX7a`oHUOk;s#q}t%crMz4t*b$NLR07^+c4{bHJ9Ka)%?2T6OS;O_-99Z8E(RqYyZM~%sTglIjT{+Wv7fQuz z7Cc;r9>e>HeS1l|G@MRuHdPnTFlny0Z7YpQfRkaYVKTA{Vs+}l+#fMWshLBpzskK) zJ_?=8`xUhp#STJq(ets#*}kY;@=m!}`O(4S^@_O|7~0%tZKQK4hRpGGS1a=LEgaE) z@GtldZl*H_5vpfouryV=(g^Oa)O5k~BSTL{GYtL)Ed0d9S;PwS@_t~D`#C)bCZo=m zwR1S!c~w?m&P%p-J9L_FMc!l(@*;a3Q4VG4(!??!B&b)1o~QUzT>VLYjb zrx}vV?p%H2RU!+K@{j5i3A0&jJ`D_%_3Qd#?(jT3m8@LkL%(0Yk&(TSWAUE#{1_n$ zGAY})|B7k;v{7w@h(-F z?r^t+-s6T}WD6tP$M9&tO7kqLxYG4_;})Gx`?{W2@H&*;xt@GD%WRdJOY2@>$^#oU z#e-Md71b>k&Ohi?BXSU-A;~j>A!2hdAIxANsV}B@7C3``h@>!(NbbVbv>?ajHkIGFEALH!~N%~WoTR|!i+Wp5X^SHQ4@dAqICkGfULpj)^AW>OS0i4#ldW2o0 zl|Hxf?tS{SdGtLK6jgGTEs=v<=ulj0%v@~#&ruZrVUF5XOV`BOPK0PbzM>5~ugc{+2UEaiyiA+@3Y{e}{ z%h;9-O;31Z$f4uGYc48WuT+P_<)dXpIn zoCcjx+kC*1qmNuL63)O%iH`UTW*?oLv`K4EKl5rUbBHrCTCz2(MFU8$dU-wye~W^U zbvF+BV|2Ee3A1a&9~LAs;@p;`M);xGC%;c8-TUb`Cq6YfC7TxYgZTBJo|dDiX~p=) z8%bdy;b$FLdfB!ab;8%VldRIcQB89(lAm$n=Ty_|XtBrnrSaieq)^b9sb}pB*p*G0 zc#%J^4$_%RB(TIh7F`Finock|9)$j`H=)G&1lu3e93&SRvCr(`L0OXWzMuU5cLHlD z5Z1M{!6!g7YaK02QVk3lpcwm_-y4$!Z+@3GYRnkvo(|_$;f6d)$ z#|hqpb~Hhz+aQ+EK}(!V%xMhC^ro3Z&qD;)_+EMPD?U71rmwY}2R^z}W#mxsPY(AdzAeSriPKdEr@_gK@u zLMBtOj!dYBTRziMJT&rW(d|Jt6c&b`&tW+D?AKgbb{#7C!3^zr@F`c)!9eYxqdC0bZm{x_X4GIBy+PWhwblt>Y+PeR8kwaKTc1t z$Rqd)Y`SX@cQ4E1bXEQ8J45%>og6N~nJH5?$BzA>#gYI@ZnN`r=}-?nWe4+ShfQ@) zod@++Qzi-pNez|hJ=DShDPUmN-Y!r~Z?$FW3T#)iv)8BgnVFcrKbem9f2ypLF|H&S z+Y%{kc>9iCs5Gi+$B84V;^aB)`T0j75san7#q8ZrrZY5+46C0$5f`NAG{h9_5a?u( zBOxL$eE=4eavBx3yI0*6D+&B;iV z;F+dcL?k4END2zE54ofDyzKV~RabL+?W4i_UT~YZI%__$YM%QU5Awxvw#QD+vIAJq3>0HR2$)anRGbNR6KJ)yDuGd%1iRSICAIKGAV;Smv9 zvH~!*i1#2QJZ&PxDq%Zb#AlnYz)bEUr&S?PFb{9r6O-uBq2FNq^ED9fEjM(;K{-1J zF$TtR2L?0dwl45od$wH2HGDZc#UKfdx0#|L~zBTg%7@{U*{ySwAoJRde=)2*IH zLIaMow&rw$eI{8;WC0=QFYUriR*SYphN;&!)^#gqpaB;61_BXRxSyDqz`$FQ2F|Or z8Jl(-)bw=Z@0f`IM7+L2#n_hG*4Ad*^1yR!>w3Nrz1y#J^_u>fdMOV!0oHy2KN*MT zQ>3F{D2-^PcY#lwKbRLvD~j|d^@wbM*!G5&&z27~`uoXZm1*1)?v)mA{b{M>B5TEb zUPw=Cez%(Kd2)%Xxqisd(zpBQ=3i|(XFs-W{@w8Ej!)ArK|NYQf%|AFa}Ag1(1us_ZjQO--eK1{ zU69p7QSPv%sI&w}vn3?>1-|B3Bh`NT@RaX#$@6F4r-V!=?VosWuHI8hlSo_1L@2c$ zPQM3J*FB1q8^Z`K>uPjy2P|usrtaV8&=E#7*x#Gy?hq^`O6Y$YR2A0Xn7Mjm&Ub%v zp6w~lxAWUz!m~N>T+?!;-jMXvDVT%q+?M`iX4lgC-d^dw^T`#xKqKCWULOKdpNV`2xM$C-&XeLlehaoIhRFGw+!f_4xGQU_)3vza<9--QRy|Cq z^8fIZomoA+66SRhkhMlK-o}?8@^tq9gU@$h!)AF+k~qGhU+NQ5+5WuVS#)Fs9Vf>v zR)2j6)rw>G%9zH}7Lr~;(l?+LrTVDejMBIJ<8fA8$-xUt|C^u?@fU(2`8V1IqxiX(#XT>kTCacgU8soc?3JAQmNi%RO6302y)jh9t;^~+D2jDG!#1LAr9F?%Ab!u#NjgQ_x7fgdxP1b{Q>ieRd(d@Kz5LL z1olt3j{>}yw=%2%%)|kod&vN5p@%&0WyrT*zjC=a*z`aA`ZcMhfHqHzxjhY1z_$k<5olg#A%HIX*7#ZA|k~k<$4%iDjwUK+IuH zV+$Ar!2>0c#OUYjxR8@S;OpCa-LIMI;O@DxEp)a&+OK0Hu!9I z=m0SQV@Ykcv&rjR+xH9ZR(Bl~P)v5@B?-r)SuXktEp0#z0$48Tz9@$+kFWIgUp|#= z@}|F&J;tgERn!~dz~Q&!wPOZxgK|3~jJ-}c^Sef~ON8}NnNl*EpJ8g-5uj8&x*%MS z4l$X3ncX}5{#8c4FvQ5nMN?i^HcW`Egj3$aZ$RXSuC+Eg@v~@yJc~ceEv%oEGt*t) zo`rbby<5Kx@6}TK{AuOnBa4L5;o?Z+H3tCwu_2@fDy=3JQG1qvCiI+k{ipkDfhF%d-rcb zL51dI259r^yOuRaTN%C-6kXpF6QyNjprC*+)12d>8{mNs@1E-!7?eBx;uGCo4F68< zhlz*x6XB3%H=gz?)z=&q?V>B?Vycoo6y*LJ*WJ>Q5yQlZ13f9t+sToWOesf*-WxU?$V&I7r_jLb>Fa<+bOU^#t?9 zK|-)puI(TCzlSkE zmJ#Nb*hlMhdFxqP{gxO-*vH0Ai%3FJR#=3HPA*3&Nz9~(c}818v(6^s4+e&rcmHi$ z*~fKIKwNt`lWr~QrST^#T=Pvm3tSfWs6-I(I-Z;Cxgh5I>*C)w)LiP;FSUN!_IQpi zK|P?@kK=V`rluATm%5y?kZ952o^4%R|L##^G5c$bIw~MAQ23A7Q$bKtJCd?7PDhy# zh}AO{k69Wj%9apyU>{oVNvj@LbrFAJX#6Pt%VNf%=}vCF%J0-eO)@Ur+0-{*~R~b}Ls> zp9(VTIl6OCL!x^|qUWUR5$y<-c>Kb54;WNz0keQVoFiO2bCbgIde6jg&(n*Fk^p-e z*xlL9Gb&7u0Z~Bb0#9p1vb#h)-=W=SSy>?=g=mxeq@u#i1+~S!9x^gU;N3N9+s*T9 z_jmCy49q>#C#>V(8Uy8TRUG$W5q!%>`?hixJW_8xe z(ut5w^ILvm+OjvY^lUq!$eGaXvtr_QGL<#ypS4{i-k4HC?;o_(QG?A zzr{5qbEtivAz)w2h7v~u_gdCb1gIdxJSOI zI_HBm+I(80fIoUlSto*x!#U#l4ASr~V1^+>BHUrD`7jLj(~ zX>aR-N;jxR1{5brm~@PSIp@urkt`(nT$*jjcdS?zcjPSe%V}7hw^a(YayPf30B%;T z95bomN$AtwW*bZjl7i=AkxroQ=4g(ySPev*ea%AqEqjQs`{5pe!)^tG}NHxJ_HkMCiF0VB5<--Re&^Fi8Uyo?v^Wjt)prG4_8etj% z_w|d+k*)HcDx=K7zqb0*V+{HWk*rdVZvGpQvSml3xRkf_*xFR7VcS1=;e%jBDdHRXpRq!Yh|?L=xYNB2hYFa~<|Ii)FMo|t zMn&pD{cUwzz4f#m%UmLrEf9=;IbMA9=sxstBu{_!>f1LzFr^5e&8%$l2I=UMiktR_ zZarK{H5s_aIF(&p;G`{2OwOUzGwqU4sr~XRv9L0c6U%4zuu1p?bi%*Ipu~kJA3E#oqWpDA*JHqF(_^RVAt zpE8%UG=9*<=aiM-gDARIgv~^Os395N<-}&bAS`KbUWitwVw%ZAd_&-<&qsJXE=yeS zhLSR2RWj!W3Y&!Cb&hIP&b8A{XINUcBF}Mn>5PKoemYlTwMiyGIFhAAT0Pvw_^cvk zW}+mLnZE<*;)9M33U#O)ebt}11c&PPJMy4@m9O^ifLq{!BI1plU$z5#nJOpTA#GCqj6iugdRp zEA)3A0G}y>0(jF>9e2kPiY|Jh=Z*qkf~b$X!%@}IDJwN!j`C!nhwceT1VW<2(uc0| z8pI=x(wbpf;^dEdA30nvbLwl(Rqoq+OJ3a>f>gb?iB903TmV|vi-!;3=Wssxn7(*l zya&w@VGU3)iAlx{K%WJ`N_qK+#(MNvDH=%YIo;OY>g%tYTfk0Z%uOSc_4g^d)bwai zJ2X}lYm62z%x#N&G7lkSPY7K!1CbL3%qKKVEjcy`9szA{Og!wipxTAZ8ewA=fU-|t zcUlZoFH8BG+=H&lb{$<~IA~^|R_ol=^N7%?T|)8qP%b5~n8J8n&uGXI6C8Ly zKB13O&Jy_0vTB(jQd{GCKia=}l#vtK&}eJ6Zx4oQXoP$m7VCQ*<@(}Bm~RHb7Imjr z&vu{yrGDbz9_8-a9)AIox$E3eAYwX6Wz{S<7=UaHHh%}FzDvhgYq~Qkj-hQ10eB^avO?Aa7etg-?uLRP$tQc0E5U z*c;7}pkQE6-HfFNdBIZH;{4oB!3nWI=l}z)YiLy!Y{{WCPcybx*EgpW&695?PX|#` z^L=8{p4vl|6^5`+3b6TXT)1%_%g4w2+3DF5{*q=vznuqdT%d)yWE36Iq!Yhza4IT%ad z@P$8?_kG)NUmaeedb~;cBz5w$8z};=RjYTZW;1#K9rawT$C6hu>NKbZ%i=v^^cn)G z7$x|;ne+3`N`+VQ@myUdW)*;v1l3)Je_+1X$yjJ?w_Bi$ZA^T7yJ-P(RI z6C8}BfzUAw=!j$wn8;+37Vl)_)nPYFofNDtOZ4^8l#NYdy(AX$YW0jQ`4>EApXZmb zqf$^{3gko-WM!u{P-B1mTTFVZ&cYdVMM4Kus%19|pPtC3iR)DS5FGm=L=%fpvh#pv zv*gtqLY!)6OAJg*ZwfKd$ZsHdcDJ}eGdixH_Q|DqRab@qV63$p)6Ju+s3BPmSpoU1 z&C*9S5{QJ)cwZGYR-=?Ob{}@|1tS4j2SUM!m^i{u-#iqk%wM89I^L5vU?5G_krM{2 zoE(Tv_PZ@W0`wh1HoXeGDwM~4XN<^RX(Ya)SDDHj-3|y1Jr>hQ=nfN4obANAH&Vcz z=Q6UD%gB5^6o2z4;o=`xX9_w2&L7TTWrqwP(+@M=Ce`eba%D4}7fvoE*!2igZv`MA zzzM6s5CptZTFsU7LxeMFgI_DXzU01a5FtL0ngFBcC_g`VfQr6qZ@)WEasKx0S@3hr zI-xBQkd?5BgLwWd<5YkQz3vSdm-^DONz~pR1=2i7)#(HGfe%CpvYEF}Kx38fe*z@` z{$A33jcVN!viUDeQo!&vgcQQB>t*m@DF*Q-W8`!q` zEUy+cV1N~diD-_fqSpD_pwg<#Spt-g0G!E~fBJ<2v7p#>64TIl<*9y#%+@&HnAlPw zPU3MTwzD%>O32p;dqR_5w$m~CZQNk<@!|GmI8RF)0*PBlHbYZKi~DwROe$?Z_v2-3 zy=nW0hnt`8-5ydIRFdSB6}n;3c*Q;1tud;SMO|Frfzj*ckAV+_-rYlVtFxB14#tF@ z3sF7qhg#b1(YX5-HJyC-lIAK+hDy}=PUj+HotD}~oLBPS+}(Cc=a+0ZFktfDEIyX4 zXdWg-bbY9$GDCPBw0 zD!__~q2|5w{(0?{{^EmepFui`fYN5$SHY&LI~0(kuWclj2wDzTIvq2Mf9f9$;vsCE z?;xaENY`v_nR8mLR^z?-%#3De-Bh;aosXqiL%6Ba`0nY+k6fp53U$B9$~I)QzFQ|2 zHw2f^bkn-4*#s)G<_}`PnOrI=>~!8@tUAHTS%6=+?|YQAhD1{&N#(KlbmqI2lqm70 z22R7T8=|=`61O@^gU3sM_s(!zFBeLsohFo^dH` zW8LmuYpi~J#>!`?JqIo-DeUUEv5hwsGkagmFNB->OAZGWKQ4|`^jCz?_JawHU{)42 zP2o`=-$B)KpeIQ8y1Rja&H$)*4$FCR%{b*&bHi#CNe_%lTR^XYz{FHKGBI}XU{<;M zTtiV&3P5S+IKjQ^pGPf{R=NUfR+|gQw`Uk8qJE6U*4NIr{lpNwUgbGuyurV|*&QJ1 z^=)jBS?vT-MSJ3RzMA8{0+6?bY>$>MFsT#3pEL=%g2}GR@ zCa&Hu;39R=2`3;mvA)MdeCGXvxO~&L+}gSvG+$@a?cmX*veVo9TB_{m@j2%j z5hA5|MKm7X`L*(X+yB)n;Ld{=8n@s{X#BJL%U&7Xi{}XvKB9>JNMcKdUcWJ?;$f}R ztuo+syxS=S6`D_+C&(gXAW$_1)N=2jD3CtuXxlNY+g^)(nO%eP1+XN6=FMNQWS^nq zMcV69xn$f#=Jd6-ZM(bBqup1zqsMP%9Unv=*E~RThlR_6g+ThY%V0J1&{~qk1n_#b3j!N$|v1$Zqd)5NkL{v z>T$=pJLwtxOAll-OBEyJ%8ZW=$nZn&iZY)t$B^6MMP(`1K-ogtO|;TrY&+7~EgUiG6|G%TwCqx&r5M(cMA;UqkMBWZ?9 zY4UpY&LaFu1#t+DL$wQ^$lPB(rRUT*1Y^O)sM?_0Ccz$v?P3;aMK;?FO-!GEtx*ci)Udjqb98&r#uM>2jl${q$H+;6HM7`+A z4Zj*GmAh={HHH4R#CJh<$?)3>L0&a~UaO6V#~Xln>TnRz`3MT3uORrWv@K@Yp?}$r zkVAza-Pp_h2}9;i4kvJ(TZb!S*W7m6!h&ml9gTs`xn3MzaRdh%+iJW9@{OBr&DJlB zw-MCX&H45hI!|I3_1e7j{E$heBs5!FS8+({9xXuNJ>FZ?&^u%>236oQF97uQJVB8A zGv{Ux2e$@j+_|C>5=%apN;0ytzeo>V>l9phABS+|6%we?QxuwK* zE^7h2^Ya%c?^w-yTp;lwlgf?SjA`ofZrsQi-JY9J($XR$ZEA(_f_tot*ez{Lc=Bi{ zQ|PZC1=ZHRt)`WAM;WN3F?SvsnukY@>Ztu}@B36yCBM0b%9V1l$Jmcg zC?+!~tj_g+#ZMK|hDwo8wCWZQKq_nZ4T*DDhI`YU{V~N_aEHvi@~pVJ%xJ_3hTnY&WSOzm(8X&H+XJ zst=0s%sW~)P<2_jA(ne@IjlZBA*HdGU$q1K9RG=+a`1lypwtOWUS-N4JCmB2=>-`g zV+yfk5=$ZyqS52B{%%b&sN{a^nvc98_m7H9>T*=AW#=kYm0$tGM?eq$CijfIMim(j zj*6w$8N3+O_knS7Mij|irKR!vBZ+12%azT4H=o1bTP!cR35lH4J|Sj)1e?5Sa1-%J_&raeWT3 zw^@yE-^X!i7xlU*s%jFSO9Sx_q&aLjf=o%|B=3{ItJ)!#<3W#_(vY{aEt2sbV_0aY zaZHTjz8~fxR$y2dpzK)#<}NEC)9Cn4ReQl6g+5Dsg5ykL$d|z>{XTWIyifs0-XzCO zHQ)}?=Q&xTJ3>BWbC~+j*I-YWh_bE}6|3l>m`SRsAqNP1ln!HQ6aEK#va5o{rJ5k8 zvwB7T)Ol_0_V%O^WN2<*T>L0$Y~G8>L@^sVsv7LD0K&~4ut zI3FOX1Ji?m#`L^}i3tzrl?{Ve$5~nBC&$VhhZyt>N#!iYlBM3gE6B?$fY3OLL6F3# zv>D%aux_E0Tz6gcxX7z&?x)*N4<+r4H@njMGlzrotTJ|$`=3Etl4-zX@&)9@8uv$7 zC3Q|8^I1=_cdxw#eBhn*Dyh!LWgkO`*nR-kk?mrK2;OijDM0C&9QKS}zwY2Exb*H= z1%+TEi3_A~KCkdS&GEuVKG4ef_Kcm>cQ|@yEKSKmdQgqLW7(<-Z)ZV6%?Z=zU}gc( z9B_WOCUaS|0v;=^!`@vW8t)x)E(Mcq5B9jT@wy>krHr!*nQI*Ah|rn1Zw-<-^6rKF z>*5f31_vi=II+=T&X$OTin=F5@L4A9kE<1BD1&YYWPSNg;_O{dvk@d%z?a7%+VuOI zZvKc=W}TIf)fF1ufdp|B0*9iDrmxx95>_HHUq;*a{DjHH3f$e}Y)Llrqi z#ZvCpu|lo4*M+&eS7{P6?+d^XRQ-)%ad>yJHP@6xG9F7%McYVVUUwRfgWqy-GYk5o zDXMpPU@V4cWS5*YL-%$%Pb3AC_T9Q$t-b$AT7v>U{bBU|yWPUf2PBji+S{kn<(`kw zsKgLpvz2&lGcO#&dr1MfDDuX{ksl@PPvL%;nwSL2pQi1cal?<_ z4C^slA9v@gxmmwMLBa8uJGZS|zC1Yj(>G*tz8Y*AG}AQJ6Q-!5Am_l!yEn$B25P&H zn?7kgn3yYXWlz)cy)!aC3y0wSp?2_N-yX%Y#fVD?k>UWTDs7TXIghiEmyLBv`P)o1 z7)crVOtn^6$pI0bq=rE*0ue@*=Dfo4ULB>_*wK92Nc_pG3=e7EyU&Qdg+o%Coe{wZ z$NL-RpluKE=~@>l2?>_oA-o+Gr>1~7ixJ*hb^zTRm4V(kPTd4yenG*8-u^FxO*r^x zR(Dt1=4&7gfB7z>TuJfM8y6xM=v>#XgM)hEuUgz{S~vLo7JFiIv~ZdeSnw6!G?SeNN3 zQ_|4*m$uAoL7s}hzmdoviQE58jLfX(sVFj&HZUc6ZCOjB8a=(AD*}0fkS*CPmic>L z0le1P^PFlx(HuckG=z$aDt4|32BgcTt#fT+m4>sJ;*nh;`B7IZ{U}+7d;z1|A7k`U z^+}c+i@8|nYGa(8KWol6_Es1kG61THZaGVxUy!N#1)#10f)5QySoZdGK?{`>40i); zPK*1ZsDKbe!@~uZNe8b&zN@L-^f^F%MkZvAL*u&(`I=AtQN(${XRH4T@(*BF{>Q(7 zU6S!zAVWt&ey-K6dMK-!$)W>_&b>SB+KqLB|4k|#H4_(9DjE%3WzYqQ_m#6Wfns5_ zJz-0BT3Q6aH}k??(CHqXvny6uYC@=XcOvxT&(4rr7LR>QA%O`^RcDoeYJHNBA+(og zqU63MsHyP;=q!EU4{wMrIVsM7>`j0>@gp8h$Clath%D287 zGU6eJ;J*)AXm)Wj8%ioP&?#prGn+2tc!M-ezq)C-vcp-)!qBMrwA$>8PCPV=$^#t; zINH_IiRMVT`qNL?)@jV4^8GT^R1w?L?>%0nA5|kXsap+Gb-F*Vz1E(z(3@@tC-Hrn zXT{_>8*pvA*t_?&CW+ZJ>rztCe|gw+`${n}6w1?+z)aD@_I~{AV0@IE7#<$}t>59EN;AS)*OsKWEojoe;Y_Ir*ECiYu0nPVF10Yko$w(+LmQRYQ`Q+@D{=^*S40 z69OusntGQ(iSWMy?AdC+X^`Uo zJldzk#yuhUgCQZv4U(F3*~qSj+NXqt-=;{gaW)+}<`XTG4`+Jv$(ybx-hmKzdYG_l zPaw@*|4lYi<00 z0Mcx(Psz)B3j?Df)t;AMl3mc?7q=w}5{AKs<_g+IKbl47$<#Q{$-@K`PkTr*;09h^w6y{|^VhA`g@PL=5h?(r%4hX;bY3$hm(%#< z0$@brYj#UY-d3Jsu7$Be3BY2`Ao&B^5cP5?Q_~+6A^_$MrLs;JO4O>I1)&l|iHywV zQb)S`B4Ivtr0Vk3{tJ++KgG+s-1n(3EL!YaDA^MH=3NJ`tMxiF*jCv9sCI56`R{a8 zxyWx$3|3Dq^!kSxPVR>_Tfomjf4O~a^#=%1MzyM$&HFy4PMNase}k_k)*3V5`owxs z*Z+x1piFae6!gR?s>lO}%OdER_-a9p)UhgrEFlRp?up`Cct9uE@mj;d4Fc3tb2}sE z$hf#A!U5ACv5M!}(cpH+AYE+!WHhhG5`n&0zAUVmm<$XYCW_RDzFEiGZ@n4^3Jy@) zgPO&~k)2MU(8k_T*A@hvh+*_vE=XSB3p$-)Ek>iyhbmXoSod~sz@^PFLn`gI^{*mX z060E$IO$(wmU{A~c`|>(;iD&_<}d{uHVuz;Ndf`_Kt28T4B@obbpycSv`OSg_@ylu zlux#du<#V8yP88ds2KKt;elV5xP_jGtGI9f2XQq4S@oqI$>~AUORCL9t8TtITU<=3 z5aG-`J=t8q5`99DIl?iL(O}x)LZotmidcU}{b>Trv zqaY<+f&$XrDIl$YbV^8fr*w&cNQ1O=cQ;74ba!`u_n7mYYp=c6yVj5I-#(5#4xCdx z&@p_T`?>GyIwQQTZ?PwZKxde(gh5%b`IBoM5! z^H}L`7>wg6r%b?0TKoeppf4&q^M2cZM`=AsdgUZWh9>2m$?Dh}f))_PpYFBipBEUu zXt@lKHsCm<1L&#hjHDiP@qy(aie-Fd4)6p*buRYtgzZTHj5$z5f6239q;WEae74<5+g%3jbX=PXW}u-hFthhn;AWqlN>`M4pA$xLH%i!*&=C!|T^CIg7nr zLpyiv{)7s%PvNk8PO86vK8Z)@P^Q>_V8siiQ+IwyIith6Mmp*|pszm|Am+Zn(-zS_ zw>mn7D<`=By^a5!4^DzIp1SV_?O(8PY$d_3o!FsrL?3t}M*q|ZpAVL!@mNokmE?+N z!F1n~%kk#WCY_1$gUK?^!~}bxTr-fhd9z4*EZp5{buVh1KRqnGdE(9M<@Gw6Cc=TA zsHt>C%W?lt_=urcRX@1L;V(ShWXtV2Ef%_7raq8XFPyws@Z4MkZVb9i_|AJ#NK(=} ze(lRk?U91U_cvC|@nLH8=$ag^RHz$^5v^$g0Dh}Y25F2RqIzE*{TdTYE zR<|hoK1VkIv9|Zc=I7;eu3DXBw3-=pRlKg*&fWzx!VJmC<8f9Q0B7ONaMz-};0bDL zqX1Hf+OyH8bB#-mk>fInUD`Def&l*HW8y)*GpQCnNsqtSm=TpZLu65)isqz4Z` z+aMP^kaAVqXh-6{WmA;I??>nG0sp<7tj(rz<&-jX zpNDdJe8CADZLioJm_!&Z;d;I!+|}9+_FE`>wfkl2UkI)QT6=tA;J%ZS-s5FA{>H)z z&(IL0lw3g*wK1b*=t(Dg-`hY1uk}Yu@?n0oJM_LAbeGs8JCb)>PIasl)VOB!)<4xv zmfitBhoJ=aT~LBAw}OX`=usBdWz5^(lHfS^7YlTYb5u}8m*w2B&>dQtuLyJyR} z_wm>PKfTwrEF#+5Pd!UM1A~=ng;4e?iB`o|z(KOLJ4Hr8f$08^ELYQ+%RO0xP0V^1 z&*Nw+D*NtF<)y|}uH|-or8$kZ$sx^)i#Dj3b6%War**!tdQ)~8#|D~9SfT#`JS|Fm z{GSM+r_+Ht?&s|H-xw*Vsq;D)p?%avEhn?fiLPKU>f>bT9}^ zQCCbHF_cYauD`~{wr|Nun*3@XdOSq{I9x-8IdfFha)J6eGhocP6qNoKfI3{gJA!cB zL^Ql>eB4c69~MBxzCe=m;JnzL{3jx>RPyy>O+IR+MRxHlEtDBO@Og&BOfd(HfvIKU z#;WD5NL;Qwfu564QhKd^RM9TtC3EhEKt6-u=R9=iD!IZ1CmE`Ah)i%;*f3RLPtX%- z>Ar@W!n=pSEz3sa`6ot3l6k7~_RWnLn_kW4j@kfUo!}y-pd)nUkR#yGhy}@+fUy#U z=|_f5#erK0W=(8$fo*;#GxcE-`yV1R3{5(r#HUNRZZ7f zyfk?8V%@derMG7p7^NQ1Pwq1eR{jq#>C8FV9Dmh|=wke#i$f<R|GoPA*&| zSE(4saro{OaF6>ChW`y^wWvAg-;z^R%H5x>tOkdMes*sOeOcs2Uq-L2>^5qUnE%=A z%5xT@D1pE61F5*AIEzsqJeAZ>tHlLP7F*ics>h|M0kgZkgQOTwDWcv!5H+3l)vMtQ z5lrO4)&YXY-^$62z7GV}C5AgO!Jw)KaDUB3UA|@pe}x1%PtsIg070Wlev`;+w~DaK zqDhI`&tBsu3vmyM$~pfFf7Pl7p=4cbr%?%Ze8Iv_HK*m!8g5YbA5QA!*e6n{ze%Yi zkAokzBgz|yx;G9>vPczhjjVZWaO0NfO$CPD1BA4n3W|_&vp-|YcyfVDtIU#MyAgrw zTZN2_p$|j}wW}A2y}y6!cMqmsEwv9>OMjE3LDks5fnBIZAD)Q+U@KihV5&8^Ff{%ZdW(T|0pD~+>e17+62G15Z2 z`az?vvx~)ktF!Qr`vy+o!Mp)?j7tY);{5f;0d8PW*HFJB3J7yx9@PB+ht%8CL&d~| z1$cLhU3V43dO*_PYR7LQ+eh+@NbIe(bxubbE|44-a?F{&vuM11#;`ZSvrw=8VnX{i zL*lgQA~J!8ftC+oWcKy5lfSsTqw(zn>m9YBCG^kJWnXey+8~~C4m{!VnRnJDml_s3 zH1Z;xTVb7e_O1@Z__WH5o_!w#Fhf)&1l7z-*inU6(%%@)YIsluE5wxS>m;??Z9ND+z8T+uma&9K13yS5Sgf)KZDbTbtwLEwmZ?7yJD z;5=Qg3#a!ukP>MZ;j8M_ss-5SuD<13<{IFT$LRya zp>J{|)F*Hm_D5bVR0{QT^l9CWO@N9=Zh}qgR|01&^Yj9{xQrm`-;9i;Q_ut|Dk=Sx zQWqD`3d3jaICb0Wc^eca#1cO&d*)eW5H4Pu^5WcDYPCN$=>&t%o%6zNd^R@f`3THxx*;5TSuUAX2!!^}KFBxqn_x_nHbUnlPN)#?ctOW}u01fai|OJ{-q8=2e} zNrobm45}fV1pkfJd}^GeI@10>vqrCegBIHP4nCOw8Bb^6qWMiknm2^8dGDL!N&|k* z*!B((pucjQ*$f`ohb}K5aP+*t`R2JqR$2Knp2vs+3J`R35CSS7+l1bK1tOD`F4d>^ z07X|ie}LpY9Bc`jnuor;hKqwlL-o|BUrij>Y2iv4*G1DL7jMIi#{ek%eu`r^%rqpy zZoDJy&*zXgmE_Q7ZIOHuz*XbpIRB?${ghPZS%8LR$HRpia0^I#S6?z~bFkpIdiwZF zj)q>yMNuzW?-4m`RnN(?|I~4#txxpE#%vDpUrO}s&u?8oY6lQvOi!B(?AvHK z{x1$`o(ABMl3-4K_*_2l$p93X@z?oz`%TI8uZx!eiwICvdItr%(~mbn9aqDyhVvy!UvnW45dHJ=Ox=B3zk$ z&(TPxm9<(N;qA^;poi%;k$`PXS=otWwalK$UPY{`afh3mMK+=Pj7062yQ(=RkReIw zDnMQ@xJLdVG$J&SlA;6S)%_7}Gkt{t_RctH(+KNh1?9i}PlEOQ#ofaj{_*(IMGsOr zDcD3}x)#!VUzcBFPcD=tfFpJByXIjjCMJy0NKV1Sx7*w76ha>hPTot3X>y5i%9&0` zS&O6W@gP2TknR>!gP_Uq4Z#5l^z~~i{Yv%N>e**uq)yzbL-8{!|3y!ZnD}qB+ind? zSN|SH03iI)PIi5im4Fvz>}l z5@s_3cn@m)r*ym$Ngekkz7qo;HW%E@OShy5!o+GY+$y7IHC4LRs7K#Q{k_4kSbhVH zYCc*(JBih9tqtf|TdgSLS z->6r2!v_kde@i=cwg0>7KF;ER?tM!dnFOq#xL8?Owm3X&;#l^Q{Rgb@vGv^))RGpL z?X3V@_Bk{=;%Gx7AJ{bfZvI>}Wy*KF_Dmw2WahXKlL4LU^t5$|l&*;vpYKq)tk-(< z*Pf%^XlTn3V*R|FiwkubJvK3MaIfJBPvL)JX)?9!+MBDq=%l8`52yK=K{>L^U#yuk zCM6|BKobq3MoUzN{u3>MGDr`Tb{ya}I+ zEN|170kE!bT4Md^@z@@W-3dV3$~p~juO_ZOT4-G<1&ViOZ8<3AIPlpA3^awjVW@O1 z>KL49nX-r>9B}HqC@E-wmGR(L98m-d!ObC}9@?EeeKI%lzmrDq)XVe~6D>^MzJ2nI zid^a$Px9b-?Y3ZA=jJEOly6iWZa&aqaPRrl;PUay)QpCozPUYbBfY$ihV_83F#^12(DGZ1kA}E|nw``A`O_NZ{yKs$Pp)d!FQ9 zWuNsTf1x&w2#L5S(U?{mm3Qhd10Y(7-%8^f)yitUQ_m-$Y1Y7N8rO3Q-#gCw-~WN{ zFYPM$!y-cVj<^q?&$syB6@l>Zm!Kdf?T<|zyNj4$5r6B9M3qE=-=qazCIFXL=MsT_ zAGGsVfU-&fj3j3WxFIWIXskGY|2~%HG?aKFM7AXc5I$?)f1!0tzLMsZbpQIZfBi2_ z86bXtDbfM=wAk}AM`GTuB5vb8*D@G0`r#Vcx-{%c8-blo&8~kqp!K?aNr`dtDk>O2 zwFdk>P@kGiGyWLZkQ)M`dH|+KTgVn#*ZuW|zKni=ghB1%;?H+ljdumcLdU14r*||J z1>ZV{8hb+D@X07BEPZ?vdH*<5e|VJps{wOqF)zM1^HM@Y1O=I_(FwW7m+?Yd1;NGv zZ>nry-l0%U+lV2bvFN!?;b+k}3MFw@)XcA6&c2+b^q$>5*tCE7>i9A~p6ckH4-+#! zp7%1v?x1#`GiTbH72R7CQS8Ry!Mz>1MHi;14*uwL?cq>n{;IqG(YMnK2C`+(gBFU` zeJ5kwE|c@(kR#@-5rcgppZZDgV#;>V*au5Oy1c! z_c-zPyC@@`*XneY0VfJdi1k#lrOi(1DX>XOD=Q;}q$&Nv!XXN(EzdESE_tiMMM3dX zIDmolW-D6$F%$uH<7~V2DFi&CQ86*zR-EQUIM=4kup06(vv##sS|2zga+1IF8j5I| zqct?}m74Z*+#Soyr0k=lld1@w za)t$kuEX$uA>wf-ivKv{ydS%Hd@x&#p*FlVU5Y*3{KynR#Bb?UT#<=JKrkaG=qP8j zo|c`BYg5V$CJ^G_%zC(+%K4g-(uo2H&mBwc!>Zbf!4=p9KRAsgnkQL}FVM^vkFkGe z3vg^!sl$X`$tsdqdMQA0}p@Hpmx|1>)>`Vr)gIn~o`4G4vk zFnt`F{XX1uP_%%}=vx={E$PeWq9)ktMOU~K;IcG4J^f{$*K4Pt`K%CxFNtVri8k-Z zgQW`K8n<_L^0Q?#<|XQ##2H-^8@zigW7Ss zd4n?ijz0s%mlN`fTh6Dnf(7rFVLHSx+aqV4qy}$i4DRnvc&edEZMl!r`#&)~AFd&7 z?V`ZJ?@#f$rmYl8m;`o2R&vXl6gJUA)B2%&yNWtuyae3r&@Chy`6PZUUHw*Gh`cKn z4OiERQ9)_(q4BMtt|i}6I=oqe5Bla^nqd`W7o%c+7(g@6cr96r5xEp;5NR6ty!*ew zkjl=6|9n0ji;6Q9Tx`TbMu+nQm-4})0-}=@+!{J_EaZ-5o%@8#1@XxlJR>P3qf_ZP zP)$L$fQQ%3_^dgH``vcjbj7x0?!+s6Gzp;bLcT0&zy?N@w)du%rmc^SSA+oBNmrq> z>7~w4RD17>aB4i8=q@ue$$MBN`QZclIEh&jUpzAK(q}eMI_j2G5vGabj@bbDW2>ic znz}5iwIiJtZZW|~Fh-jC;UZ2FijP%^A2^9<~ z3Vd@_1Wa@mk#+@I!4DV+ee*5H?^MqIWtKfDAc}mr+I+cRT)*QuqH)c|JJ1-L9&TrR+d3s1D*Euo4^^vuJOF+qr}^TD$ee>ZzPEW0hLybk^F9$EiyTxGNB}` ztv+g}D`UhF$tmAm)-EHLL;CS3*&Qr%?3*5!yMYyC=OO6>I_OUqG;aki4h7+3)e-VL zL$YLWm%CmR?Hna9Ty)MD9Gz{$=Ek6e5hlatPfXI83~MxHf|BZdy3{W6{yf=f9ux9| z(}_lfzImR+-A1A*GlA0>vFT>Ri?%pdM9suH4kkKfr(xV6=e1cZ%BSco(WSiZ2Gkb2 zp=LBeLDGVo0sf1YZUZl!a1bXkSH6O=aE0~sDf1N*6H9s7Q^B8)15qDdJ=fO{Ag5%l zmv2RaV1X2-6+l!z@9M|GAobtASTEWCgMRIPjOeDysGyGdj zFe(No@+3r>uiO>A%6#63I#ii@D)mkC4aWI7y0gmw>k|~T4ljWR*gy=@-IB0z3nSpA zd{A%M*^apos612ad=npMedbIESSY*J3FtJ~(7&Wv;0B9=vRzS44J}y?6>@Yjy6p4e zHFzgXe-R($9NW^HS}!tFLs%MFA|!cvdH1trbx2ENv*(j3EjMah`X6I?n#5A_@;`r4 z9FGKkR@Bf4TogFUA-QyMT3-!eyL=y@bfIdszGwb1$JTn2nA4!ummBJeFqS{1dwJ0K zgV!m0?Tm;!A)xUVWBGa_l9Gi54QxA-5h+_G$qV15-i35;H@Ij1v^yf_6!8ydM9ctOkAU=7Q<$z`55=hHCWz|oROUM8?pUtI3M zbPW$@xpnbg-FCsqmcY~P(f028jlACM#e9wZgL%Q@mhux0nC2Ddm%!m%uH6#PL*b*M zzc<7yr{HzV;-Y|>icU{}xUkJYtoJ*_nM~xb;+TG4?Fb>mhFEauuc%9`shD07sr{f5 z*h?+KMT$RDf@9V9IaXY`!9h7b4^z`M#1mYIjD-EvQHewpveU_kjfnbHgP}^ZhMwRD z5!~-!D@?U%(ycr1HKw#qcynny4TX0RxNx|5qVv=&7=4&L<`vV^?@_S$cmq@@SX=mvmYuhX0(<;qGxdwV-vaPTp(OM!!c7%*}Wu!G_c zi|F^XJwTkyRgFr{h?fH^IKyZfoH>!@V{^&`(o&J-eII|NwLmhn2mOItrRJP9AjxCY zU+&q_@`uaHeto@mbUZl&XQiBDk5+SD^-f8I4|s*cKmcuqhHDjDe3b$9{gDyZo-Oeb$Nn<0wQU;*{2>x7yh(IxpRk>pR5NV#0_>9 zB@52h%hl{~4fOW5H!o@jdNr40EP6E~uo}N>?WsDK9;k0_CiQGQkpnl`4w&&u+?|bi z7N|R50;Ro`D>$i=zJMYpZ9>;O8T9+tSQw0mID$Wu`~1V$3VofBfCZ`uYVs%E#kfc2^{S|+FosxyoEk8( zBJN(c75F%&Y>?$9eWn%tycb{q9Yp#xbZ3VU=lOG>tnMShF)=Y06E&cWAmNT^Xn-v( zZX!#osv@a8KmWEXZzqGQeqKmSf?JWIesxn3`EjYI%GVc`w_(!NEX~SdUpXOGnmPb8 z@_^+{Wu=4KRr)T+&5@!_*#HwSdJ)5xh~;Dodq6?ou8dkDx{UB1`l z$1|Hc&|Ob_?RrWT#185owax?AEA=F{w~5-tfpMyW+0yT6d759^-|0{zi8R(AXDCy}Y{1U>e`kV{KBYzP7Qk+R=+ff4lxHGb@Xdmg@;9Ye8^Pvb^QR8Om4T zjiaWm@ECXQ*u)cuB2b9y?oJZpGwauAL@0ZC>3|2XUE}cqQh1|c!e9CA)Us6VYiu%V zr8Jm|&};H|aacKnMUL-|uLhe-8W?)r z34Ix$snXqIOjDSFhj$Au*ZNVoVypf>u@NP$yv$cEn#e%>B_~+mYK711ZsS*-!f;yx) zINztceRGSggzS{xvwbE z?U(b^Q1CNy&tUkn@v0Ak6ePrqywuGalqbHrqQ~=1P;0kWk9xK=SZ#Wfv4;CZ?qChJ6nZg_z_ii);{o z*h((^?X+ObrEP2s+qm!b-XS}Cby{9+?N}LD%xHr>sye@u`H53KFP_J|GD_Wbcj}gN z3Zqd@J$V@E)@jjYTFT3L@a;}@hncO~Z_A1sT$aUr`ZOxq`gfqfTPZ0KM>*~0DIi+5 z$!>rJkA=Myj|ZDjW-%9P=jBVE4C#x`G?CySf|0#(p9$6Smw^BXI;S~}u0Q42^KmOps)bN`2 zISZp7pI~2VKTn<25QA7dYs}mHab05ppvdyp2;1$MBW4?Vdwoz#!THoFdCK#ikWM!=%+6+I4NG?}bS65Jb9|pGKy_s^%MbG-DU_5i7 zf~j=kxwNEczW9LcY4>O=Choz}{rMj*fP`+RR`m+FQa?*_zi{$HtBAJmm>x`@Z{E9~ z^u|GRkwx4!Q>V%wTb_dJM@~KCAb2|@i=}~!GYQlE=OBHJg^XsYfi~tHk>QC6vcd>e zm3mMWZ0{yI5BDpyx4g!`26{IuekAi~Cb4JaVwjjb*ASr!gr&0(#Y{5AK}wD3_oidk zAJ&(y!0(yXp^bRDTv%JP({*=Uel$`2*j~M8*+J|y_r<7L6V|Uai2d^evM`O`guZ`Z zAfYOw#tyjWb@oqRy~;nDJ1quTP)jFSp;%Z;i{y~yo+{Usg7Z0tsY2(~aHkThM{>17 z4#3}q>7~T3*Sz?iSkzvBSlBI!DToq1Gpe`4Z=AK+$zDv!z%c)C6Z}#K0s$hMrR&Xy z1M7Kf$%JIB!BC~m^xP8sA>bc|hDdpEGr=%F$Tgh278N|*wED8(@yZTF=t8?MyHD~? zwEX7|=gx=sk$NnN=@TZpY>C#uEl=P1U4>{r6?JF(QSH;!t_T=N7%49y0YT25pDb+4 zPybVp@)Oj|UqxtE2@MX5d(&7j7KYVWd-JP5DpfUTk^hYLy);5sL}SN7U3(N8FDNG_ z@bXYM98q3zDQ&v(I6{`DkE6k8f(i!(0h|)dV7t_Gn|S{1R2y{h)e8?0&N=Jn`ac7M zr^@r9!Tye`?Xl0mcR!vnY!-0U34luztgj5(4QfD$FVtg{kMyB!!7X?ng-xlA#5_~t@7&yiFVb1ScvvVf&4=>5+pO|WV*Q)uO(!*% zp<*8P&F^Il##yyfbw*WbzkZ=;Yu91G8DhM5og2_Hb^OGAxfag3=-+ArvLo*ZjR)Tg zWgKtWa^TZ#m(G}jXkH&sJzHA~#g${d{V4IPD}V;%a8hv5rg_YH3xKB0SA=mF$Jtsu zVLrY%U1bx=U?Dj7Q1e>Ty5Hh;@;8a|s}G_@ny)zp|Lj-x&Cb369WD5>t(|+gtbK&o z-_*DJ6S$)H;qEb zIDc4F-iP2vHO?CqPKZ#J3i+#7WkcsVRaF=u_!~d=8$8HY4u4TCQuLLQntNo~g8TKr zx1I0O+Z9hYl4IXE*{UWae;iZ-Rj`=Ym-E;BwzWj*>8REL} z3kNZ~8i1D&^Oevy03feas;2CoUPepOtkh0KmXdws)Ku~U;x*`f2O~X@`|RxIvRr*x z>|I@r=LFhP@Tw*D!xY0y%gudT;BiU~Xh+~P)mNDED>{J=7#()H;~O}c!O=`sr`x-3 z3*MxZ%%lZG`r^Vu|8(%sOL|yP^*~>?)K%_VfjbH6&TiXF%gf70PUo)t01&!!M^d$O z7KW(HdVjz&?l^jHC#XXYK2<`3`cmFR%dGJhMxTn>d$jUiSjf@osW1RP(|YQ4u@w|Q zgttJNg{LUe>auZD)X_mbhh(mUwsAu63Y0P_JA9W2T0xsG+VV)3=f2=0rjg)0k6p<# z*6t9Toa??=TFawTXt09S(sCXe8sGhM1u_xY4oAa zKs*#E`3Dch&^`1;U$l|{GUw{lp(FtW)=%#UD%L?2bi9%ggxijNJS z(sltgN0aEowEI>1?H|jp56rnIMH6N=>t)nOm-WtF%hrZEHRbNwUaJ;B${h z89LMNN&!kccbyGy0`%wcLmVcOH*vM)gQ0cZD8Lz`94X+c7ac|ahB^!GFK=ruc63Ct z92-pYK{9)DiTYl#ku-JMS5YiTBAI7_$&2~bxyvaOi~ij`|bHmQV=pa^ZkO2Aq!pbazDW>7o3UI+P zuOLJN^{9k`u2A6Q8(&!=kkF`(UibtZBVbZ_R*#q?5gml!q$>b$8pRg^yeWifc9Y7p zvpS%MaJ%wTOss+Jp%aZzQ*Z0ROs-^!1P5-|Yqo06-fn0u!&f@vY(VayshU zCpshAtU=zgH?I9|R-4R{7ydCf^y>{H*pj2lI*U+50v-wBymnpRYJIiGH8aVr{#EV?0+}Nv8(ZesbxBSGuUjflD8Tf_YNuwVVircn#sokHngT73 zmUa$&A|!tRB}<8}HbSaUP^k#-ph2FD0+E#QmEfa0WO+FqWVP&1xiZ#LAxLB(caO^H zo@63N2<*48W!fe82t_ zgox`u`cOJw&Yzo@PKah)fY!HdMLce0BeA$rP^|Iv!1euy38DS;uHNW);2?_6N`v6# zZWw!yIP|GpZ(rlkU}ypEXmopc^rxh?HQ8jtRRur8VM9F$uj4^!bJJZjT`-&^^az=Rue( zAK^uMj{AIdz^gG3_@S7vk(8)g)LeIcE-@K#@7?{XBmlsAjccHkEl|5o^GGHwxP^!Q zfRx-F%rbc`(C2K-6LiaoiREVgz4K}Jxn{04R-gVD_aM(g%Odh?j7lH@7&YfqQ{??E zrf`-@t63>%4#aDY1M-P}@hT0T4%TI#sRV#*(52uUZ6Pqz0Yx>l=bQ=f=gG*f!cfkB z*Wq!wKu32tIF~a>VdN#neaW+%KvDT{Rw1u*yQxvLa?<%$HhGo~{m|=*8Q53@OJgs} z+K6)%uT0H1b1tbuVC&V4`4?>aQW_)|{rI8R)AQSr?`mluJ@R)31TuE!qUBgWoa;S9 z=}lVZO(`yvhS5Wd2M25S?2pD1v0Jz%^|TTR_oc`)xCXus6;ue;5^@Y@FazOW;~O)! zphJWTgu>5fEf++`jn+D3Z;{5geF7#g4)^*Hq<4LoNNwsn&sTQg5cIv*+$E*Gb>Fne zqmJ)%;$mnMmO2Fd1p{BdePSxV`%yBqvqjXL3=5mA%Dbm{ds<(g4F4UC`Ai+0m&X~o zm_$HENeMnr@>S@U>NpI{0`=g`OlOe(oWui?HHPltq$shOOjl|!9FG1ifQv^dCK-{f zci1HA?0LQ#6o%$EW8M{&@scOg;Qs_W6VK5XExIoD>yM`-NhOq&_`uG8{CkIey37IT zJH1?N(i^(W%$caOGcoCf-X!uk$CcsM1-LEH{K+E0I|2aiiL6_{|^cauJ z{z@%wGbQDsKIY_U^+ZtIRDpz$Hz^RP;7CShb0-0ndE}key+j$I}EM}p8=7gVO;;F zOES2h7H4IbptPmOjxeL5;#~SzDRdn`$u;iwQ}cXtz2+>d5wn(J09AE%b|zzHCRw)d z^Uwab(Cnt8nj~NbZww?Og91283i$~-vV>G`{6&j`GB!Be+#4J&gr>&nu!-b$K^&6x zj1lVB7%GVXaOjmRjdD+)j(G;2Q2WQQ@qQz5vR2_f{L_ess=P@FU>KHKG*ul3m$-R| z>+GRYpXA&mACO54F~+SXeD<2jDWY+^B=K#^dVX0{(=L$UW@2KUdU9CcIt@lrp)n2h zr_(ks>0IJmN$k2johF%S&$*J>J$^N^_kbzj*BJcBCx5BLIQq{_$bitBgfoo?Ca$Vs?suOU!b#og*UCm{Ukam0hn zI^T{C=fGVx`E$v>^A!>Psi#q&OV&k6L8&o(rWX)eWWr{~rovv!)(E}F4-$UU6y!6l zr*|`x@ZpGQ4KPgV-XYl+rd;>40>xIJmznk&YQnM$zfV+pSpEh7KF$I7mkSF2ZiX)s zycj>OZ1XYBoE9o|>keiWa;=>XH#DSWa`!e~Q+gGPFThvVKVdNrsF(0b4?MuE}QiP2y|o9A{ZH-eUqLDbs%Yky9ZoniB( zqb8S(m5bA5>8hO4Mg92c57vu%)(BxBrv6pBG&)1P5E_Ig>FMbsx@#kG{Wecfsb7I6 z4$K&YXE?e7mD0ZqPl^qXa#;GYo*W$5Ia`@JuFdTp+>Ff&$V!KF-|h+o0C(M1(Qa!N zer(XIFKurl*J?)8RPn66CEm5v00j)*No@qQre%Y|yk~>8=@Yx>M<=%Xy1cSo7b*~R zR~xh^;c(3lsptY=J5RvXRf8x@&{w5=?S(v^YW#M%%n;UaLWx7|^R}EFRO)uo@FCbzqHnC5A^{$#hg99?;<^FE01jNo+=IO494zu*twklaO2h&hodw` z&(1#x@|e>`Dv$`*SzJ;_=Vm!Td0cMqylA#Nhm%`j*Zut$tQXSfW;U*FyuTx9zaRj`Gw;4N5)rmWj?R zUQm44!zjmbJ}S>BQj&0y0GMSA;%4)ybG^?r)9#h&?wu)ILQ(#y8P27tEj4 zOLCp*PCMS0&G1A@jyTFn)(=ws%`l}eYL05=KcmLas$+!ZO&!_z0pJ^8XZ&T`uUuAD z>*Jt$IHG5h0H9oyM&2ud>UtylQ@1bYvp&h3(=RzWF9%~(rx!n)SqdxLmA4zQ(!YY8 ztvZx25LGV5bh)3wE6NR&j(w(*UpcgR$Z+V#JTN$_u|Uip=@9ouvah7MJ8j$+0|P^3 z#%11Fy1(x!(K9yPO&2F(-iWn%g4_4DOId+-GGK*I)=oV)nkEM880bEd-VTWY)&(wr z6yt2OmyU*b^<; z80KDi+7KT#quF5f$`Z7Ue5(qLK@2*3Zd`Qrka_h0aAcw1kID9Yiz#L{g!1|5(=o_< z+~|f?I+Ud^NvGpc4L?9!CiGRGLXO^}54uuE_k-}UzzoSTvT1lA1O+2A&~ChtALNQkruoh^#^<w}w2dB%3Id{Ad; z1$NIMcR0X?!5(ZW&k9j1T#2#UsM_cUxExJ6!go8^0C3Fc?PBtNO^NacId|?g4|cc3 zwu1{g<;L>um>N!Lb#=I)8GO87(U`tDl3A=Uja~x2z5BDIX?co#HNZfi#A%T{@Io9k zRUxkUxVRydl93rjMKFkdBg=P}-<_OtK1vfui}bm=4%GW?mL8l?SE41`6h*5K0)(+! z-tCg|O*0k*0MAcBFueXjQaqe(iqj_dTvWrQQ!3BT9J^3(Dz47$h5*jRe*-^TyAtp2 zyDlHx!c!35OrNAgB^S`43Vr!VU2_ zQKp1FWs4M3_|c&t7$1P)%J|TYv_#IdJc!tIAzOu4Sin~dXg*z`u3~y}@k|3N>4p2z zCH%%qSU1!TT&(BeD;x%crbbMh!safDYDh${O))Jy&eExUHEA$dTq?xS=wmoxM^tQD z-a&@*I!$>_2MLB)+wk5ZlQuZ)76C%R3#$e{aCEeqRqE};AipJv-#T*L+1)u_xFu~j zkA7*2+n{;xZ%!0!i=Pe(3~Y%z;ec8ux7@i~;-Q-=__{L(36sjm@{? z>GtQ=mY}^iPIgPy3gAa1{kEcaH5X)K&<%lfMG}XHg(+XO(jp4yrz_<>#lcxB(+SATp3&O2apKk}RJkSE z>p#3ER#`aB7horjQxaHz{z0L~$ODBhsTiPw0h225hzKJ2BQ*sDNMoLH374%uJ~^$; zf81oIGurxXpaAUn{{;s_)Q6g6vA2Go*ld=rhBegR)_nczTFrk9I4CfH0uusLwI{Vp z-)B{LsKa-T^8@Bw8f{NA<`;II-P}MNvM~5!jGKKFxFq$K^Kx=NvftEIWTPzwrJI)1 z01JraD$mU;6xa1$6`VExWYliLqNljifcoYqLTRYD{Cj{6qgTo^V#AS%2CHWQ6X(Z7 zV=(Fi10!B)*;kb)ii%N1i#pH7_KMDG@{>M)-c?XniBP1I`r2zaU1ReaRL4r+u(hh1 z+mhaMLda-``TcUyvhNw$p~u2OPks5Ifl}Xa{3Z__C#%$Cbj06f(CF#kfb-L+*8r{t!zbvAv2zIyiX($pEe-&p|9-70 zp$Yu+%`*S*oByx88hxS6V_<$%*1k)u@z9;{uN%Hbfv0t}oPjyz=vrLy^9Q>RbhrS* zamA7a}4uUAhN7(x8&x8wAW{`Ken|9jY*i&=-2xw{On?Tmn>v?fRu@H8oehzh7$nvqsU$ zK)ZBz=RQ6)MM+K$)7sjKr%6Ka&&MT%2V?xN(a~^#;0C|0tztn#TFD3 z7ncEMUW$r}U~BK`@AsQ9BOZ16kAI@c*HZODT6%ggJ`*uFht5qv?ooibXTbX-au$31 z&t!jnKyLGepVaj5gx>)B(bmpxd3P5CpTduij{}m9W6!$5TLe<+*C8KI|Gt)bq#iX@ z?YkP58oeUvBCX(RaPk6-U zk4Sj5QywQ$s03MhpZNDB6vO*6I;sR(BoMj@3qe6eMWP~JAmrC}<9M!a;f4j6Q~z2+ zj98$Y^Y`}$GkpnncRs)vp0D$$9QA(r$cNq}KtjA={Li}Df*l(j^_`p3eB$d3t|K79 z$5|=U{5aa>efBj8e|nqMv7YQFjMHXR$`;CbO=NaAIO2C?rHgO${G< zDa&V)nurqzLUev(w!BeVt-nixew+X-0Bs&N%?E94e2UX#gMPlPw)aX(Anq5oqoYF? zPzU97yjHAXFc)}hXNRC`iBRPCC)9_tKtY4kM^7OO3t~7dwzVJcXh5Gm^EkoDR-yp@ zlDQ7EN(#V2g~nAe>%+ss!X}kz7fMIwJn+?b8;z-&kNv|1fIe@G(x?)6t97QePcBTw zffeGyY*g5)zc9obYft%P|;83JETs>pHo~1g?9-%cp@SB8XJ;zGR3m ziW)Cm0dt`HZR4I2m<$Rt8q;6ve=47`UTP6G%r^d6TZ;g09zLfheOEXs@7Ad6t5ck& zo1GW8p9OVv3S5t>=Yqj4v>czF4(jTX_~P$R&dmH2+#qoJcr*yJFZ;Mjv@Xhz?Nhim z#)HSS?g<>Gf%MUD^xM5+N;q{U@*4~B3Sx^?};}C2p2reMIfG! z9T?CR(aONmgDsF^mQ!@PFpdrJj=Z4K-?mfLby9&JM|pjak~*?}QavExf1M$%sIczW z-TVVq_$xO9gC0!E(dCKtm7BP@c+K@jqRnRULDzR#jgjf$9!7k;r>!(C<{WU4va&Mk zo%bATw%p&fo8Tkwmi6WwE|bACd0=m+psE_kYP7E9=m1(AP+MWagU$j57_ZDPxP#F> z_zCTsV((tV9ozVg4cwSiZoiFr0BdaW*7p5bVUCY^VgV zaF7T<;4o0rWrY_GxU9-an@MY{)1qW{GbG6FQWc`X=Ajp4u6ow2?F`Ex@%-T`m0jCixD~l`v~y@SCx2pyJ2s!|M%~FAh6ih5^E!4sxN&Yr53i3(_{HMXRm`mvUye zNqaB`uQ>%Q3(eo=9II#BV@K!b;vyojW@csz8XB|hoMCDXLu$D(^NVf=+zMfZf)m1w zm%3mFLX4bdvZ7CU=0A@7^l0+euzrc|Y5KGGgZ0yQ0D@D<)!}!Fco|-+Iet=}V%%*?AyVA;zD$?rZozKMwCFVE1XuwF7 z;N_^?&!Q@fKuqG!q}mmT60m^2I^5I&#|JqF2k!DyK={iQ`rr_If2-ql&TeZbXK;0d zPensb_VShu`m6qI1~tbC7Acn$E-TVwZ&nW-OqrbHVN(6CDL<%>O$ zUFHNW;%~rwsMnfmxVF=)R9941Mr~|DKn_Oca(D$qiAKrales@5e)^q+5ia3pA!sRS zA$$7<>|n$J1D{0ol_)G5wq0*;T|M&od!iEU2uTPr=j{8#0om~fo9Q!c=VkHCtmKYw z{c%EBO6Q(QJA9>vHm|^>MqF6Eo%7MrkuEhkMO_cFuWmDG znHoKqS2D%FyJ%*>MFpY6U+mOle@&Qy`vGR>pu5SPFauxUY`zt&R39L zoxUxVigr|3uT<-k1HJL3@$OBPohNCawUP_EqB zGc+yggk7(;9bfGOdd*bGe)pj6ZWIg72M%5bjo9!N(QnOhPf=>oRT7C>EwmWSVHM^x zg}*dU$ITmlSn7S0YkeMla@?~nX(PWQWiAuxJF|0#DlZ)-h#Q94o%H|lbS2^s?FjC~EsYtJ5o!6?fZgTWYsv3&RRug^8t zd%ai8^W4i>e&=`2eQx#dv&Fi(HQnL2Vd^?2)5EL2{nT9jkM^T4%ZAx!_)|wK?D37k zgBb}dKSBZZd4fs#=ayp}i2BQ+oo*$2)0ubdG|nvk$Or*v-Mt*-!Rfo`hDjNa`tq*N zXlRhxFY^~Tsq}4&ZG=hk>e<`3Z}05&x?joi{G=_E-R#x)7`3d&ev zii%EaYHDK4YHeug>E9p_!a(n`ha7~y1PB7u3g95<%68T94NYmiwM}jBm?Y~uHtc@a z!sSi~1+G(@Jv0}v*cH8eH2LwZKUn05?wC4{AWpY5ZrF&ereEOnxq?XYN5fxqO**Hw z8R_hJ(_2e_JpQ46>ll?y;x5Yc5!I_v{?MVEZ%7)I*~R3EaC(Q4BKPFOL9%|?bMzT`|}Eh@*z(PNV#RTN^Szk|k4* zh4X-uVOD!ps!0m2%%Ev<85E-pZkc}mm9Pe*TvAxt=)?BaOXfp(w6cW-7jS`5s&O~J zAqN;=U%3KsBZ(BY9)Bp%Wo(Dr~P!A$37$e%P-CK`UMc&?6pyredqz^fEEJ@M* z5|^2`Fd!_9!4XNJ*RPa4J@10b-<8{I)F+gfo|u~hPS%O+Ehs2Rub`m7qJJgl%O-sg z)LAXMSB|i$u*8s}rD#AgSCXq3cljW?0~3vfk&I*?~OAC2*5Jfj!{;we;(GSATyi84rCGzD^7B*;lUcQK=^iDcyT^<2}VY7H*gU_Ai0kOI*Hs@ z3h}#kLGtP)Do}6@dz+YNjT&3svFbGdaXYA2J*9}BK*6p&rY&v!W#Y0QbpHLB2pv5m zdQhYDIYxEcwzPShKd|;!fQ@a&_tNynV^rW&k!rW<<*~dcd@rUMkk*9!+l7UN4~!?+ z0OM#qvtOSi;Vqh9tvRgHyP;c$4@zyJ-^8yM`Gh)7vyNEU46 zu!7CwXgUs3Cwy@o!8m-(9~pYT_mZ%MAh>n$%6!FWDF1W3yx5mET-Tr#ADnOToSN6M z22ahsS1w3P`vnv(}za$HeF*m)Lu+d#ZA96?XZ~gV0p9cI*wU6SjAp>o0vuNb1*KV^Kc56E!eVo<2@tT2 z&2=DZLP>IwS|=?6t%GVa5#*JVK9}{fo10Jf^`&>Z&ZSeGGp|}Uk$Zv^_2TZD`~ADj zpJ2DMvoi}0oVIpy2Y(>rTez{tH}5RQ-dW#d=2jS>=2`k)FygONfZO9$fg6CU>&I`Q zK=Ox{o_5KDjWLquzBtd+tp^W&@+?sTC2)EW9L=YbL4Y{f0U*YE_ox733=34JZFTx) zUh9SCA9ASp-PqUe@YDI+#fxtT2cOJiFuqBVZ$K29!@~!nR1QhE|8Va6=K*bFt0W>QA?2gcjc=fKmKuztx*69T~dO#Uu^rcG9jUp{t!`~P= z$jgI5ss*6sJF}sTFZ#rhzvth1f$kKSE>PGiKG5$N%n?YInO6wRQ(s^I*UrWqFc2$) z?9cehO#12|jxM#CwM&{i9=;dnU;4uiGE!3`KnxDF?l`#0(q$K5WBmRdi$YSwPBEhC z!@Q2_W43tuYLm!;mL2-uMBz@5lL=`GAmx;7(bI<+^uMI=Stsr@{o$OlvYtKuVew1o z)TvX2MMX*)8b3Z_-=AZ<`mA-O&P_4c{=lP(T~-@@nkq6f(!s^$0pLm~+^_OGpxuNc zk|3{E#w&2S!_Lkg71i<315kHbEaEyT2JY0cK5A{EmjSHQ&?^8u!BQz{YBJE}(gC+E z<-d6yv_ofNW)>~PPXH^jIB*8P?HMr|mdU86#5=NGmjPOiQi!N9W-yPpiML;wmA-m3 zXXQSVkS5BEcHO^pYWI|spC31`cKS(h_LTYxU{t!`VE*QvIaFHV>FNKYf;K3n}c56(}H`gE)v(ST|2R{u|d0J%kNBDGrU_`CkRL* z(q(=n)wlF&?{rS}|6?1W*5Ke(X6C@R(DUFxR}SbOKS)2w#_Z7c;Z_@v!PLltY&|u1 z$KHRWyfg@1Q3bl!F@S)4;OL0WqP_P)YZJSDH?;Nj56;efE!$c;Ajsh7Wq9kedjLWJ z15Vf04Jb;W2InqcmVXusGZo|&G#@{>7k6|ZK?li&eGcIruffhs`?e}X6>(7o$TqLL&C3@svN?n+kp5?5dZsq zh7tn-%J;?Cjal8E0`3@*|Rwh0WWFP~LQk1ZU3_HUNUZ$U@}O0R)t z(zW%ykBHDQG{n@bI?De@QVj5`&E%FhUVV{B_v#;~yc}|cmQPq%1;84*=dZvuGXQTk z+M32#ITh-^7gD@y>e6+4kW_T`|4Gm3F9?JR6e>?={mRzfzR~vMKv&sX=S!&| z@ya+|0s16G&E6sjz$<{CLT7tZ`N3~@lQn?9Q33V8z0_G%CU90sna@A~IQc@J-n#wa z3hdnZPRMUx0SwPz=KqiEm(?fg=)FCNo}nQqpBle`Gwjk#x|>UG<&97`eRbrH?+-Pb zChRo_XFMbi!-I)Bt<#D|bU-tv{)rp9%O7!9-*IocZbLS+y5dkJ~-?Ly|sQ zbG|T(ir_b!yJ~eyxctQP1bUnGH&l0DU}iwey{Srf=m2(Yq}WGZ{cteGWE!q=+q?EN zCT0eq`z1GlOLc${5Y(d5f;MbtYP$RA01ORp_Un#Zi3uyvv8+}9^61*D6zV!3BJ0z`j``!F6;ZL z&n{jqFY2I)Wl?a!^Ig=yZu8dc^qRWMq}{{J-7Uuh6*TF~wom_+zi&$0-SzML@q;g2 zOMokyBzB>-!so~-v(L_yWRBD?(i>h}%+HYXXI{48_QlVP5^+ez>g5L{G0l^~iwEAW zESl{y8oRUp8Crd#Tj+gw^iogWL==M2b^RCO-~hqZOa6Jdg1Hq>nUo0i)1psWnF{bg z1%#2HPYK@DIN@b|8u(ED@oCZLg#}x9X=#|y<(k^s>WYdS5coZZqIZ-1W<4$Ub5$xz z4jk9~qZIbl%z{bo!S`z}a?)pGPEa0w<)>)!hyD!t;y?w(vhGW$>h3S?HkPukf7=t;1&`s$GYQg zD#tzQ?d|0U7tU>3c%X)=1u8=Z++C&NOCV{V7AxL znTw*?yMc#~@o@tuX}Fxc4NV_v*BFnY_oFQbC80}GA$U@B5Vm)3za^lgg;1-IMQRRw zKl^8Qo^63WF12WatP~s3_w4U~73>|3b7^lcvhRJ+Qm4YrUah5; zqayd8dJ5$@|JRIv7B42e-MYbN)x_omqo#d-n)GHGol130P5#UJz&D=frU<{biqbUh z`aM=D*BfCx+HuEDO+17iS=N71#}trUmH)S?BFspHrJ)>G^u9+@eh~m6(SfRiL?RKK zC^;K0FDO4VmfUVOiv!EAxNmuA3MKhAYj;1;%-mUO?{|FQ^G9y!QSA{d@md9)i+;a1 z>V(W2#D<|pCML1C1{ehH>V1~+Q?Ga4(^gLp0H`-md(8u1bA%6rYl*eNE7w9$=K?9S zp`r(wsZUyIQg?hh#bsrSHF)n<43Ols-`@ZA1W~>6YW-f~45uQ&)AA=7?fEn+6G=B< z+em$jR^iW=!H*VEq=tE^;e0me0@_zdQ6xMAK6cX1a2* zdiK2)D?f5s-u);}@qoP7+Oqd0!_5dX*N4D1HXoSG)>uMZRo%bp+K->2Oz4?Zf2Ja^ zS�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 RMHB!4002ovPDHLkV1fnQ0~i1R literal 0 HcmV?d00001 diff --git a/docs/malcolm-hedgehog-e2e-iso-install.md b/docs/malcolm-hedgehog-e2e-iso-install.md index a697517ce..58852f66e 100644 --- a/docs/malcolm-hedgehog-e2e-iso-install.md +++ b/docs/malcolm-hedgehog-e2e-iso-install.md @@ -13,11 +13,13 @@ In contrast to using the ISO installer, Malcolm can also be installed "natively" * [Booting the Installation Media](#BootUSB) * [Malcolm Installation and Configuration](#MalcolmInstallAndConfig) - [ISO Installation](#ISOInstallMalcolm) - - [System](#MalcolmSystem) + - [Desktop Environment](#MalcolmDesktop) - [Configuration](#MalcolmConfig) - [Setting up Authentication](#MalcolmAuthSetup) * [Hedgehog Linux Installation and Configuration](#HedgehogInstallAndConfig) - [Hedgehog Linux ISO Installation](#ISOInstallHedgehog) + - [Configure Interfaces](#HedgehogInterfaces) + - [Configure Capture and Forwarding](#HedgehogCapture) ## Obtaining the Installation ISOs @@ -104,7 +106,7 @@ Following these prompts, the installer will reboot and the Malcolm base operatin The Malcolm installer does not require an internet connection to complete successfully. If the installer prompts you to configure network connectivity, you may choose "do not configure the network at this time." -### Malcolm System +### Malcolm Desktop Environment The Malcolm base operating system is a [hardened](hardening.md#Hardening) Linux installation based on the current [stable release](https://wiki.debian.org/DebianStable) of [Debian](https://www.debian.org/) [running](https://wiki.debian.org/Xfce) the [XFCE desktop environment](https://www.xfce.org/). It has been preloaded with all of the [components](components.md#Components) that make up Malcolm. @@ -250,6 +252,25 @@ The [configuration and tuning](malcolm-config.md#ConfigAndTuning) wizard's quest ### Setting up Authentication for Malcolm +Once the [configuration](#MalcolmConfig) questions have been completed as described above, you can click the circular yellow Malcolm icon the panel at the top of the [desktop](#MalcolmDesktop) to start Malcolm. As you have not yet configured authentication, you will be prompted to do so. This authentication setup can be run again later by running [`./scripts/auth_setup`](authsetup.md#AuthSetup) from the Malcolm installation directory. + +![Setting up authentication on Malcolm's first run](./images/screenshots/iso_install_auth_setup.png) + +*The Configure Authentication dialog* + +As this is the first time setting up authentication, ensure the **all** option is selected and press **OK**. + +You will be prompted to do the following: + +* Store administrator username/password for local Malcolm access: specifies the administrator credentials when using [local account management](#AuthBasicAccountManagement) (instead of LDAP) for authentication. +* (Re)generate self-signed certificates for HTTPS access: creates the self-signed [TLS certificates](#TLSCerts) used for encrypting the connections between users' web browsers and Malcolm +* (Re)generate self-signed certificates for a remote log forwarder: creates the self-signed [TLS certificates](#TLSCerts) for communications from a remote log forwarder (such as Hedgehog Linux or forwarders for other [third-Party logs](third-party-logs.md#ThirdPartyLogs)) +* Configure remote primary or secondary OpenSearch instance: **N** if you are using Malcolm's local OpenSearch instance, or **Y** to specify credentials for a remote OpenSearch cluster (see [OpenSearch instances](opensearch-instances.md#OpenSearchInstance)) +* Store username/password for email alert sender account: answer **Y** to specify credentials for [Email Sender Accounts](alerting.md#AlertingEmail) to be used with OpenSearch Dashboards' alerting plugin +* (Re)generate internal passwords for NetBox: if you answered **Y** to "Should Malcolm run and maintain an instance of NetBox...?" during the configuration questions, you should need to asnwer **Y** to this question at least the first time you start Malcolm +* Transfer self-signed client certificates to a remote log forwarder: in order for a Hedgehog Linux to securely communicate with Malcolm, it needs the client certificates generated when you answered **Y** to "(Re)generate self-signed certificates for a remote log forwarder" a few moments ago. Malcolm can facilitate the secure transfer of these to a sensor running Hedgehog. If you will be continuing on to configure a sensor running Hedgehog Linux, answer **Y** here. + - You're prompted to "Run configure-capture on the remote log forwarder, select 'Configure Forwarding,' then 'Receive client SSL files...'." Return here and press **Enter** when you've finished with [Configure Capture and Forwarding](#HedgehogCapture) below. + ## Hedgehog Linux Installation and Configuration ## Hedgehog Linux ISO Installation @@ -271,3 +292,6 @@ At the end of the installation process, you will be prompted with a few self-exp Following these prompts, the installer will reboot and Hedgehog Linux will boot. +## Configure Interfaces + +## Configure Capture and Forwarding \ No newline at end of file From 5dd79a5c3dfd4991016a40311f3330d243069d16 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 11 Apr 2023 13:27:32 -0600 Subject: [PATCH 120/239] documentation wip --- docs/hedgehog-config-user.md | 8 +- .../hedgehog/images/forwarder_config.png | Bin 32989 -> 35120 bytes .../hedgehog/images/pcap_compression.png | Bin 0 -> 18860 bytes .../hedgehog/images/ssl_client_receive.png | Bin 0 -> 34332 bytes .../images/ssl_client_receive_code.png | Bin 0 -> 22577 bytes .../hedgehog/images/ssl_client_transmit.png | Bin 0 -> 54739 bytes docs/malcolm-hedgehog-e2e-iso-install.md | 282 +++++++++++++++++- 7 files changed, 280 insertions(+), 10 deletions(-) create mode 100644 docs/images/hedgehog/images/pcap_compression.png create mode 100644 docs/images/hedgehog/images/ssl_client_receive.png create mode 100644 docs/images/hedgehog/images/ssl_client_receive_code.png create mode 100644 docs/images/hedgehog/images/ssl_client_transmit.png diff --git a/docs/hedgehog-config-user.md b/docs/hedgehog-config-user.md index 4ec5f02f5..5bbaea362 100644 --- a/docs/hedgehog-config-user.md +++ b/docs/hedgehog-config-user.md @@ -53,11 +53,11 @@ Select **Configure Forwarding** to set up forwarding logs and statistics from th ![Configure forwarders](./images/hedgehog/images/forwarder_config.png) -There are five forwarder services used on the sensor, each for forwarding a different type of log or sensor metric. +There are three forwarder services used on the sensor, each for forwarding a different type of log or sensor metric. -## capture: Arkime session forwarding +## arkime-capture: Arkime session forwarding -[capture](https://github.com/arkime/arkime/tree/master/capture) is not only used to capture PCAP files, but also the parse raw traffic into sessions and forward this session metadata to an [OpenSearch](https://opensearch.org/) database so that it can be viewed in [Arkime viewer](https://arkime.com/), whether standalone or as part of a [Malcolm]({{ site.github.repository_url }}) instance. If you're using Hedgehog Linux with Malcolm, please read [Correlating Zeek logs and Arkime sessions]({{ site.github.repository_url }}#ZeekArkimeFlowCorrelation) in the Malcolm documentation for more information. +arkime-[capture](https://github.com/arkime/arkime/tree/master/capture) is not only used to capture PCAP files, but also the parse raw traffic into sessions and forward this session metadata to an [OpenSearch](https://opensearch.org/) database so that it can be viewed in [Arkime viewer](https://arkime.com/), whether standalone or as part of a [Malcolm]({{ site.github.repository_url }}) instance. If you're using Hedgehog Linux with Malcolm, please read [Correlating Zeek logs and Arkime sessions]({{ site.github.repository_url }}#ZeekArkimeFlowCorrelation) in the Malcolm documentation for more information. First, select the OpenSearch connection transport protocol, either **HTTPS** or **HTTP**. If the metrics are being forwarded to Malcolm, select **HTTPS** to encrypt messages from the sensor to the aggregator using TLS v1.2 using ECDHE-RSA-AES128-GCM-SHA256. If **HTTPS** is chosen, you must choose whether to enable SSL certificate verification. If you are using a self-signed certificate (such as the one automatically created during [Malcolm's configuration]({{ site.github.repository_url }}#configure-authentication)), choose **None**. @@ -67,7 +67,7 @@ Next, enter the **OpenSearch host** IP address (ie., the IP address of the aggre ![OpenSearch host and port](./images/hedgehog/images/arkime-capture-ip-port.png) -You will be asked to enter authentication credentials for the sensor's connections to the aggregator's OpenSearch API. After you've entered the username and the password, the sensor will attempt a test connection to OpenSearch using the connection information provided. +You will be asked to enter authentication credentials for the sensor's connections to the aggregator's OpenSearch API. After you've entered the username and the password, the sensor will attempt a test connection to OpenSearch using the connection information provided. If the Malcolm services have not yet been started, you may receive a **Connection refused** error. You may select **Ignore Error** for the credentials to be accepted anyway. ![OpenSearch username](./images/hedgehog/images/opensearch_username.png) ![OpenSearch password](./images/hedgehog/images/opensearch_password.png) ![Successful OpenSearch connection](./images/hedgehog/images/opensearch_connection_success.png) diff --git a/docs/images/hedgehog/images/forwarder_config.png b/docs/images/hedgehog/images/forwarder_config.png index 4a4d0f2e3e8895d8b4deb0cd11cf50c431f6da2b..b20139db3a8e78f7bdc97e16d560abaa5f723e2a 100644 GIT binary patch literal 35120 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

7#gc9XARO?Q`Wc&39{p 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.0.0.1:8022/files` -* [Host and Subnet Name Mapping](host-and-subnet-mapping.md#HostAndSubnetNaming) Editor: [https://localhost/name-map-ui/](https://localhost/name-map-ui/) * [NetBox](asset-interaction-analysis.md#AssetInteractionAnalysis): [https://localhost/netbox/](https://localhost/netbox/) * [Account Management](authsetup.md#AuthBasicAccountManagement): [https://localhost:488](https://localhost:488) \ No newline at end of file diff --git a/docs/severity.md b/docs/severity.md index c48951d78..45bd31d03 100644 --- a/docs/severity.md +++ b/docs/severity.md @@ -5,7 +5,7 @@ As Zeek logs are parsed and enriched prior to indexing, a severity score up to `100` (a higher score indicating a more severe event) can be assigned when one or more of the following conditions are met: -* cross-segment network traffic (if [network subnets were defined](host-and-subnet-mapping.md#HostAndSubnetNaming)) +* cross-segment network traffic (if [network subnets were defined](asset-interaction-analysis.md#AssetInteractionAnalysis)) * connection origination and destination (e.g., inbound, outbound, external, internal) * traffic to or from sensitive countries - The comma-separated list of countries (by [ISO 3166-1 alpha-2 code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Current_codes)) can be customized by setting the `SENSITIVE_COUNTRY_CODES` environment variable in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml). @@ -42,6 +42,6 @@ These categories' severity scores can be customized by editing `logstash/maps/ma "PROTOCOL_SSH": 40 ``` -Restart Logstash after modifying `malcolm_severity.yaml` for the changes to take effect. The [hostname and CIDR subnet names interface](host-and-subnet-mapping.md#NameMapUI) provides a convenient button for restarting Logstash. +Restart Logstash after modifying `malcolm_severity.yaml` for the changes to take effect. Severity scoring can be disabled globally by setting the `LOGSTASH_SEVERITY_SCORING` environment variable to `false` in the [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) file and [restarting Malcolm](running.md#StopAndRestart). \ No newline at end of file diff --git a/docs/ubuntu-install-example.md b/docs/ubuntu-install-example.md index d26daba88..bc528d210 100644 --- a/docs/ubuntu-install-example.md +++ b/docs/ubuntu-install-example.md @@ -247,7 +247,6 @@ Pulling filebeat ... done Pulling freq ... done Pulling htadmin ... done Pulling logstash ... done -Pulling name-map-ui ... done Pulling netbox ... done Pulling netbox-postgresql ... done Pulling netbox-redis ... done @@ -271,7 +270,6 @@ ghcr.io/idaholab/malcolm/filebeat-oss kubern ghcr.io/idaholab/malcolm/freq kubernetes xxxxxxxxxxxx 3 days ago 132MB ghcr.io/idaholab/malcolm/htadmin kubernetes xxxxxxxxxxxx 3 days ago 242MB ghcr.io/idaholab/malcolm/logstash-oss kubernetes xxxxxxxxxxxx 3 days ago 1.35GB -ghcr.io/idaholab/malcolm/name-map-ui kubernetes xxxxxxxxxxxx 3 days ago 143MB ghcr.io/idaholab/malcolm/netbox kubernetes xxxxxxxxxxxx 3 days ago 1.01GB ghcr.io/idaholab/malcolm/nginx-proxy kubernetes xxxxxxxxxxxx 3 days ago 121MB ghcr.io/idaholab/malcolm/opensearch kubernetes xxxxxxxxxxxx 3 days ago 1.17GB @@ -292,7 +290,6 @@ In a few minutes, Malcolm services will be accessible via the following URLs: - OpenSearch Dashboards: https://localhost/dashboards/ - PCAP upload (web): https://localhost/upload/ - PCAP upload (sftp): sftp://username@127.0.0.1:8022/files/ - - Host and subnet name mapping editor: https://localhost/name-map-ui/ - NetBox: https://localhost/netbox/ - Account management: https://localhost:488/ - Documentation: https://localhost/readme/ @@ -307,7 +304,6 @@ malcolm-filebeat-1 "/usr/local/bin/dock…" filebeat r malcolm-freq-1 "/usr/local/bin/dock…" freq running (starting) … malcolm-htadmin-1 "/usr/local/bin/dock…" htadmin running (starting) … malcolm-logstash-1 "/usr/local/bin/dock…" logstash running (starting) … -malcolm-name-map-ui-1 "/usr/local/bin/dock…" name-map-ui running (starting) … malcolm-netbox-1 "/usr/bin/tini -- /u…" netbox running (starting) … malcolm-netbox-postgres-1 "/usr/bin/docker-uid…" netbox-postgres running (starting) … malcolm-netbox-redis-1 "/sbin/tini -- /usr/…" netbox-redis running (starting) … diff --git a/kubernetes/14-name-map-ui.yml b/kubernetes/14-name-map-ui.yml deleted file mode 100644 index 216745e1f..000000000 --- a/kubernetes/14-name-map-ui.yml +++ /dev/null @@ -1,47 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: name-map-ui - namespace: malcolm -spec: - ports: - - port: 9200 - protocol: TCP - selector: - name: name-map-ui-deployment - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: name-map-ui-deployment - namespace: malcolm -spec: - selector: - matchLabels: - name: name-map-ui-deployment - replicas: 1 - template: - metadata: - labels: - name: name-map-ui-deployment - spec: - containers: - - name: name-map-ui-container - image: ghcr.io/idaholab/malcolm/name-map-ui:kubernetes - imagePullPolicy: Always - stdin: false - tty: true - ports: - - containerPort: 9200 - envFrom: - - configMapRef: - name: process-env - - configMapRef: - name: ssl-env - env: - - name: NAME_MAP_UI_DISABLED - value: "true" - - name: VIRTUAL_HOST - value: "name-map-ui.malcolm.local" diff --git a/kubernetes/logstash.env b/kubernetes/logstash.env index 5eff3eabe..daddf0249 100644 --- a/kubernetes/logstash.env +++ b/kubernetes/logstash.env @@ -9,7 +9,5 @@ LOGSTASH_OUI_LOOKUP=true LOGSTASH_SEVERITY_SCORING=true # Whether or not Logstash will perform a reverse DNS lookup for external IP addresses LOGSTASH_REVERSE_DNS=false -# Whether or not Logstash will enrich network traffic metadata directly from net-map.json -LOGSTASH_NETWORK_MAP_ENRICHMENT=true # Whether or not Logstash will enrich network traffic metadata via NetBox API calls LOGSTASH_NETBOX_ENRICHMENT=false \ No newline at end of file diff --git a/kubernetes/services-dev-plan.md b/kubernetes/services-dev-plan.md index 47b82f108..e469c91b3 100644 --- a/kubernetes/services-dev-plan.md +++ b/kubernetes/services-dev-plan.md @@ -20,7 +20,6 @@ See **support Malcolm deployment with Kubernetes** [idaholab/Malcolm#149](https: * freq - This one really doesn't have many dependencies on other services, so it could be done whenever. * logstash -* name-map-ui * netbox-redis * netbox-redis-cache * netbox-postgres diff --git a/logstash/scripts/ip-to-segment-logstash.py b/logstash/scripts/ip-to-segment-logstash.py deleted file mode 100755 index bbeff56f0..000000000 --- a/logstash/scripts/ip-to-segment-logstash.py +++ /dev/null @@ -1,305 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. - -import sys -import os -import re -import argparse -import struct -import ipaddress -import itertools -import json -import pprint -import uuid -from collections import defaultdict - -UNSPECIFIED_TAG = '<~<~~>~>' -DEVICE_LIST_IDX = 0 -SEGMENT_LIST_IDX = 1 - -JSON_MAP_TYPE_SEGMENT = 'segment' -JSON_MAP_TYPE_DEVICE = 'host' -JSON_MAP_KEY_ADDR = 'address' -JSON_MAP_KEY_NAME = 'name' -JSON_MAP_KEY_TAG = 'tag' -JSON_MAP_KEY_TYPE = 'type' - -################################################################################################### -# print to stderr -def eprint(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - - -################################################################################################### -# convenient boolean argument parsing -def str2bool(v): - if v.lower() in ('yes', 'true', 't', 'y', '1'): - return True - elif v.lower() in ('no', 'false', 'f', 'n', '0'): - return False - else: - raise argparse.ArgumentTypeError('Boolean value expected.') - - -################################################################################################### -# main -def main(): - - # extract arguments from the command line - # print (sys.argv[1:]); - parser = argparse.ArgumentParser( - description='Logstash IP address to Segment Filter Creator', - add_help=False, - usage='ip-to-segment-logstash.py ', - ) - parser.add_argument( - '-i', - '--input', - dest='jsonInput', - metavar='', - type=str, - nargs='*', - default='', - help='JSON network mapping file(s)', - ) - parser.add_argument( - '-o', - '--output', - dest='output', - metavar='', - type=str, - default='-', - help='Output file', - ) - parser.add_argument( - '-t', - '--tags', - dest='tagChecking', - type=str2bool, - nargs='?', - const=True, - default=False, - help=f"Generate check for required tags based on {JSON_MAP_KEY_TAG} attribute", - ) - try: - parser.error = parser.exit - args = parser.parse_args() - except SystemExit: - parser.print_help() - exit(2) - - # read each input file into its own list - mixedEntries = [] - - for inFile in args.jsonInput: - try: - tmpMixedEntries = json.load(open(inFile, 'r')) - if isinstance(tmpMixedEntries, list): - mixedEntries.extend(tmpMixedEntries) - except: - pass - - if len(mixedEntries) > 0: - - filterId = 0 - addedFields = set() - - outFile = open(args.output, 'w+') if (args.output and args.output != '-') else sys.stdout - try: - print('filter {', file=outFile) - print("", file=outFile) - print(" # this file was automatically generated by {}".format(os.path.basename(__file__)), file=outFile) - print("", file=outFile) - - # process segment mappings into a dictionary of two dictionaries of lists (one for devices, one for segments) - # eg., tagListMap[required tag name][DEVICE_LIST_IDX|SEGMENT_LIST_IDX][network segment name] = [172.16.0.0/12, 192.168.0.0/24, 10.0.0.41] - tagListMap = defaultdict(lambda: [defaultdict(list), defaultdict(list)]) - - # handle mixed entries from the JSON-formatted file - for entry in mixedEntries: - - # the entry must at least contain type, address, name; may optionally contain tag - if ( - isinstance(entry, dict) - and all(key in entry for key in (JSON_MAP_KEY_TYPE, JSON_MAP_KEY_NAME, JSON_MAP_KEY_ADDR)) - and entry[JSON_MAP_KEY_TYPE] in (JSON_MAP_TYPE_SEGMENT, JSON_MAP_TYPE_DEVICE) - and (len(entry[JSON_MAP_KEY_NAME]) > 0) - and (len(entry[JSON_MAP_KEY_ADDR]) > 0) - ): - - addressList = [] - networkList = [] - - tagReq = ( - entry[JSON_MAP_KEY_TAG] - if args.tagChecking and (JSON_MAP_KEY_TAG in entry) and (len(entry[JSON_MAP_KEY_TAG]) > 0) - else UNSPECIFIED_TAG - ) - - # account for comma-separated multiple addresses per 'address' value - for addr in ''.join(entry[JSON_MAP_KEY_ADDR].split()).split(','): - - if entry[JSON_MAP_KEY_TYPE] == JSON_MAP_TYPE_SEGMENT: - # potentially interpret address as a CIDR-formatted subnet - try: - networkList.append( - str(ipaddress.ip_network(addr)).lower() - if ('/' in addr) - else str(ipaddress.ip_address(addr)).lower() - ) - except ValueError: - eprint('"{}" is not a valid IP address, ignoring'.format(addr)) - - else: - # should be an IP or MAC address - try: - # see if it's an IP address - addressList.append(str(ipaddress.ip_address(addr)).lower()) - except ValueError: - # see if it's a MAC address - if re.match(macAddrRegex, addr): - # prepend _ temporarily to distinguish a mac address - addressList.append("_{}".format(addr.replace('-', ':').lower())) - else: - eprint('"{}" is not a valid IP or MAC address, ignoring'.format(ip)) - - if len(networkList) > 0: - tagListMap[tagReq][SEGMENT_LIST_IDX][entry[JSON_MAP_KEY_NAME]].extend(networkList) - - if len(addressList) > 0: - tagListMap[tagReq][DEVICE_LIST_IDX][entry[JSON_MAP_KEY_NAME]].extend(addressList) - - # go through the lists of segments/devices, which will now be organized by required tag first, then - # segment/device name, then the list of addresses - for tag, nameMaps in tagListMap.items(): - print("", file=outFile) - - # if a tag name is specified, print the IF statement verifying the tag's presence - if tag != UNSPECIFIED_TAG: - print(' if ("{}" in [tags]) {{'.format(tag), file=outFile) - try: - - # for the device(s) to be checked, create two filters, one for source IP|MAC and one for dest IP|MAC - for device, addrList in nameMaps[DEVICE_LIST_IDX].items(): - - # ip addresses mapped to device - ipList = list(set([a for a in addrList if not a.startswith('_')])) - if len(ipList) >= 1: - for source in ['source', 'destination']: - filterId += 1 - newFieldName = "".join([f"[{x}]" for x in [source, "device", "name"]]) - print("", file=outFile) - print( - ' if ([{}][ip]) and ({}) {{ '.format( - source, ' or '.join(['([{}][ip] == "{}")'.format(source, ip) for ip in ipList]) - ), - file=outFile, - ) - print( - ' mutate {{ id => "mutate_add_autogen_{}_ip_device_{}"'.format( - source, filterId - ), - file=outFile, - ) - print( - ' add_field => {{ "{}" => "{}" }}'.format(newFieldName, device), - file=outFile, - ) - print(" }", file=outFile) - print(" }", file=outFile) - addedFields.add(newFieldName) - - # mac addresses mapped to device - macList = list(set([a for a in addrList if a.startswith('_')])) - if len(macList) >= 1: - for source in ['source', 'destination']: - filterId += 1 - newFieldName = "".join([f"[{x}]" for x in [source, "device", "name"]]) - print("", file=outFile) - print( - ' if ([{}][mac]) and ({}) {{ '.format( - source, - ' or '.join(['([{}][mac] == "{}")'.format(source, mac[1:]) for mac in macList]), - ), - file=outFile, - ) - print( - ' mutate {{ id => "mutate_add_autogen_{}_mac_device_{}"'.format( - source, filterId - ), - file=outFile, - ) - print( - ' add_field => {{ "{}" => "{}" }}'.format(newFieldName, device), - file=outFile, - ) - print(" }", file=outFile) - print(" }", file=outFile) - addedFields.add(newFieldName) - - # for the segment(s) to be checked, create two cidr filters, one for source IP and one for dest IP - for segmentName, ipList in nameMaps[SEGMENT_LIST_IDX].items(): - ipList = list(set(ipList)) - for source in ['source', 'destination']: - filterId += 1 - # ip addresses/ranges mapped to network segment names - newFieldName = "".join([f"[{x}]" for x in [source, "segment", "name"]]) - print("", file=outFile) - print(" if ([{}][ip]) {{ cidr {{".format(source), file=outFile) - print(' id => "cidr_autogen_{}_segment_{}"'.format(source, filterId), file=outFile) - print(' address => [ "%{{[{}][ip]}}" ]'.format(source), file=outFile) - print( - ' network => [ {} ]'.format(', '.join('"{}"'.format(ip) for ip in ipList)), - file=outFile, - ) - print( - ' add_field => {{ "{}" => "{}" }}'.format(newFieldName, segmentName), file=outFile - ) - print(" } }", file=outFile) - addedFields.add("{}".format(newFieldName)) - - finally: - # if a tag name is specified, close the IF statement verifying the tag's presence - if tag != UNSPECIFIED_TAG: - print("", file=outFile) - print(' }} # end (if "{}" in [tags])'.format(tag), file=outFile) - - finally: - # deduplicate any added fields - if addedFields: - print("", file=outFile) - print(' # deduplicate any added fields', file=outFile) - for field in list(itertools.product(['source', 'destination'], ['device', 'segment'], ['name'])): - newFieldName = "".join([f"[{x}]" for x in [field[0], field[1], field[2]]]) - if newFieldName in addedFields: - print("", file=outFile) - print(' if ({}) {{ '.format(newFieldName), file=outFile) - print( - ' ruby {{ id => "ruby{}deduplicate"'.format( - ''.join(c for c, _ in itertools.groupby(re.sub('[^0-9a-zA-Z]+', '_', newFieldName))) - ), - file=outFile, - ) - print(' code => "', file=outFile) - print(" fieldVals = event.get('{}')".format(newFieldName), file=outFile) - print( - " if fieldVals.kind_of?(Array) then event.set('{}', fieldVals.uniq) end".format( - newFieldName - ), - file=outFile, - ) - print(' "', file=outFile) - print(' } }', file=outFile) - - # close out filter with ending } - print("", file=outFile) - print('} # end Filter', file=outFile) - - if outFile is not sys.stdout: - outFile.close() - - -if __name__ == '__main__': - main() diff --git a/logstash/scripts/logstash-start.sh b/logstash/scripts/logstash-start.sh index bee60878c..6d91c3240 100755 --- a/logstash/scripts/logstash-start.sh +++ b/logstash/scripts/logstash-start.sh @@ -17,9 +17,6 @@ export PIPELINES_CFG="/usr/share/logstash/config/pipelines.yml" # pipeline section in pipelines.yml (then delete 00_config.conf before starting) export PIPELINE_EXTRA_CONF_FILE="00_config.conf" -# files defining IP->host and MAC->host mapping -INPUT_MIXED_MAP="/usr/share/logstash/config/net-map.json" - # the name of the enrichment pipeline subdirectory under $PIPELINES_DIR ENRICHMENT_PIPELINE=${LOGSTASH_ENRICHMENT_PIPELINE:-"enrichment"} @@ -31,10 +28,6 @@ export OPENSEARCH_PIPELINE_ADDRESS_INTERNAL=${LOGSTASH_OPENSEARCH_PIPELINE_ADDRE export OPENSEARCH_PIPELINE_ADDRESS_EXTERNAL=${LOGSTASH_OPENSEARCH_PIPELINE_ADDRESS_EXTERNAL:-"external-os"} OPENSEARCH_OUTPUT_PIPELINE_ADDRESSES=${LOGSTASH_OPENSEARCH_OUTPUT_PIPELINE_ADDRESSES:-"$OPENSEARCH_PIPELINE_ADDRESS_INTERNAL,$OPENSEARCH_PIPELINE_ADDRESS_EXTERNAL"} -# ip-to-segment-logstash.py translates $INPUT_MIXED_MAP into this logstash filter file -NETWORK_MAP_OUTPUT_FILTER="$PIPELINES_DIR"/"$ENRICHMENT_PIPELINE"/16_host_segment_filters.conf -export NETWORK_MAP_ENRICHMENT=${LOGSTASH_NETWORK_MAP_ENRICHMENT:-"true"} - # output plugin configuration for primary and secondary opensearch destinations OPENSEARCH_LOCAL=${OPENSEARCH_LOCAL:-"true"} OPENSEARCH_SECONDARY=${OPENSEARCH_SECONDARY:-"false"} @@ -76,10 +69,6 @@ find "$PIPELINES_DIR" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null | sort fi ' -# create filters for network segment and host mapping in the enrichment directory -rm -f "$NETWORK_MAP_OUTPUT_FILTER" -[[ "$NETWORK_MAP_ENRICHMENT" == "true" ]] && /usr/local/bin/ip-to-segment-logstash.py --input "$INPUT_MIXED_MAP" -o "$NETWORK_MAP_OUTPUT_FILTER" - if [[ -z "$OPENSEARCH_SECONDARY_URL" ]]; then # external ES host destination is not specified, remove external destination from enrichment pipeline output OPENSEARCH_OUTPUT_PIPELINE_ADDRESSES="$(echo "$OPENSEARCH_OUTPUT_PIPELINE_ADDRESSES" | sed "s/,[[:blank:]]*$OPENSEARCH_PIPELINE_ADDRESS_EXTERNAL//")" diff --git a/malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/panel/launcher-19/16343117417.desktop b/malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/panel/launcher-19/16343117417.desktop deleted file mode 100644 index 95e27ad26..000000000 --- a/malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/panel/launcher-19/16343117417.desktop +++ /dev/null @@ -1,12 +0,0 @@ -[Desktop Entry] -Version=1.0 -Name=Malcolm - Host and Subnet Name Mapping -Exec=/opt/firefox/firefox https://localhost/name-map-ui/ -Terminal=false -X-MultipleArgs=false -Type=Application -Icon=server -Categories=Network; -StartupWMClass=Firefox -StartupNotify=true -X-XFCE-Source=file:///usr/share/applications/malcolm-mapping.desktop diff --git a/malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml b/malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml index 9d0f3e6b6..a9613f2cf 100644 --- a/malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml +++ b/malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml @@ -20,7 +20,6 @@ - @@ -97,11 +96,6 @@ - - - - - diff --git a/malcolm-iso/config/includes.chroot/usr/share/applications/malcolm-mapping.desktop b/malcolm-iso/config/includes.chroot/usr/share/applications/malcolm-mapping.desktop deleted file mode 100644 index 1ec8d8648..000000000 --- a/malcolm-iso/config/includes.chroot/usr/share/applications/malcolm-mapping.desktop +++ /dev/null @@ -1,11 +0,0 @@ -[Desktop Entry] -Version=1.0 -Name=Malcolm - Host and Subnet Name Mapping -Exec=/opt/firefox/firefox https://localhost/name-map-ui/ -Terminal=false -X-MultipleArgs=false -Type=Application -Icon=server.png -Categories=Network; -StartupWMClass=Firefox -StartupNotify=true diff --git a/name-map-ui/config/fpm-pool.conf b/name-map-ui/config/fpm-pool.conf deleted file mode 100644 index a3a6b288f..000000000 --- a/name-map-ui/config/fpm-pool.conf +++ /dev/null @@ -1,56 +0,0 @@ -[global] -; Log to /dev/fd/1 for docker -error_log = /dev/fd/1 - -[www] -; The address on which to accept FastCGI requests. -; Valid syntaxes are: -; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on -; a specific port; -; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on -; a specific port; -; 'port' - to listen on a TCP socket to all addresses -; (IPv6 and IPv4-mapped) on a specific port; -; '/path/to/unix/socket' - to listen on a unix socket. -; Note: This value is mandatory. -listen = 127.0.0.1:9000 - -; Enable status page -pm.status_path = /fpm-status - -; Ondemand process manager -pm = ondemand - -; The number of child processes to be created when pm is set to 'static' and the -; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. -; This value sets the limit on the number of simultaneous requests that will be -; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. -; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP -; CGI. The below defaults are based on a server without much resources. Don't -; forget to tweak pm.* to fit your needs. -; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' -; Note: This value is mandatory. -pm.max_children = 100 - -; The number of seconds after which an idle process will be killed. -; Note: Used only when pm is set to 'ondemand' -; Default Value: 10s -pm.process_idle_timeout = 10s; - -; The number of requests each child process should execute before respawning. -; This can be useful to work around memory leaks in 3rd party libraries. For -; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. -; Default Value: 0 -pm.max_requests = 1000 - -; Make sure the FPM workers can reach the environment variables for configuration -clear_env = no - -; Catch output from PHP -catch_workers_output = yes - -; Remove the 'child 10 said into stderr' prefix in the log and only show the actual message -decorate_workers_output = no - -; Enable ping page to use in healthcheck -ping.path = /fpm-ping diff --git a/name-map-ui/config/nginx.conf b/name-map-ui/config/nginx.conf deleted file mode 100644 index b93d077a4..000000000 --- a/name-map-ui/config/nginx.conf +++ /dev/null @@ -1,92 +0,0 @@ -worker_processes 1; -error_log stderr warn; -pid /tmp/nginx.pid; - -events { - worker_connections 1024; -} - -http { - include mime.types; - default_type application/octet-stream; - - # Define custom log format to include reponse times - log_format main_timed '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for" ' - '$request_time $upstream_response_time $pipe $upstream_cache_status'; - - access_log /var/log/nginx/access.log main_timed; - error_log /var/log/nginx/error.log notice; - - keepalive_timeout 65; - - # Write temporary files to /tmp so they can be created as a non-privileged user - client_body_temp_path /tmp/client_temp; - proxy_temp_path /tmp/proxy_temp_path; - fastcgi_temp_path /tmp/fastcgi_temp; - uwsgi_temp_path /tmp/uwsgi_temp; - scgi_temp_path /tmp/scgi_temp; - - # Default server definition - server { - listen 8080 default_server; - server_name _; - - sendfile off; - - root /var/www/html; - index index.php index.html; - - location / { - # First attempt to serve request as file, then - # as directory, then fall back to index.php - try_files $uri $uri/ /index.php?q=$uri&$args; - } - - location ~* maps/.+\.(txt|json)$ { - expires -1; - add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; - } - - # Redirect server error pages to the static page /50x.html - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /var/lib/nginx/html; - } - - # Pass the PHP scripts to PHP-FPM listening on 127.0.0.1:9000 - location ~ \.php$ { - try_files $uri =404; - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass 127.0.0.1:9000; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param SCRIPT_NAME $fastcgi_script_name; - fastcgi_index index.php; - include fastcgi_params; - } - - location ~* \.(jpg|jpeg|gif|png|css|js|ico|xml)$ { - expires 5d; - } - - # Deny access to . files, for security - location ~ /\. { - log_not_found off; - deny all; - } - - # Allow fpm ping and status from localhost - location ~ ^/(fpm-status|fpm-ping)$ { - access_log off; - allow 127.0.0.1; - deny all; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - include fastcgi_params; - fastcgi_pass 127.0.0.1:9000; - } - } - - # Include other server configs - include /etc/nginx/conf.d/*.conf; -} diff --git a/name-map-ui/config/php.ini b/name-map-ui/config/php.ini deleted file mode 100644 index d91b95891..000000000 --- a/name-map-ui/config/php.ini +++ /dev/null @@ -1,7 +0,0 @@ -[PHP] -file_uploads = On -upload_max_filesize = 64M -max_file_uploads = 4 - -[Date] -date.timezone="UTC" diff --git a/name-map-ui/config/supervisor_logstash_ctl.conf b/name-map-ui/config/supervisor_logstash_ctl.conf deleted file mode 100644 index 700b4cfae..000000000 --- a/name-map-ui/config/supervisor_logstash_ctl.conf +++ /dev/null @@ -1,10 +0,0 @@ -; Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. - -[supervisord] -nodaemon=true -logfile=/dev/null -logfile_maxbytes=0 -pidfile=/tmp/supervisord-logstash.pid - -[supervisorctl] -serverurl=http://logstash:9001 diff --git a/name-map-ui/config/supervisor_netbox_ctl.conf b/name-map-ui/config/supervisor_netbox_ctl.conf deleted file mode 100644 index e354667cb..000000000 --- a/name-map-ui/config/supervisor_netbox_ctl.conf +++ /dev/null @@ -1,10 +0,0 @@ -; Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. - -[supervisord] -nodaemon=true -logfile=/dev/null -logfile_maxbytes=0 -pidfile=/tmp/supervisord-netbox.pid - -[supervisorctl] -serverurl=http://netbox:9001 diff --git a/name-map-ui/config/supervisord.conf b/name-map-ui/config/supervisord.conf deleted file mode 100644 index 00da1bfa9..000000000 --- a/name-map-ui/config/supervisord.conf +++ /dev/null @@ -1,72 +0,0 @@ -; Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. - -[unix_http_server] -file=/tmp/supervisor-main.sock ; (the path to the socket file) -chmod=0700 - -[supervisord] -nodaemon=true -logfile=/dev/null -logfile_maxbytes=0 -pidfile=/tmp/supervisor-main.pid - -[rpcinterface:supervisor] -supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface - -[supervisorctl] -serverurl=unix:///tmp/supervisor-main.sock - -[program:php-fpm] -command=/usr/sbin/php-fpm81 -F -stopasgroup=true -killasgroup=true -stdout_logfile=/dev/fd/1 -stdout_logfile_maxbytes=0 -redirect_stderr=true -autorestart=false -startretries=0 - -[program:nginx] -command=/usr/sbin/nginx -g 'daemon off;' -stopasgroup=true -killasgroup=true -stdout_logfile=/dev/fd/1 -stdout_logfile_maxbytes=0 -redirect_stderr=true -autorestart=false -startretries=0 - -[program:logaccess] -command=/usr/bin/tail -F /var/log/nginx/access.log -startsecs=10 -startretries=2000000000 -stopasgroup=true -killasgroup=true -stdout_logfile=/dev/fd/1 -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/null -stdout_logfile_maxbytes=0 -redirect_stderr=false - -[program:logerrors] -command=/usr/bin/tail -F /var/log/nginx/error.log -startsecs=10 -startretries=2000000000 -stopasgroup=true -killasgroup=true -stdout_logfile=/dev/fd/1 -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/null -stdout_logfile_maxbytes=0 -redirect_stderr=false - -[program:watch-upload] -command=/bin/bash -c "sleep 15 && /usr/local/bin/name-map-save-watch.sh" -startsecs=20 -startretries=1 -stopasgroup=true -killasgroup=true -directory=/var/www/html/upload -stdout_logfile=/dev/fd/1 -stdout_logfile_maxbytes=0 -redirect_stderr=true diff --git a/name-map-ui/scripts/name-map-save-watch.sh b/name-map-ui/scripts/name-map-save-watch.sh deleted file mode 100755 index 254a47c91..000000000 --- a/name-map-ui/scripts/name-map-save-watch.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. - -PROCESS_DIR=${NAME_MAP_SAVE_DIR:-/var/www/html/upload/} -DEST_MAP=${NAME_MAP_JSON:-/var/www/html/maps/net-map.json} - -inotifywait -m -e close_write --format '%w%f' "${PROCESS_DIR}" | while read NEWFILE -do - sleep 0.1 - # verify that the file is json|txt and that it is valid JSON - FILEMIME=$(file -b --mime-type "$NEWFILE") - if ( echo "$FILEMIME" | grep -q -e "\(application/json\|text/plain\)" ) && ( python3 -mjson.tool "$NEWFILE" >/dev/null 2>&1 ); then - # move the new net-map.json file into its final location - # (done like this with "tee" since we may not be able to cp overwrite a volume-mounted file) - (>/dev/null tee "$DEST_MAP" < "$NEWFILE") && echo "\"$NEWFILE\" -> \"$DEST_MAP\"" - rm -f "$NEWFILE" - else - # invalid or unhandled file type uploaded, delete it - (>&2 rm -f "$NEWFILE") && echo "Removed \"$NEWFILE\" (\"$FILEMIME\"): invalid file type or format" - fi -done diff --git a/name-map-ui/site/index.html b/name-map-ui/site/index.html deleted file mode 100644 index 4152e343b..000000000 --- a/name-map-ui/site/index.html +++ /dev/null @@ -1,466 +0,0 @@ - - - - - - - - - - - - - - - - Host and Network Segment Name Mapping - - - - -