From 10b1cabc28a7762b94bed22f05bad754c11dd7e9 Mon Sep 17 00:00:00 2001 From: Jalen Hatcher Date: Fri, 10 Apr 2026 03:14:53 -0400 Subject: [PATCH 01/24] cron job for ETL is working, no duplicate jobs --- scripts/cron_creation.sh | 72 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100755 scripts/cron_creation.sh diff --git a/scripts/cron_creation.sh b/scripts/cron_creation.sh new file mode 100755 index 0000000..af724d3 --- /dev/null +++ b/scripts/cron_creation.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# Run this from the root of the repo + +# Exit on errors, prevent undefined variables, and fail if any command in a pipeline fails +set -euo pipefail + +# Move into repo root +cd "$(dirname "$0")/.." + +# install dependencies +echo "Installing dependencies..." +# pip install python-dotenv +# pip install python-crontab + +# for reading time fields +declare update_interval +declare cron_command +declare cron_expression + +extract_email_time_field() +{ + echo "$(grep )" + #local time="$(grep -e $1 config.ini | grep -oE '[0-9]+|:' | tr -d '\n')" + #IFS=':' read -r hours mins <<< "$time" +} + +extract_etl_time_field() +{ + update_interval="$(grep $1 config.ini | cut -d '=' -f2)" +} + +write_cron_job() +{ + cat <(fgrep -i -v "$cron_command" <(crontab -l)) <(echo "$cron_expression") | crontab - +} + +fail() { + echo + echo "ERROR: $1" + exit 1 +} + +echo "Creating cron job for ETL..." +extract_etl_time_field "ELASTIC_UPDATE_INTERVAL" + +DOCKER_PATH="$(which "docker")" +COMPOSE_PATH="$(realpath "compose.yaml")" + +cron_command="${DOCKER_PATH} compose -f ${COMPOSE_PATH} up -d" + +case $update_interval in + *m) + minutes=${update_interval%m} + cron_expression="*/$minutes * * * * $cron_command" + ;; + *h) + hours=${update_interval%h} + cron_expression="0 */$hours * * * $cron_command" + ;; + *d) + days=${update_interval%d} + cron_expression="0 0 */$days * * $cron_command" + ;; + *) + fail "Invalid ELASTIC_UPDATE_INTERVAL" + ;; +esac + +write_cron_job +echo "ETL cron job created!" + +echo "Creating cron job for email sender..." From 911a3d0d31771d1c3d7307837683ff27e02a4be4 Mon Sep 17 00:00:00 2001 From: Jalen Hatcher Date: Fri, 10 Apr 2026 04:18:37 -0400 Subject: [PATCH 02/24] email cron job working! need to handle some more edge cases though --- scripts/cron_creation.sh | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/scripts/cron_creation.sh b/scripts/cron_creation.sh index af724d3..91e45ac 100755 --- a/scripts/cron_creation.sh +++ b/scripts/cron_creation.sh @@ -24,9 +24,20 @@ extract_email_time_field() #IFS=':' read -r hours mins <<< "$time" } -extract_etl_time_field() +extract_field() { - update_interval="$(grep $1 config.ini | cut -d '=' -f2)" + echo "$(grep $1 config.ini | cut -d '=' -f2)" +} + +parse_email_time_field() +{ + local h_or_m=$(extract_field "EMAIL_SEND_TIME" | cut -d ':' -f$1) + + if [[ "$h_or_m" == "00" ]]; then + echo "0" + else + echo $h_or_m | grep -oE '[1-9]+' + fi } write_cron_job() @@ -41,12 +52,12 @@ fail() { } echo "Creating cron job for ETL..." -extract_etl_time_field "ELASTIC_UPDATE_INTERVAL" +update_interval=$(extract_field "ELASTIC_UPDATE_INTERVAL") -DOCKER_PATH="$(which "docker")" -COMPOSE_PATH="$(realpath "compose.yaml")" +CRON_EXEC="$(which "docker")" +CRON_TARGET="$(realpath "compose.yaml")" -cron_command="${DOCKER_PATH} compose -f ${COMPOSE_PATH} up -d" +cron_command="${CRON_EXEC} compose -f ${CRON_TARGET} up -d" case $update_interval in *m) @@ -69,4 +80,14 @@ esac write_cron_job echo "ETL cron job created!" + echo "Creating cron job for email sender..." + +CRON_EXEC="$(which python3)" +CRON_TARGET=$(realpath "src/email_sender.py") + +cron_command="${CRON_EXEC} ${CRON_TARGET}" +cron_expression="$(parse_email_time_field "2") $(parse_email_time_field "1") * * * ${cron_command}" +write_cron_job +echo "Email cron job created!" + From d6e963e9a951eed1edb9dd25693268e2185a98c2 Mon Sep 17 00:00:00 2001 From: Jalen Hatcher Date: Fri, 10 Apr 2026 04:23:01 -0400 Subject: [PATCH 03/24] There may be a better place to plac this, but hopefully will do for now --- scripts/initial_setup.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index aabee8c..a3e0548 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -21,9 +21,13 @@ echo echo "Running certificate creation script for ElasticSearch..." ./scripts/cert_creation.sh +echo +echo "running cron_creation.sh..." +./scripts/cron_creation + echo echo "Starting normal secure stack..." docker compose up -d echo -echo "Setup complete." \ No newline at end of file +echo "Setup complete." From 9efc3b82e7a836755bb45b413789c5fb94f34183 Mon Sep 17 00:00:00 2001 From: Jalen Hatcher Date: Fri, 10 Apr 2026 04:30:44 -0400 Subject: [PATCH 04/24] removed some unused functions, added dependency installation for email_sender.py --- scripts/cron_creation.sh | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/scripts/cron_creation.sh b/scripts/cron_creation.sh index 91e45ac..7732c0f 100755 --- a/scripts/cron_creation.sh +++ b/scripts/cron_creation.sh @@ -8,22 +8,14 @@ set -euo pipefail cd "$(dirname "$0")/.." # install dependencies -echo "Installing dependencies..." -# pip install python-dotenv -# pip install python-crontab +echo "Installing dependencies for email_sender.py..." +pip install python-dotenv # for reading time fields declare update_interval declare cron_command declare cron_expression -extract_email_time_field() -{ - echo "$(grep )" - #local time="$(grep -e $1 config.ini | grep -oE '[0-9]+|:' | tr -d '\n')" - #IFS=':' read -r hours mins <<< "$time" -} - extract_field() { echo "$(grep $1 config.ini | cut -d '=' -f2)" From 97c1a5afb8fbd04bda3e3c608b1bf0eb3daff54a Mon Sep 17 00:00:00 2001 From: Jalen Hatcher Date: Mon, 13 Apr 2026 13:45:14 -0400 Subject: [PATCH 05/24] Handled edege cases in my script, checked for and installed python dependencies, replaced entry point to cron_creation in initial_setup script --- scripts/cron_creation.sh | 40 ++++++++++++++++++++++++++++++---------- scripts/initial_setup.sh | 8 ++++---- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/scripts/cron_creation.sh b/scripts/cron_creation.sh index 7732c0f..d4035b6 100755 --- a/scripts/cron_creation.sh +++ b/scripts/cron_creation.sh @@ -7,7 +7,9 @@ set -euo pipefail # Move into repo root cd "$(dirname "$0")/.." -# install dependencies +# start venv and install dependencies +echo "Starting a python virtual environment..." +python3 -m venv .venv echo "Installing dependencies for email_sender.py..." pip install python-dotenv @@ -21,14 +23,31 @@ extract_field() echo "$(grep $1 config.ini | cut -d '=' -f2)" } +fail() { + echo + echo "ERROR: $1" + exit 1 +} + parse_email_time_field() { local h_or_m=$(extract_field "EMAIL_SEND_TIME" | cut -d ':' -f$1) + if [[ "$h_or_m" =~ ^[0-9]{2}$ ]]; then + local stoi=10#$h_or_m + + # handle cases where hour > 23 and minute > 59 + if (( ("$1" == "1" && $stoi > 23) || ("$1" == "2" && $stoi > 59) )); then + echo "-1" + + elif [[ "$h_or_m" == "00" ]]; then + echo "0" + + else + echo $h_or_m | grep -oE '[1-9]+' + fi - if [[ "$h_or_m" == "00" ]]; then - echo "0" else - echo $h_or_m | grep -oE '[1-9]+' + echo "-1" fi } @@ -37,13 +56,8 @@ write_cron_job() cat <(fgrep -i -v "$cron_command" <(crontab -l)) <(echo "$cron_expression") | crontab - } -fail() { - echo - echo "ERROR: $1" - exit 1 -} - echo "Creating cron job for ETL..." + update_interval=$(extract_field "ELASTIC_UPDATE_INTERVAL") CRON_EXEC="$(which "docker")" @@ -80,6 +94,12 @@ CRON_TARGET=$(realpath "src/email_sender.py") cron_command="${CRON_EXEC} ${CRON_TARGET}" cron_expression="$(parse_email_time_field "2") $(parse_email_time_field "1") * * * ${cron_command}" + +# check if cron_expression contains "-1", this means we reached an edge case +# so send time format is invalid +if [[ "$cron_expression" == *"-1"* ]]; then + fail "Invalid EMAIL_SEND_TIME, must be in 24-hour format" +fi write_cron_job echo "Email cron job created!" diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index a3e0548..fa0dc59 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -21,13 +21,13 @@ echo echo "Running certificate creation script for ElasticSearch..." ./scripts/cert_creation.sh -echo -echo "running cron_creation.sh..." -./scripts/cron_creation - echo echo "Starting normal secure stack..." docker compose up -d +echo +echo "running cron_creation.sh..." +./scripts/cron_creation + echo echo "Setup complete." From d3f6bfd9dbd262efddf118270f64657164413d83 Mon Sep 17 00:00:00 2001 From: Jalen Hatcher Date: Mon, 13 Apr 2026 13:46:20 -0400 Subject: [PATCH 06/24] removed email_scheduler.py (working entirely from bash now) ;) --- src/email_scheduler.py | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 src/email_scheduler.py diff --git a/src/email_scheduler.py b/src/email_scheduler.py deleted file mode 100644 index 3ec00b0..0000000 --- a/src/email_scheduler.py +++ /dev/null @@ -1,40 +0,0 @@ -import datetime -import logging -import os -import sys - -from crontab import CronTab -from ConfigReader import config - -cron = CronTab(user=True) -logging.basicConfig(level=logging.INFO) - -def _verify_time_format(date_str: str) -> list[str]: - try: - datetime.datetime.strptime(date_str, '%H:%M') - except ValueError: - logging.error("Invalid EMAIL_SEND_TIME, must be 24-hour format") - exit(1) - return date_str.split(':') - -def create_cron_job(): - mail_time = config ['Email'] ['EMAIL_SEND_TIME'] - hours, mins = _verify_time_format(mail_time) - - script_path = os.path.abspath("src/email_sender.py") - # double check to make sure this is the path to the right executable - python_executable = sys.executable - command = f"{python_executable} {script_path}" - - job = cron.new(command=command, comment='Kibana Dashboard Email') - - # schedule the job to run every day at EMAIL_SEND_TIME - job.minute.on(int(mins)) - job.hour.on(int(hours)) - job.day.every(1) # Run every day - # the other fields (month, day of week) default to '*' - - # write the job to the crontab, critical - cron.write() - -create_cron_job() From 5fb040209cbde6fb9c2fb7f9b5e8b73c48734eb4 Mon Sep 17 00:00:00 2001 From: Jalen Hatcher Date: Mon, 13 Apr 2026 14:41:58 -0400 Subject: [PATCH 07/24] Moved installation of python and dependencies to initial_setup.sh --- scripts/cron_creation.sh | 6 ------ scripts/initial_setup.sh | 8 ++++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/scripts/cron_creation.sh b/scripts/cron_creation.sh index d4035b6..0882977 100755 --- a/scripts/cron_creation.sh +++ b/scripts/cron_creation.sh @@ -7,12 +7,6 @@ set -euo pipefail # Move into repo root cd "$(dirname "$0")/.." -# start venv and install dependencies -echo "Starting a python virtual environment..." -python3 -m venv .venv -echo "Installing dependencies for email_sender.py..." -pip install python-dotenv - # for reading time fields declare update_interval declare cron_command diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index ddb40fa..eaca012 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -11,8 +11,16 @@ echo echo "Running dependencies check..." sudo apt update sudo apt install -y unzip openssl +sudo apt install python3-venv +sudo apt install python3-tk echo "Dependencies check complete..." +echo +echo "Setting up Python virtual environment and installing dependencies..." +python3 -m venv .venv +source .venv/bin/activate +pip install python-dotenv + echo echo "Launching setup dialogue box..." python3 ./src/dialogueBox.py From 22fe006afd415fb1e048d8bdd1b256bfa249ac75 Mon Sep 17 00:00:00 2001 From: Jacob Lanter Date: Mon, 13 Apr 2026 17:28:26 -0400 Subject: [PATCH 08/24] Fix script execution by adding .sh extension --- scripts/initial_setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index eaca012..bae9857 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -40,7 +40,7 @@ docker compose up -d echo echo "running cron_creation.sh..." -./scripts/cron_creation +./scripts/cron_creation.sh echo echo "Setup complete." From 3fda0bfa7dcfd39208c7c1d47a6b980349f94fde Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Mon, 13 Apr 2026 18:18:29 -0400 Subject: [PATCH 09/24] Fixed Error with Windows Formatting Fixed an error on Windows machines where whitespace and capitalization were causing errors. Signed-off-by: Julian Valleroy --- scripts/cron_creation.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/cron_creation.sh b/scripts/cron_creation.sh index 0882977..6610405 100755 --- a/scripts/cron_creation.sh +++ b/scripts/cron_creation.sh @@ -14,7 +14,7 @@ declare cron_expression extract_field() { - echo "$(grep $1 config.ini | cut -d '=' -f2)" + echo "$(grep -i "$1" config.ini | cut -d '=' -f2 | xargs)" } fail() { @@ -97,3 +97,4 @@ fi write_cron_job echo "Email cron job created!" + From 5b9869f89442c4ce24e728dd26a4a302a82921c1 Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Mon, 13 Apr 2026 19:42:44 -0400 Subject: [PATCH 10/24] Added Cron deletion in initial setup script Signed-off-by: Julian Valleroy --- scripts/initial_setup.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index bae9857..ce7ad6b 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -7,6 +7,14 @@ set -euo pipefail # Move into repo root cd "$(dirname "$0")/.." +if crontab -l >/dev/null 2>&1; then + echo "Cron jobs exist" + echo "Deleting Cron jobs" + crontab -r +else + echo "No cron jobs" +fi + echo echo "Running dependencies check..." sudo apt update From 83139c586e7acc4793e501ecba75c13ebbf1c191 Mon Sep 17 00:00:00 2001 From: Jacob Lanter Date: Mon, 13 Apr 2026 21:56:55 -0400 Subject: [PATCH 11/24] Fixed email time formatting (base 10), restored config reader fix (credit Jalen), and added fresh docker build before running elastic setup. Signed-off-by: Jacob Lanter --- scripts/cron_creation.sh | 2 +- scripts/initial_setup.sh | 4 ++++ src/ConfigReader.py | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/cron_creation.sh b/scripts/cron_creation.sh index 6610405..aef35bc 100755 --- a/scripts/cron_creation.sh +++ b/scripts/cron_creation.sh @@ -37,7 +37,7 @@ parse_email_time_field() echo "0" else - echo $h_or_m | grep -oE '[1-9]+' + echo $((10#$h_or_m)) fi else diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index ce7ad6b..4e892d1 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -34,6 +34,10 @@ echo "Launching setup dialogue box..." python3 ./src/dialogueBox.py echo "Setup dialogue box entry complete..." +echo +echo "building images from compose.yaml..." +docker compose build --no-cache + echo echo "running elastic_setup.sh..." ./scripts/elastic_setup.sh diff --git a/src/ConfigReader.py b/src/ConfigReader.py index 99633a2..4b3f40b 100644 --- a/src/ConfigReader.py +++ b/src/ConfigReader.py @@ -1,4 +1,5 @@ import configparser +import Utils config = configparser.ConfigParser() -config.read("/config.ini") +config.read(Utils.get_project_root() / "config.ini") From 3b5e53ea212f887bb32b732ff6fbacedcbaef8b3 Mon Sep 17 00:00:00 2001 From: Jalen Hatcher Date: Tue, 14 Apr 2026 00:21:15 -0400 Subject: [PATCH 12/24] removed redundant check for 00 in the email send time field --- scripts/cron_creation.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/cron_creation.sh b/scripts/cron_creation.sh index aef35bc..4c80b74 100755 --- a/scripts/cron_creation.sh +++ b/scripts/cron_creation.sh @@ -33,8 +33,8 @@ parse_email_time_field() if (( ("$1" == "1" && $stoi > 23) || ("$1" == "2" && $stoi > 59) )); then echo "-1" - elif [[ "$h_or_m" == "00" ]]; then - echo "0" + #elif [[ "$h_or_m" == "00" ]]; then + # echo "0" else echo $((10#$h_or_m)) From 507dd47a4f205aaa22b023554916ffe3bea7d659 Mon Sep 17 00:00:00 2001 From: Jalen Hatcher Date: Tue, 14 Apr 2026 00:23:24 -0400 Subject: [PATCH 13/24] see last commit, actually removed it this time lol --- scripts/cron_creation.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/cron_creation.sh b/scripts/cron_creation.sh index 4c80b74..8aee753 100755 --- a/scripts/cron_creation.sh +++ b/scripts/cron_creation.sh @@ -31,10 +31,7 @@ parse_email_time_field() # handle cases where hour > 23 and minute > 59 if (( ("$1" == "1" && $stoi > 23) || ("$1" == "2" && $stoi > 59) )); then - echo "-1" - - #elif [[ "$h_or_m" == "00" ]]; then - # echo "0" + echo "-1" else echo $((10#$h_or_m)) From 2ea38ef4bb218e1e4e4747dde4e9f9d98a6e2571 Mon Sep 17 00:00:00 2001 From: Jacob Lanter Date: Tue, 14 Apr 2026 14:25:14 -0400 Subject: [PATCH 14/24] fix cron environment and config path in application.py Signed-off-by: Jacob Lanter --- scripts/cron_creation.sh | 2 +- src/Application.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/cron_creation.sh b/scripts/cron_creation.sh index 8aee753..9b122d7 100755 --- a/scripts/cron_creation.sh +++ b/scripts/cron_creation.sh @@ -80,7 +80,7 @@ echo "ETL cron job created!" echo "Creating cron job for email sender..." -CRON_EXEC="$(which python3)" +CRON_EXEC="$(pwd)/.venv/bin/python" CRON_TARGET=$(realpath "src/email_sender.py") cron_command="${CRON_EXEC} ${CRON_TARGET}" diff --git a/src/Application.py b/src/Application.py index e79984d..62b0412 100644 --- a/src/Application.py +++ b/src/Application.py @@ -4,7 +4,7 @@ import os from Verkada import VerkadaContext -from ConfigReader import config +import configparser from datetime import datetime, date, timedelta ELASTIC_PASSWORD = os.environ.get("ELASTIC_PASSWORD") @@ -12,6 +12,9 @@ print("ERROR: ELASTIC_PASSWORD has not been set, exiting...") exit(1) +config = configparser.ConfigParser() +config.read("/config.ini") + def init(): args = CLI.setup_cli() From 096046edddcfeefba11ff784bbdbac75190c0e83 Mon Sep 17 00:00:00 2001 From: Jacob Lanter Date: Wed, 15 Apr 2026 14:17:40 -0400 Subject: [PATCH 15/24] Created get ip script that is added to the initial script. I modified email send to chech .env first for ip before using local ip function. adjusted email body. Signed-off-by: Jacob Lanter --- scripts/get_ip.sh | 5 +++++ scripts/initial_setup.sh | 6 ++++++ src/email_sender.py | 6 +++--- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100755 scripts/get_ip.sh diff --git a/scripts/get_ip.sh b/scripts/get_ip.sh new file mode 100755 index 0000000..5fdb3e9 --- /dev/null +++ b/scripts/get_ip.sh @@ -0,0 +1,5 @@ +WINDOWS_IP=$(ipconfig.exe | sed -n '/Wireless LAN adapter Wi-Fi:/,/IPv4 Address/s/.*IPv4 Address[^:]*: //p' | tr -d '\r' | xargs) + +grep -q "^WINDOWS_IP=" .env && \ + sed -i "s|^WINDOWS_IP=.*|WINDOWS_IP=$WINDOWS_IP|" .env || \ + echo "WINDOWS_IP=$WINDOWS_IP" >> .env \ No newline at end of file diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index 4e892d1..6d88613 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -29,6 +29,12 @@ python3 -m venv .venv source .venv/bin/activate pip install python-dotenv +echo +echo "Getting Windows IP" +./scripts/get_ip.sh +echo "Windows IP added to .env" + + echo echo "Launching setup dialogue box..." python3 ./src/dialogueBox.py diff --git a/src/email_sender.py b/src/email_sender.py index 47f2df3..9c80051 100644 --- a/src/email_sender.py +++ b/src/email_sender.py @@ -44,7 +44,8 @@ def get_ip(): s.close() return IP -IP = get_ip() +load_dotenv() +IP = os.getenv("WINDOWS_IP") or get_ip() # read prefix only from config (no env fallback) body_prefix = config['Email'].get( @@ -55,14 +56,13 @@ def get_ip():

{body_prefix}

- Kibana Dashboard + Kibana Dashboard """ message.attach(MIMEText(html, "html")) -load_dotenv() email_password = os.getenv("EMAIL_PASSWORD") send_email() From c8058e0a30b4076328795b0ef43f786f93b2327c Mon Sep 17 00:00:00 2001 From: Jacob Lanter Date: Wed, 15 Apr 2026 14:24:29 -0400 Subject: [PATCH 16/24] Fix typo in email HTML link text --- src/email_sender.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/email_sender.py b/src/email_sender.py index 9c80051..0b06021 100644 --- a/src/email_sender.py +++ b/src/email_sender.py @@ -56,7 +56,7 @@ def get_ip():

{body_prefix}

- Kibana Dashboard + Kibana Dashboards """ From 5833d980faf8c30e3bb28966b588db7521928ba7 Mon Sep 17 00:00:00 2001 From: Jacob Lanter Date: Wed, 15 Apr 2026 16:57:20 -0400 Subject: [PATCH 17/24] Ensures .env and config.ini exists before initial scripts continues. Ensured our .examples match current configurations. Signed-off-by: Jacob Lanter --- .env.example | 3 ++- config.ini.example | 18 +++++++++--------- scripts/initial_setup.sh | 7 +++++++ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/.env.example b/.env.example index 12009b6..d14be81 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,5 @@ VERKADA_API_KEY=This should be your API Key== ELASTIC_PASSWORD=At least six characters long KIBANA_SYSTEM_PASSWORD=At least six characters long and you will need to set in Kibana refer to README KIBANA_ENCRYPTION_KEY=Create Key using command "openssl rand -base64 32" in linux or wsl== -EMAIL_PASSWORD = Password for the email account you are sending the dashboard from \ No newline at end of file +EMAIL_PASSWORD = Password for the email account you are sending the dashboard from +WINDOWS_IP=This is the IP for your device \ No newline at end of file diff --git a/config.ini.example b/config.ini.example index fadeaa1..d01ecd9 100644 --- a/config.ini.example +++ b/config.ini.example @@ -1,14 +1,14 @@ [Email] -EMAIL_TO=receiver_email@example.com -EMAIL_FROM=your_email@example.com -EMAIL_BODY_PREFIX=Click the link below to access the Kibana dashboard: -EMAIL_SUBJECT=Subject for the Email -EMAIL_SEND_TIME=09:00 +email_to = email@example.com +email_from = email@example.com +email_body_prefix = Click the link below to access link: +email_subject = Subject Line goes here +email_send_time = 14:40 [Verkada] # Default number of days to pull on first run or if index is empty -TIME_DELTA_INSTALLATION=365 +time_delta_installation = 3 -# This is how often you want to update elasticSearch (in 24-hour format) -# i.e. every 15 minutes -> ELASTIC_UPDATE_INTERVAL=00:15 -ELASTIC_UPDATE_INTERVAL = 00:15 +# This is how often you want to update elasticSearch +# i.e. every 15 minutes -> ELASTIC_UPDATE_INTERVAL=15m +elastic_update_interval = 15m diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index 6d88613..315c34a 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -34,6 +34,13 @@ echo "Getting Windows IP" ./scripts/get_ip.sh echo "Windows IP added to .env" +echo +echo "Ensuring config.ini exists..." +cp -n config.ini.example config.ini + +echo +echo "Ensuring .env exists..." +cp -n .env.example .env echo echo "Launching setup dialogue box..." From d90deab3dca3fb6265e8c05e0704a494e8ce02a5 Mon Sep 17 00:00:00 2001 From: Jacob Lanter Date: Wed, 15 Apr 2026 18:29:49 -0400 Subject: [PATCH 18/24] Added -i to the cron jobs check in initial setup. This confirms deletion of old jobs Signed-off-by: Jacob Lanter --- scripts/initial_setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index 315c34a..eae9727 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -10,7 +10,7 @@ cd "$(dirname "$0")/.." if crontab -l >/dev/null 2>&1; then echo "Cron jobs exist" echo "Deleting Cron jobs" - crontab -r + crontab -i -r else echo "No cron jobs" fi From efed9e3a9e2b105be8d4c13261308f31786b43dc Mon Sep 17 00:00:00 2001 From: Jalen Hatcher Date: Wed, 15 Apr 2026 23:31:14 -0400 Subject: [PATCH 19/24] Implemented functionality to automatically contact the correct smtp server given a sender email in .env --- src/email_sender.py | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/email_sender.py b/src/email_sender.py index 0b06021..fe7780e 100644 --- a/src/email_sender.py +++ b/src/email_sender.py @@ -12,21 +12,39 @@ sender_email = config ['Email'] ['EMAIL_FROM'] email_subject = config ['Email'] ['EMAIL_SUBJECT'] +domain_to_server_mapping: dict = { + "gmail.com": "gmail.com", + "outlook.com": "office365.com", + "louisville.edu": "office365.com", + "shslou.org": "office365.com" +} + message = MIMEMultipart() message["From"] = sender_email message["To"] = recipient_email message["Subject"] = email_subject +def domain_to_server(email: str) -> str | None: + if '@' in email: + domain = email[email.index('@') + 1:] + smtp_server = domain_to_server_mapping.get(domain, None) + return smtp_server + return None + def send_email(): - try: - with smtplib.SMTP("smtp.gmail.com", 587) as s: - s.starttls() - s.login(sender_email, str(email_password)) - s.sendmail(sender_email, recipient_email, message.as_string()) - s.quit() - except Exception as e: - logging.error(f"Unable to send email: error code {e.args.index}") - exit(1) + smtp_server = domain_to_server(sender_email) + if smtp_server: + try: + with smtplib.SMTP(f"smtp.{smtp_server}", 587) as s: + s.set_debuglevel(1) + s.starttls() + s.login(sender_email, str(email_password)) + s.sendmail(sender_email, recipient_email, message.as_string()) + s.quit() + except Exception as e: + logging.error(f"Unable to send email: error code {e.args.index}") + exit(1) + logging.error("Unable to send email. Make sure the sender email is correct!") # for sending the link to Kibana through email # IP address is likely to change due to proxies or DHCP lease termination @@ -66,3 +84,4 @@ def get_ip(): email_password = os.getenv("EMAIL_PASSWORD") send_email() + From 2004ef31bb05ac2550cafcba00401cb5ebc46bb3 Mon Sep 17 00:00:00 2001 From: Jalen Hatcher Date: Thu, 16 Apr 2026 01:33:48 -0400 Subject: [PATCH 20/24] Added else catch to avoid false error in domain_to_server --- src/email_sender.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/email_sender.py b/src/email_sender.py index fe7780e..d9395a7 100644 --- a/src/email_sender.py +++ b/src/email_sender.py @@ -44,7 +44,8 @@ def send_email(): except Exception as e: logging.error(f"Unable to send email: error code {e.args.index}") exit(1) - logging.error("Unable to send email. Make sure the sender email is correct!") + else: + logging.error("Unable to send email. Make sure the sender email is correct!") # for sending the link to Kibana through email # IP address is likely to change due to proxies or DHCP lease termination From 36fb11d0a2706c131882285766e621a85a6cd9cc Mon Sep 17 00:00:00 2001 From: Jacob Lanter Date: Thu, 16 Apr 2026 12:10:08 -0400 Subject: [PATCH 21/24] added subprocess run inside email_sender to run get_ip.sh so that latest ip is retreived. add safe guards to get_ip and cleaned it up Signed-off-by: Jacob Lanter --- scripts/get_ip.sh | 19 ++++++++++++++++++- src/email_sender.py | 5 +++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/scripts/get_ip.sh b/scripts/get_ip.sh index 5fdb3e9..4318dad 100755 --- a/scripts/get_ip.sh +++ b/scripts/get_ip.sh @@ -1,4 +1,21 @@ -WINDOWS_IP=$(ipconfig.exe | sed -n '/Wireless LAN adapter Wi-Fi:/,/IPv4 Address/s/.*IPv4 Address[^:]*: //p' | tr -d '\r' | xargs) +#!/usr/bin/env bash +# Run this from the root of the repo + +set -euo pipefail + +# Move into repo root +cd "$(dirname "$0")/.." + +WINDOWS_IP=$(/mnt/c/Windows/System32/ipconfig.exe | sed -n '/Wireless LAN adapter.*:/,/IPv4 Address/s/.*IPv4 Address[^:]*: //p' | head -n 1 | tr -d '\r' | xargs) + +if [ -z "$WINDOWS_IP" ]; then + WINDOWS_IP=$(/mnt/c/Windows/System32/ipconfig.exe | sed -n '/Ethernet adapter.*:/,/IPv4 Address/s/.*IPv4 Address[^:]*: //p' | head -n 1 | tr -d '\r' | xargs) +fi + +if [ -z "$WINDOWS_IP" ]; then + echo "ERROR: Could not determine Windows IP" + exit 1 +fi grep -q "^WINDOWS_IP=" .env && \ sed -i "s|^WINDOWS_IP=.*|WINDOWS_IP=$WINDOWS_IP|" .env || \ diff --git a/src/email_sender.py b/src/email_sender.py index d9395a7..1579b16 100644 --- a/src/email_sender.py +++ b/src/email_sender.py @@ -2,6 +2,8 @@ import os import smtplib import socket +import subprocess +import Utils from dotenv import load_dotenv from email.mime.multipart import MIMEMultipart @@ -63,6 +65,9 @@ def get_ip(): s.close() return IP +script_path = Utils.get_project_root() / "scripts" / "get_ip.sh" +subprocess.run([script_path], check=True) + load_dotenv() IP = os.getenv("WINDOWS_IP") or get_ip() From 90a2b80d54697f911f4e58ce3a5d95a2ccf17216 Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Thu, 16 Apr 2026 16:02:35 -0400 Subject: [PATCH 22/24] Fixed Issue with Dialogue Box Memory Fixed an issue where the popup box was pulling from memory instead of the disk. Signed-off-by: Julian Valleroy --- src/dialogueBox.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index bf8ca0a..336132e 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -18,15 +18,15 @@ def Save_Button(): newTo = inpTo.get() newFrom = inpFrom.get() newSubject = inpSubject.get() - newMessage = inpMessage.get("1.0", "end-1c") + newMessage = inpMessage.get("1.0", "end").strip() newSendTime = inpSendTime.get() newFreq = freqVar.get() newFreqUnit = timeUnit.get() newEmailPass = inpEmailPass.get() - newVerkAPI = inpApiKey.get("1.0", "end-1c") + newVerkAPI = inpApiKey.get("1.0", "end").strip() newElastPass = inpElastPass.get() newKibPass =inpKibPass.get() - newKibEnc = inpKibEnc.get("1.0", "end-1c") + newKibEnc = inpKibEnc.get("1.0", "end").strip() #Updates config values co["Email"]["EMAIL_TO"] = newTo @@ -40,6 +40,8 @@ def Save_Button(): dotenv.set_key(dotenv_file, "ELASTIC_PASSWORD", newElastPass) dotenv.set_key(dotenv_file,"KIBANA_SYSTEM_PASSWORD", newKibPass) dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) + + dotenv.load_dotenv(override=True) #Writes new config values to file with open(os.path.join(BASE_DIR, '..', 'config.ini'), "w") as f: From dc2964a95a99f9801b263c38762aadf67419f0ae Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Thu, 16 Apr 2026 16:32:27 -0400 Subject: [PATCH 23/24] Fixed Issue with multiple .env files Signed-off-by: Julian Valleroy --- src/dialogueBox.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 336132e..51df303 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -41,7 +41,7 @@ def Save_Button(): dotenv.set_key(dotenv_file,"KIBANA_SYSTEM_PASSWORD", newKibPass) dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) - dotenv.load_dotenv(override=True) + dotenv.load_dotenv(dotenv_file, override=True) #Writes new config values to file with open(os.path.join(BASE_DIR, '..', 'config.ini'), "w") as f: @@ -61,8 +61,8 @@ def generate_kibana_key(): co = configparser.ConfigParser() co.read(os.path.join(BASE_DIR, '..', 'config.ini')) - dotenv.load_dotenv() - dotenv_file = ".env" + dotenv_file = os.path.join(BASE_DIR, '..', '.env') + dotenv.load_dotenv(dotenv_file) root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) frame = tk.Frame(root) From 8cda9b7088679812f18d675db7a796ff7e8bb945 Mon Sep 17 00:00:00 2001 From: Jalen Hatcher Date: Thu, 16 Apr 2026 16:50:51 -0400 Subject: [PATCH 24/24] Added check for existing .env file --- scripts/initial_setup.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index eae9727..dd52451 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -25,7 +25,9 @@ echo "Dependencies check complete..." echo echo "Setting up Python virtual environment and installing dependencies..." -python3 -m venv .venv +if [ ! -d ".venv" ]; then + python3 -m venv .venv +fi source .venv/bin/activate pip install python-dotenv