diff --git a/.dockerignore b/.dockerignore index 8ffb737e2c..c3aefc7e58 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,49 @@ +# Files to ignore by Docker when transfering context + **/.git /docs/ /notebooks/ + +**/.DS_Store + +# Back-up files +**/*~ +**/*.swp + +# Generic auto-generated build files +**/*.pyc +**/*.pyo +**/.ipynb_checkpoints/ + +# Specific auto-generated build files +/.eggs +/.tox +**/__pycache__ +/api_client/python/build/ +/cli_client/python/build/ +/importer_client/python/build/ +**/dependencies/ +/*.egg-info + +# Ignore frontend build related files +**/node_modules +/timesketch/static/dist +**/package-lock.json +**/yarn.lock + +# Test files +**/.coverage +**/tests-coverage.txt + +# Exclude Vagrant runtime files +/vagrant/.vagrant/ +/vagrant/*.log + +# Exclude .venv folder +/.venv/ + +# Exclude Visual Studio Code files +/.vscode/* + +# Exclude JetBrains IDE files +/.idea/ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..8c3938ddca --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +* text=auto +**/docker/** text=auto eol=lf +requirements.txt text=auto eol=lf +requirements-dev.txt text=auto eol=lf + diff --git a/.gitignore b/.gitignore index 45ed349e37..100d9312bf 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,6 @@ vagrant/*.log # Exclude debugging profiles profiles/ + +# Exclude Docker Compose .env file +.env diff --git a/contrib/docker/dev/.env.template b/contrib/docker/dev/.env.template new file mode 100644 index 0000000000..4a16a750e6 --- /dev/null +++ b/contrib/docker/dev/.env.template @@ -0,0 +1,22 @@ +GIFT_PPA_TRACK="stable" +GIFT_PPA_URL="https://ppa.launchpadcontent.net/gift/${GIFT_PPA_TRACK}/ubuntu" +NODE_VERSION="20.x" +NODE_PPA_URL="https://deb.nodesource.com/node_${NODE_VERSION}" +NODE_NPMRC="" +YARN_YARNRC="" +PYTHON_PIP_CONF="" + +TIMESKETCH_BASE_IMAGE="ubuntu:24.04" +TIMESKETCH_CONF_DIR="/etc/timesketch" +TIMESKETCH_SECRET_KEY="L4np0jV3yAdAFdbVzWRMaBqiFMV8FKYd+Je1WKE40o8=" +TIMESKETCH_USER="dev" +TIMESKETCH_PASSWORD="dev" +TIMESKETCH_USER_NAME="ubuntu" +TIMESKETCH_USER_UID="1000" +TIMESKETCH_USER_GID="1000" + +POSTGRES_USER="timesketch" +POSTGRES_PASSWORD="password" +POSTGRES_DB="timesketch" + +FRONTEND_VERSION="frontend-ng" diff --git a/contrib/docker/dev/README.md b/contrib/docker/dev/README.md new file mode 100644 index 0000000000..7d7eabac62 --- /dev/null +++ b/contrib/docker/dev/README.md @@ -0,0 +1,63 @@ +# Docker Compose for development + +## Prepare a .env file + +Compose requires a `.env` file with top level environment variables to be set. +To create it, just copy the `.env.template` file as a base. + +```bash +cp contrib/docker/dev/.env.template contrib/docker/dev/.env +``` + +Note the `.env` is ignored by Git: You can safely write sensitive data in it. + +You can optionally edit the `.env` file. +This is useful if you need to build images with some company restrictions +(accessing remote Ubuntu, PyPI or Node repositories). + +The default Timesketch user is `dev` and its password is `dev`. + +## Build or rebuild images + +To build images: + +```bash +docker compose -f contrib/docker/dev/compose.yaml build +``` + +## Start services + +To start all services: + +```bash +docker compose -f contrib/docker/dev/compose.yaml up -d +``` + +On the first run, the _setup_ service will: + +- Create the Timesketch user (defaults `dev` with password `dev`), +- Clone (fetch on subsequent runs) the + [SigmaHQ/sigma](https://github.com/SigmaHQ/sigma) repository, +- Import the sigma rules set in [sigma_rules.txt](timesketch/sigma_rules.txt) + +Go to your browser: + +- : Frozen frontend (_dist_ compiled files), backend + with latest sources and live reload, +- : Frontend and backend with latest sources and live + reload (slower to be available). +- : Jupyter notebook with (password: `timesketch`). + +## Stop services + +To stop services and remove related containers: + +```bash +docker compose -f contrib/docker/dev/compose.yaml down +``` + +To delete service data at once: + +```bash +docker compose -f contrib/docker/dev/compose.yaml down -v +``` diff --git a/contrib/docker/dev/compose.yaml b/contrib/docker/dev/compose.yaml new file mode 100644 index 0000000000..c06cac9a6e --- /dev/null +++ b/contrib/docker/dev/compose.yaml @@ -0,0 +1,277 @@ +name: timesketch-dev + +networks: + timesketch-dev: + +volumes: + setup-data: + timesketch-data: + celery-worker-data: + opensearch-data: + postgresql-data: + redis-data: + prometheus-data: + notebook-data: + +services: + setup: + image: timesketch-setup:latest + build: + context: ../../.. + dockerfile: contrib/docker/dev/timesketch/Dockerfile + target: setup + args: + BASE_IMAGE: "${TIMESKETCH_BASE_IMAGE:?}" + TIMESKETCH_USER_NAME: "${TIMESKETCH_USER_NAME:?}" + TIMESKETCH_USER_UID: "${TIMESKETCH_USER_UID:?}" + TIMESKETCH_USER_GID: "${TIMESKETCH_USER_GID:?}" + TIMESKETCH_CONF_DIR: "${TIMESKETCH_CONF_DIR:?}" + GIFT_PPA_TRACK: "${GIFT_PPA_TRACK:?}" + GIFT_PPA_URL: "${GIFT_PPA_URL:?}" + NODE_VERSION: "${NODE_VERSION:?}" + NODE_PPA_URL: "${NODE_PPA_URL:?}" + NODE_NPMRC: "${NODE_NPMRC?}" + YARN_YARNRC: "${YARN_YARNRC?}" + PYTHON_PIP_CONF: "${PYTHON_PIP_CONF?}" + command: timesketch + env_file: + - timesketch/timesketch.env + volumes: + - "setup-data:/usr/local/src/sigma" + - "../../../:/usr/local/src/timesketch" + - "./timesketch/timesketch.conf:${TIMESKETCH_CONF_DIR:?}/timesketch.conf:ro" + - "./timesketch/sigma_rules.txt:${TIMESKETCH_CONF_DIR:?}/sigma_rules.txt:ro" + - "../../../data/sigma_config.yaml:${TIMESKETCH_CONF_DIR:?}/sigma_config.yaml:ro" + - "../../../data/sigma:${TIMESKETCH_CONF_DIR:?}/sigma:ro" + depends_on: + postgresql: + condition: service_healthy + redis: + condition: service_healthy + networks: + - timesketch-dev + + celery-worker: + image: timesketch-celery-worker:latest + build: + context: ../../.. + dockerfile: contrib/docker/dev/timesketch/Dockerfile + target: celery-worker + args: + BASE_IMAGE: "${TIMESKETCH_BASE_IMAGE:?}" + TIMESKETCH_USER_NAME: "${TIMESKETCH_USER_NAME:?}" + TIMESKETCH_USER_UID: "${TIMESKETCH_USER_UID:?}" + TIMESKETCH_USER_GID: "${TIMESKETCH_USER_GID:?}" + TIMESKETCH_CONF_DIR: "${TIMESKETCH_CONF_DIR:?}" + GIFT_PPA_TRACK: "${GIFT_PPA_TRACK:?}" + GIFT_PPA_URL: "${GIFT_PPA_URL:?}" + NODE_VERSION: "${NODE_VERSION:?}" + NODE_PPA_URL: "${NODE_PPA_URL:?}" + NODE_NPMRC: "${NODE_NPMRC?}" + YARN_YARNRC: "${YARN_YARNRC?}" + PYTHON_PIP_CONF: "${PYTHON_PIP_CONF?}" + env_file: + - timesketch/timesketch.env + volumes: + - "../../../:/usr/local/src/timesketch/" + - "./timesketch/timesketch.conf:${TIMESKETCH_CONF_DIR:?}/timesketch.conf:ro" + - "../../../data/regex_features.yaml:${TIMESKETCH_CONF_DIR:?}/regex_features.yaml:ro" + - "../../../data/winevt_features.yaml:${TIMESKETCH_CONF_DIR:?}/winevt_features.yaml:ro" + - "../../../data/tags.yaml:${TIMESKETCH_CONF_DIR:?}/tags.yaml:ro" + - "../../../data/intelligence_tag_metadata.yaml:${TIMESKETCH_CONF_DIR:?}/intelligence_tag_metadata.yaml:ro" + - "../../../data/plaso.mappings:${TIMESKETCH_CONF_DIR:?}/plaso.mappings:ro" + - "../../../data/generic.mappings:${TIMESKETCH_CONF_DIR:?}/generic.mappings:ro" + - "../../../data/ontology.yaml:${TIMESKETCH_CONF_DIR:?}/ontology.yaml:ro" + - "../../../data/data_finder.yaml:${TIMESKETCH_CONF_DIR:?}/data_finder.yaml:ro" + - "../../../data/bigquery_matcher.yaml:${TIMESKETCH_CONF_DIR:?}/bigquery_matcher.yaml:ro" + - "../../../data/sigma_config.yaml:${TIMESKETCH_CONF_DIR:?}/sigma_config.yaml:ro" + - "../../../data/sigma:${TIMESKETCH_CONF_DIR:?}/sigma:ro" + - "../../../data/dfiq:${TIMESKETCH_CONF_DIR:?}/dfiq:ro" + - "../../../data/context_links.yaml:${TIMESKETCH_CONF_DIR:?}/context_links.yaml:ro" + - "../../../data/plaso_formatters.yaml:${TIMESKETCH_CONF_DIR:?}/plaso_formatters.yaml:ro" + - "../../../data/nl2q:${TIMESKETCH_CONF_DIR:?}/nl2q:ro" + - "../../../data/llm_summarize:${TIMESKETCH_CONF_DIR:?}/llm_summarize:ro" + - "timesketch-data:/tmp" + - "celery-worker-data:/var/log/timesketch/psort" + depends_on: + setup: + condition: service_completed_successfully + opensearch: + condition: service_started + networks: + - timesketch-dev + + gunicorn: + image: timesketch-gunicorn:latest + build: + context: ../../.. + dockerfile: contrib/docker/dev/timesketch/Dockerfile + target: gunicorn + args: + BASE_IMAGE: "${TIMESKETCH_BASE_IMAGE:?}" + TIMESKETCH_USER_NAME: "${TIMESKETCH_USER_NAME:?}" + TIMESKETCH_USER_UID: "${TIMESKETCH_USER_UID:?}" + TIMESKETCH_USER_GID: "${TIMESKETCH_USER_GID:?}" + TIMESKETCH_CONF_DIR: "${TIMESKETCH_CONF_DIR:?}" + GIFT_PPA_TRACK: "${GIFT_PPA_TRACK:?}" + GIFT_PPA_URL: "${GIFT_PPA_URL:?}" + NODE_VERSION: "${NODE_VERSION:?}" + NODE_PPA_URL: "${NODE_PPA_URL:?}" + NODE_NPMRC: "${NODE_NPMRC?}" + YARN_YARNRC: "${YARN_YARNRC?}" + PYTHON_PIP_CONF: "${PYTHON_PIP_CONF?}" + ports: + - name: gunicorn + published: "5000" + target: 5000 + - name: metrics + published: "8080" + target: 8080 + env_file: + - timesketch/timesketch.env + volumes: + - "../../../:/usr/local/src/timesketch/" + - "./timesketch/timesketch.conf:${TIMESKETCH_CONF_DIR:?}/timesketch.conf:ro" + - "../../../data/regex_features.yaml:${TIMESKETCH_CONF_DIR:?}/regex_features.yaml:ro" + - "../../../data/winevt_features.yaml:${TIMESKETCH_CONF_DIR:?}/winevt_features.yaml:ro" + - "../../../data/tags.yaml:${TIMESKETCH_CONF_DIR:?}/tags.yaml:ro" + - "../../../data/intelligence_tag_metadata.yaml:${TIMESKETCH_CONF_DIR:?}/intelligence_tag_metadata.yaml:ro" + - "../../../data/plaso.mappings:${TIMESKETCH_CONF_DIR:?}/plaso.mappings:ro" + - "../../../data/generic.mappings:${TIMESKETCH_CONF_DIR:?}/generic.mappings:ro" + - "../../../data/ontology.yaml:${TIMESKETCH_CONF_DIR:?}/ontology.yaml:ro" + - "../../../data/data_finder.yaml:${TIMESKETCH_CONF_DIR:?}/data_finder.yaml:ro" + - "../../../data/bigquery_matcher.yaml:${TIMESKETCH_CONF_DIR:?}/bigquery_matcher.yaml:ro" + - "../../../data/sigma_config.yaml:${TIMESKETCH_CONF_DIR:?}/sigma_config.yaml:ro" + - "../../../data/sigma:${TIMESKETCH_CONF_DIR:?}/sigma:ro" + - "../../../data/dfiq:${TIMESKETCH_CONF_DIR:?}/dfiq:ro" + - "../../../data/context_links.yaml:${TIMESKETCH_CONF_DIR:?}/context_links.yaml:ro" + - "../../../data/plaso_formatters.yaml:${TIMESKETCH_CONF_DIR:?}/plaso_formatters.yaml:ro" + - "../../../data/nl2q:${TIMESKETCH_CONF_DIR:?}/nl2q:ro" + - "../../../data/llm_summarize:${TIMESKETCH_CONF_DIR:?}/llm_summarize:ro" + - "timesketch-data:/tmp" + depends_on: + setup: + condition: service_completed_successfully + networks: + - timesketch-dev + + vue-cli-service: + image: timesketch-vue-cli-service:latest + build: + context: ../../.. + dockerfile: contrib/docker/dev/timesketch/Dockerfile + target: vue-cli-service + args: + BASE_IMAGE: "${TIMESKETCH_BASE_IMAGE:?}" + TIMESKETCH_USER_NAME: "${TIMESKETCH_USER_NAME:?}" + TIMESKETCH_USER_UID: "${TIMESKETCH_USER_UID:?}" + TIMESKETCH_USER_GID: "${TIMESKETCH_USER_GID:?}" + TIMESKETCH_CONF_DIR: "${TIMESKETCH_CONF_DIR:?}" + GIFT_PPA_TRACK: "${GIFT_PPA_TRACK:?}" + GIFT_PPA_URL: "${GIFT_PPA_URL:?}" + NODE_VERSION: "${NODE_VERSION:?}" + NODE_PPA_URL: "${NODE_PPA_URL:?}" + NODE_NPMRC: "${NODE_NPMRC?}" + YARN_YARNRC: "${YARN_YARNRC?}" + PYTHON_PIP_CONF: "${PYTHON_PIP_CONF?}" + FRONTEND_VERSION: "${FRONTEND_VERSION?}" + ports: + - name: vue-cli-service + published: "5001" + target: 5001 + env_file: + - timesketch/timesketch.env + volumes: + - "../../../:/usr/local/src/timesketch/" + depends_on: + gunicorn: + condition: service_healthy + networks: + - timesketch-dev + + opensearch: + image: timesketch-opensearch:latest + build: + context: opensearch + env_file: + - opensearch/opensearch.env + ports: + - name: api + published: "9200" + target: 9200 + volumes: + - "opensearch-data:/usr/share/opensearch/data" + networks: + - timesketch-dev + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + + postgresql: + image: timesketch-postgresql:latest + build: + context: postgresql + env_file: + - postgresql/postgresql.env + ports: + - name: database + published: "5432" + target: 5432 + volumes: + - "postgresql-data:/var/lib/postgresql/data" + networks: + - timesketch-dev + + redis: + image: timesketch-redis + build: + context: redis + ports: + - name: database + published: "6379" + target: 6379 + volumes: + - "redis-data:/data" + networks: + - timesketch-dev + + prometheus: + image: prom/prometheus:v2.24.1 + volumes: + - "./prometheus:/etc/prometheus:ro" + - "prometheus-data:/prometheus" + ports: + - name: database + published: "9090" + target: 9090 + + command: --config.file=/etc/prometheus/prometheus.yml + depends_on: + gunicorn: + condition: service_healthy + networks: + - timesketch-dev + + notebook: + image: timesketch-notebook:latest + build: + context: ../../.. + dockerfile: contrib/docker/dev/notebook/Dockerfile + args: + PYTHON_PIP_CONF: "${PYTHON_PIP_CONF?}" + ports: + - name: web + published: "8844" + target: 8844 + volumes: + - "../../../:/usr/local/src/timesketch/:ro" + - "notebook-data:/usr/local/src/picadata" + depends_on: + opensearch: + condition: service_started + networks: + - timesketch-dev diff --git a/contrib/docker/dev/notebook/10-widgets.py b/contrib/docker/dev/notebook/10-widgets.py new file mode 100644 index 0000000000..baf47efd43 --- /dev/null +++ b/contrib/docker/dev/notebook/10-widgets.py @@ -0,0 +1,108 @@ +# Copyright 2020 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Few functions that take advantage of ipywidgets for jupyter. + +These are temporary functions until they have been generalized +and implemented in picatrix. +""" + +# pylint: disable=undefined-variable +# pylint: disable=import-error +from picatrix.lib import utils + +import ipydatetime +import ipywidgets as widgets +import pytz + +from IPython.display import Markdown + +from timesketch_api_client import search + + +# TODO: Generalize and move to picatrix. +def generate_connect_button(click_function = None): + """Creates a button and an int form to connect to timesketch.""" + button = widgets.Button(description='Connect to sketch') + output = widgets.Output() + + sketch_field = widgets.IntText( + value=1, + description='Sketch ID:', + disabled=False + ) + + display(Markdown('## Connect to a sketch')) + display(Markdown('Select a sketch to connect to.')) + display(sketch_field, button, output) + + def _click_function(_): + with output: + timesketch_set_active_sketch_func(str(sketch_field.value)) + sketch = timesketch_get_sketch_func() + try: + display(Markdown( + f'Connected to sketch: {sketch.id}: **{sketch.name}**')) + valid = widgets.Valid(value=True, description='Connected') + display(valid) + utils.ipython_bind_global('sketch', sketch) + display(Markdown('Sketch object saved to **sketch**')) + except KeyError: + display(Markdown('**Unable to connect to sketch.**')) + invalid = widgets.Valid(value=False, description='Connected') + display(invalid) + + if click_function: + button.on_click(click_function) + else: + button.on_click(_click_function) + + +def generate_query_button(): + """Generates a button and form to query Timesketch data.""" + button = widgets.Button(description='Query Timesketch') + query_string_form = widgets.Text( + value='*', + placeholder='Type something', + description='Query String:', + disabled=False + ) + + start_time_form = ipydatetime.DatetimePicker(tzinfo=pytz.utc) + end_time_form = ipydatetime.DatetimePicker(tzinfo=pytz.utc) + + display(Markdown('## Query A Sketch')) + display(query_string_form) + display(Markdown('Start time: '), start_time_form) + display(Markdown('End time: '), end_time_form) + display(button) + + def _click_function(_): + sketch = timesketch_get_sketch_func() + search_obj = search.Search(sketch) + if start_time_form.value and end_time_form.value: + date_chip = search.DateRangeChip() + date_chip.start_time = start_time_form.value.strftime( + '%Y-%m-%dT%H:%M:%S') + date_chip.end_time = end_time_form.value.strftime( + '%Y-%m-%dT%H:%M:%S') + search_obj.add_chip(date_chip) + search_obj.query_string = query_string_form.value + display(Markdown( + f'Query **executed** - returned: {len(search_obj.table)} ' + 'records')) + utils.ipython_bind_global('search_obj', search_obj) + display(Markdown( + 'Results are stored in the **search_obj**')) + + button.on_click(_click_function) diff --git a/contrib/docker/dev/notebook/Dockerfile b/contrib/docker/dev/notebook/Dockerfile new file mode 100644 index 0000000000..028f534074 --- /dev/null +++ b/contrib/docker/dev/notebook/Dockerfile @@ -0,0 +1,42 @@ +FROM us-docker.pkg.dev/osdfir-registry/picatrix/picatrix:latest + +USER picatrix + +ENV VIRTUAL_ENV="/home/picatrix/picenv" +ENV PATH="${VIRTUAL_ENV}/bin:$PATH" +ENV JUPYTER_PORT="8844" + +RUN mkdir -p \ + "/home/picatrix/.local/share/jupyter/nbextensions/snippets/" \ + "/home/picatrix/.jupyter/custom" + +COPY --chown=1000:1000 ["contrib/docker/dev/notebook/timesketchrc", "/home/picatrix/.timesketchrc"] +COPY --chown=1000:1000 ["contrib/docker/dev/notebook/timesketch_token", "/home/picatrix/.timesketch.token"] +COPY --chown=1000:1000 [".", "/home/picatrix/code"] +COPY --chown=1000:1000 ["contrib/docker/dev/notebook/snippets.json", "/home/picatrix/.local/share/jupyter/nbextensions/snippets/snippets.json"] +COPY --chown=1000:1000 ["contrib/docker/dev/notebook/10-widgets.py", "/home/picatrix/.ipython/profile_default/startup/10-widgets.py"] +COPY --chown=1000:1000 ["contrib/docker/dev/notebook/logo.png", "/home/picatrix/.jupyter/custom/logo.png"] +COPY --chown=1000:1000 ["contrib/docker/dev/notebook/custom.css", "/home/picatrix/.jupyter/custom/custom.css"] +COPY --chown=1000:1000 ["contrib/docker/dev/notebook/timesketch", "/home/picatrix/picenv/share/jupyter/nbextensions/timesketch"] + +ARG PYTHON_PIP_CONF="" +RUN if [ -n "${PYTHON_PIP_CONF}" ]; then \ + mkdir -p ~/.config/pip; \ + env echo -e "${PYTHON_PIP_CONF}" > ~/.config/pip/pip.conf; \ + fi + +RUN sed -i -e "s/c.NotebookApp.token = 'picatrix'/c.NotebookApp.token = 'timesketch'/g" /home/picatrix/.jupyter/jupyter_notebook_config.py \ + && sed -i -e "s/c.NotebookApp.port = 8899/c.NotebookApp.port = ${JUPYTER_PORT}/g" /home/picatrix/.jupyter/jupyter_notebook_config.py \ + && pip install -e /home/picatrix/code/api_client/python \ + && pip install -e /home/picatrix/code/importer_client/python/ \ + && jupyter nbextension enable snippets/main \ + && jupyter nbextension enable timesketch/main \ + && pip install ipydatetime \ + && jupyter nbextension install --user --py ipydatetime \ + && jupyter nbextension enable --user --py ipydatetime + +WORKDIR /usr/local/src/picadata +EXPOSE 8844 + +# Run jupyter. +ENTRYPOINT ["jupyter", "notebook"] diff --git a/contrib/docker/dev/notebook/custom.css b/contrib/docker/dev/notebook/custom.css new file mode 100644 index 0000000000..5b2a19ae6c --- /dev/null +++ b/contrib/docker/dev/notebook/custom.css @@ -0,0 +1,11 @@ +#ipython_notebook img{ + display:block; + background: url("logo.png") no-repeat; + background-size: contain; + width: 83px; + height: 35px; + padding-left: 90px; + padding-bottom: 50px; + -moz-box-sizing: border-box; + box-sizing: border-box; +} diff --git a/contrib/docker/dev/notebook/logo.png b/contrib/docker/dev/notebook/logo.png new file mode 100644 index 0000000000..2775b207c3 Binary files /dev/null and b/contrib/docker/dev/notebook/logo.png differ diff --git a/contrib/docker/dev/notebook/snippets.json b/contrib/docker/dev/notebook/snippets.json new file mode 100644 index 0000000000..8b95e175bf --- /dev/null +++ b/contrib/docker/dev/notebook/snippets.json @@ -0,0 +1,81 @@ +{ + "snippets": [ + { + "name": "Example initial import", + "code" : [ + "from timesketch_api_client import search", + "from picatrix.lib import state as state_lib", + "import altair as alt", + "import numpy as np", + "import pandas as pd", + "state = state_lib.state()" + ] + }, + { + "name": "Select a sketch using magics", + "code" : [ + "%timesketch_set_active_sketch 1" + ] + }, + { + "name" : "Select a sketch using a button and a form", + "code" : [ + "# Execute cell in order to generate button.", + "generate_connect_button()" + ] + }, + { + "name" : "Query a sketch using a button and a form", + "code" : [ + "# Execute cell in order to generate button.", + "generate_query_button()" + ] + }, + { + "name": "Search with date range filter", + "code" : [ + "sketch = %timesketch_get_sketch", + "search_obj = search.Search(sketch)", + "date_chip = search.DateRangeChip()", + "date_chip.start_time = '2020-12-24T18:00:00'", + "date_chip.end_time = '2020-12-31T23:59:59'", + "search_obj.add_chip(date_chip)", + "search_obj.query_string = '*'", + "df = search_obj.table" + ] + }, + { + "name": "Run an example aggregation.", + "code" : [ + "sketch = %timesketch_get_sketch", + "params = {", + " 'field': 'domain',", + " 'limit': 10,", + " 'supported_charts': 'hbarchart',", + " 'chart_title': 'Top 10 Domains'", + "}", + " ", + "agg = sketch.run_aggregator(", + " aggregator_name='field_bucket', aggregator_parameters=params)", + " ", + "# You can get the aggregation data as a chart or a dataframe", + "agg.chart", + " ", + "# Enable this to get the results as a table", + "# agg.table", + " ", + "# To save the aggregation you need to create a name and a title.", + "# agg.name = 'Top 10 Domains'", + "# agg.title = 'Top 10 Domains'", + "# agg.save()" + ] + }, + { + "name" : "Get Timesketch client object", + "code" : [ + "# Execute cell in order to get a Timesketch API client instance.", + "client = state.get_from_cache('timesketch_client')" + ] + }, + ] +} diff --git a/contrib/docker/dev/notebook/timesketch/README.md b/contrib/docker/dev/notebook/timesketch/README.md new file mode 100644 index 0000000000..69f3fa3c04 --- /dev/null +++ b/contrib/docker/dev/notebook/timesketch/README.md @@ -0,0 +1,7 @@ +timesketch +========= + +This extension adds few default cells to each new notebook created in this +Timesketch container. + +This is a simple extension with a simple purpose. diff --git a/contrib/docker/dev/notebook/timesketch/main.js b/contrib/docker/dev/notebook/timesketch/main.js new file mode 100644 index 0000000000..a72a44aac5 --- /dev/null +++ b/contrib/docker/dev/notebook/timesketch/main.js @@ -0,0 +1,46 @@ +// Code cell snippets + +define([ + 'base/js/namespace', +], function( + Jupyter, +) { + "use strict"; + + // will be called when the nbextension is loaded + function load_extension() { + var ncells = Jupyter.notebook.ncells(); + if (ncells > 1) { + return true; + } + + var new_cell = Jupyter.notebook.insert_cell_above('markdown', 0); + new_cell.set_text('# Timesketch Notebook\nThis is a base notebook for connecting to a dev instance of Timesketch.\n**Remember to rename the notebook**.'); + new_cell.render(); + new_cell.focus_cell(); + + var new_cell = Jupyter.notebook.insert_cell_below('markdown'); + new_cell.set_text('*If you want to query data you can use the snippets menu, or create a search obj, and to display a table use `display_table(search_obj.table)` or `display_table(data_frame)`*\n\nTo see a list of available helper functions run `%picatrixhelpers` in a cell, or to see a list of functions/magics use `%picatrixmagics`.'); + new_cell.render(); + + var select_cell = Jupyter.notebook.insert_cell_below('code'); + select_cell.set_text('generate_connect_button()'); + + var text_cell = Jupyter.notebook.insert_cell_below('markdown'); + text_cell.set_text('## Select a Sketch.\nNow it is time to select a sketch to use, first execute the cell and then change the ID of the sketch to the one you want, and press the button.'); + text_cell.render(); + + var import_cell = Jupyter.notebook.insert_cell_below('code'); + import_cell.set_text('from timesketch_api_client import search\nfrom picatrix.lib import state as state_lib\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\nstate = state_lib.state()'); + + var new_cell = Jupyter.notebook.insert_cell_below('markdown'); + new_cell.set_text('## Import\nTo start a notebook we import few base libraries.\nExecute the cell below by pressing the play button or using "shift + enter"'); + new_cell.render(); + import_cell.focus_cell(); + }; + + // return public methods + return { + load_ipython_extension : load_extension + }; +}); diff --git a/contrib/docker/dev/notebook/timesketch/timesketch.yaml b/contrib/docker/dev/notebook/timesketch/timesketch.yaml new file mode 100644 index 0000000000..619fd93499 --- /dev/null +++ b/contrib/docker/dev/notebook/timesketch/timesketch.yaml @@ -0,0 +1,6 @@ +Type: IPython Notebook Extension +Compatibility: 4.x, 5.x, 6.x +Name: Timesketch +Main: main.js +Link: README.md +Description: Add few default cells to a new notebook. diff --git a/contrib/docker/dev/notebook/timesketch_token b/contrib/docker/dev/notebook/timesketch_token new file mode 100644 index 0000000000..43a2dff189 --- /dev/null +++ b/contrib/docker/dev/notebook/timesketch_token @@ -0,0 +1,2 @@ +^_ ~ &lę/ŕ +słgAAAAABfy0n4zomCWiuIuz9XGnC5Co9x1omFCNbZmpOowgqdhCzzJrzvqM6A0qhYcK4k9iILVW9CWvmn-SS0IBBnsfv-un1Amh9Ip9j4zNZNST-DBOc8N9piJ0BCXck6bVy48J_g4BfI8idmTBzXi6ehKfX4oD4Bbg== \ No newline at end of file diff --git a/contrib/docker/dev/notebook/timesketchrc b/contrib/docker/dev/notebook/timesketchrc new file mode 100644 index 0000000000..b5aa567b76 --- /dev/null +++ b/contrib/docker/dev/notebook/timesketchrc @@ -0,0 +1,8 @@ +[timesketch] +host_uri = http://timesketch:5000 +username = dev +verify = True +client_id = +client_secret = +auth_mode = userpass +cred_key = oqyymP-6FS9IY-Id-5nieXMzIajJ7NWx9Ndm_r7K0xg= diff --git a/contrib/docker/dev/opensearch.env b/contrib/docker/dev/opensearch.env new file mode 100644 index 0000000000..3176c6728d --- /dev/null +++ b/contrib/docker/dev/opensearch.env @@ -0,0 +1,6 @@ +discovery.type="single-node" +bootstrap.memory_lock="true" +network.host="0.0.0.0" +OPENSEARCH_JAVA_OPTS="-Xms2g -Xmx2g" +DISABLE_INSTALL_DEMO_CONFIG="true" +DISABLE_SECURITY_PLUGIN="true" # TODO: Enable when we have migrated the python client to Opensearch as well. diff --git a/contrib/docker/dev/opensearch/Dockerfile b/contrib/docker/dev/opensearch/Dockerfile new file mode 100644 index 0000000000..18ccc0ddf9 --- /dev/null +++ b/contrib/docker/dev/opensearch/Dockerfile @@ -0,0 +1,5 @@ +FROM opensearchproject/opensearch:2.15.0 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=90s --start-interval=5s --retries=1 \ + CMD ["curl", "-f", "-s", "http://localhost:9200/_cluster/health"] + diff --git a/contrib/docker/dev/opensearch/opensearch.env b/contrib/docker/dev/opensearch/opensearch.env new file mode 100644 index 0000000000..3176c6728d --- /dev/null +++ b/contrib/docker/dev/opensearch/opensearch.env @@ -0,0 +1,6 @@ +discovery.type="single-node" +bootstrap.memory_lock="true" +network.host="0.0.0.0" +OPENSEARCH_JAVA_OPTS="-Xms2g -Xmx2g" +DISABLE_INSTALL_DEMO_CONFIG="true" +DISABLE_SECURITY_PLUGIN="true" # TODO: Enable when we have migrated the python client to Opensearch as well. diff --git a/contrib/docker/dev/postgresql/Dockerfile b/contrib/docker/dev/postgresql/Dockerfile new file mode 100644 index 0000000000..c582354842 --- /dev/null +++ b/contrib/docker/dev/postgresql/Dockerfile @@ -0,0 +1,6 @@ +FROM postgres:13.1-alpine + +COPY --chown=root:root --chmod=755 ["docker-healthcheck.sh", "/usr/local/bin/"] + +HEALTHCHECK --interval=10s --timeout=5s --start-period=15s --start-interval=2s --retries=1 \ + CMD ["/usr/local/bin/docker-healthcheck.sh"] diff --git a/contrib/docker/dev/postgresql/docker-healthcheck.sh b/contrib/docker/dev/postgresql/docker-healthcheck.sh new file mode 100755 index 0000000000..23da820457 --- /dev/null +++ b/contrib/docker/dev/postgresql/docker-healthcheck.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +exec pg_isready -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" diff --git a/contrib/docker/dev/postgresql/postgresql.env b/contrib/docker/dev/postgresql/postgresql.env new file mode 100644 index 0000000000..40cc20cbfb --- /dev/null +++ b/contrib/docker/dev/postgresql/postgresql.env @@ -0,0 +1,3 @@ +POSTGRES_USER="${POSTGRES_USER?-}" +POSTGRES_PASSWORD="${POSTGRES_PASSWORD?-}" +POSTGRES_DB="${POSTGRES_DB?-}" diff --git a/contrib/docker/dev/prometheus/prometheus.yml b/contrib/docker/dev/prometheus/prometheus.yml new file mode 100644 index 0000000000..8f92f67fe9 --- /dev/null +++ b/contrib/docker/dev/prometheus/prometheus.yml @@ -0,0 +1,8 @@ +global: + scrape_interval: 10s + external_labels: + monitor: "timesketch" +scrape_configs: + - job_name: "timesketch" + static_configs: + - targets: ["timesketch:8080"] diff --git a/contrib/docker/dev/redis/Dockerfile b/contrib/docker/dev/redis/Dockerfile new file mode 100644 index 0000000000..f981d3ff8c --- /dev/null +++ b/contrib/docker/dev/redis/Dockerfile @@ -0,0 +1,4 @@ +FROM redis:7.2.11-alpine + +HEALTHCHECK --interval=10s --timeout=5s --start-period=5s --start-interval=1s --retries=1 \ + CMD ["redis-cli", "ping"] diff --git a/contrib/docker/dev/redis/redis.env b/contrib/docker/dev/redis/redis.env new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contrib/docker/dev/timesketch/Dockerfile b/contrib/docker/dev/timesketch/Dockerfile new file mode 100644 index 0000000000..e0a4400919 --- /dev/null +++ b/contrib/docker/dev/timesketch/Dockerfile @@ -0,0 +1,183 @@ +# Use the official Docker Hub Ubuntu base image +ARG BASE_IMAGE="ubuntu:24.04" +FROM ${BASE_IMAGE} AS common + +USER root + +ARG TIMESKETCH_USER_NAME="ubuntu" +ARG TIMESKETCH_USER_UID="1000" +ARG TIMESKETCH_USER_GID="1000" +ARG TIMESKETCH_CONF_DIR="/etc/timesketch" +RUN if id -u "${TIMESKETCH_USER_UID}" &>/dev/null; then \ + echo "User with UID ${TIMESKETCH_USER_UID} already exists."; \ + else \ + echo "Creating user ${USER_NAME} (${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID})..."; \ + if getent group "${TIMESKETCH_USER_GID}" >/dev/null; then \ + echo "Group with GID ${TIMESKETCH_USER_GID} already exists."; \ + else \ + echo "Creating group ${TIMESKETCH_USER_NAME} with GID ${TIMESKETCH_USER_GID}..."; \ + groupadd -g "${TIMESKETCH_USER_GID}" "${TIMESKETCH_USER_NAME}"; \ + fi; \ + useradd -m -u "${TIMESKETCH_USER_UID}" -g "${TIMESKETCH_USER_GID}" -s /bin/bash "${TIMESKETCH_USER_NAME}"; \ + fi \ + && for d in "${TIMESKETCH_CONF_DIR}" "/usr/local/src/sigma"; do \ + mkdir -p "${d}" \ + && chown "${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" "${d}"; \ + done \ + && echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + software-properties-common \ + apt-transport-https \ + apt-utils \ + ca-certificates \ + curl \ + git \ + gpg-agent \ + python3-dev \ + python3-pip \ + python3-wheel \ + python3-setuptools \ + python3-psycopg2 \ + python3-venv \ + tzdata \ + nano \ + vim \ + python3-venv \ + && rm -rf /var/lib/apt/lists/* + +FROM common AS common-python + +# Install Plaso +ARG GIFT_PPA_TRACK="stable" +ARG GIFT_PPA_URL="http://ppa.launchpad.net/gift/${GIFT_PPA_TRACK}/ubuntu" +RUN set -eux \ + && DIST="$(lsb_release -cs)" \ + && KEY_ID="$(curl -sS "${GIFT_PPA_URL}/dists/${DIST}/Release.gpg" | gpg --list-packets | grep -oE 'keyid [0-9A-F]+' | cut -d ' ' -f 2)" \ + && curl -sSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${KEY_ID}" | \ + gpg --dearmor -o /usr/share/keyrings/gift.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/gift.gpg] ${GIFT_PPA_URL} ${DIST} main" > /etc/apt/sources.list.d/gift.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + plaso-tools \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /root/.gnupg + +# Fix for broken PPA dependency in Ubuntu 24.04: Plaso needs the 'events' +# library for its opensearch output module. +RUN pip3 install --break-system-packages events + +ARG PYTHON_PIP_CONF="" +RUN if [ -n "${PYTHON_PIP_CONF}" ]; then \ + mkdir -p /root/.config/pip /home/${TIMESKETCH_USER_NAME}/.config/pip; \ + env echo -e "${PYTHON_PIP_CONF}" > /root/.config/pip/pip.conf; \ + cp /root/.config/pip/pip.conf /home/${TIMESKETCH_USER_NAME}/.config/pip/pip.conf; \ + chown -R "${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" /home/${TIMESKETCH_USER_NAME}/.config; \ + fi + +USER "${TIMESKETCH_USER_NAME}" + +# Install dependencies for Timesketch in a virtual environment +COPY --chown="${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" ["api_client", "/usr/local/src/timesketch/api_client/"] +COPY --chown="${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" ["cli_client", "/usr/local/src/timesketch/cli_client/"] +COPY --chown="${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" ["end_to_end_tests", "/usr/local/src/timesketch/end_to_end_tests/"] +COPY --chown="${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" ["importer_client", "/usr/local/src/timesketch/importer_client/"] +COPY --chown="${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" ["timesketch", "/usr/local/src/timesketch/timesketch/"] +COPY --chown="${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" ["tests", "/usr/local/src/timesketch/tests/"] +COPY --chown="${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" [ \ + "requirements.txt", \ + "setup.py", \ + "test_requirements.txt", \ + "/usr/local/src/timesketch/" \ +] + +RUN python3 -m venv --upgrade-deps --system-site-packages "${HOME}/venv" \ + && . "${HOME}/venv/bin/activate" \ + && pip install --no-cache-dir \ + -r /usr/local/src/timesketch/requirements.txt \ + -r /usr/local/src/timesketch/test_requirements.txt \ + && pip install -e /usr/local/src/timesketch + +# Update the PATH to include the virtual environment +ENV PATH="/home/${TIMESKETCH_USER_NAME}/venv/bin:${PATH}" +ENV TIMESKETCH_CONF_DIR="${TIMESKETCH_CONF_DIR}" + +FROM common-python AS setup + +COPY --chown=root:root --chmod=755 ["contrib/docker/dev/timesketch/setup-docker-entrypoint.sh", "/usr/local/bin/docker-entrypoint.sh"] +ENTRYPOINT ["docker-entrypoint.sh"] + +FROM common-python AS celery-worker + +USER root + +ARG PLASO_LOG_DIR="/var/log/timesketch/psort" +RUN mkdir -p "${PLASO_LOG_DIR}" \ + && chown "${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" "${PLASO_LOG_DIR}" + +USER "${TIMESKETCH_USER_NAME}" + +COPY --chown=root:root --chmod=755 ["contrib/docker/dev/timesketch/celery-worker-docker-entrypoint.sh", "/usr/local/bin/docker-entrypoint.sh"] +ENTRYPOINT ["docker-entrypoint.sh"] + +FROM common-python AS gunicorn + +COPY --chown=root:root --chmod=755 ["contrib/docker/dev/timesketch/gunicorn-docker-entrypoint.sh", "/usr/local/bin/docker-entrypoint.sh"] + +HEALTHCHECK --interval=10s --timeout=5s --start-period=15s --start-interval=2s --retries=1 \ + CMD ["curl", "-f", "-s", "http://localhost:5000/"] + +ENTRYPOINT ["docker-entrypoint.sh"] + +FROM common AS vue-cli-service + +USER root + +# Install NodeJS for frontend development +ARG NODE_VERSION="20.x" +ARG NODE_PPA_URL="https://deb.nodesource.com/node_${NODE_VERSION}" +RUN set -eux \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | \ + gpg --dearmor -o /usr/share/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/nodesource.gpg] ${NODE_PPA_URL} nodistro main" > /etc/apt/sources.list.d/nodesource.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + nodejs \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /root/.gnupg + +ARG TIMESKETCH_USER_NAME="timesketch" +ARG TIMESKETCH_USER_UID="1000" +ARG TIMESKETCH_USER_GID="1000" +ARG NODE_NPMRC="" +RUN if [ -n "${NODE_NPMRC}" ]; then \ + env echo -e "${NODE_NPMRC}" > /root/.npmrc; \ + cp /root/.npmrc /home/${TIMESKETCH_USER_NAME}/.npmrc; \ + chown "${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" /home/${TIMESKETCH_USER_NAME}/.npmrc; \ + fi + +ARG YARN_YARNRC="" +RUN if [ -n "${YARN_YARNRC}" ]; then \ + env echo -e "${YARN_YARNRC}" > /root/.yarnrc; \ + cp /root/.yarnrc /home/${TIMESKETCH_USER_NAME}/.yarnrc; \ + chown "${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" /home/${TIMESKETCH_USER_NAME}/.yarnrc; \ + fi + +# Install Yarn for frontend development +RUN npm install --global yarn + +USER "${TIMESKETCH_USER_NAME}" + +COPY --chown="${TIMESKETCH_USER_UID}:${TIMESKETCH_USER_GID}" ["timesketch", "/usr/local/src/timesketch/timesketch/"] + +ARG FRONTEND_VERSION="frontend-ng" +ENV FRONTEND_VERSION="${FRONTEND_VERSION}" +RUN if ! yarn --cwd="/usr/local/src/timesketch/timesketch/${FRONTEND_VERSION}" install; then \ + yarn --cwd="/usr/local/src/timesketch/timesketch/${FRONTEND_VERSION}" install --no-lockfile; \ +fi + +COPY --chown=root:root --chmod=755 [ \ + "contrib/docker/dev/timesketch/vue-cli-service-docker-entrypoint.sh", \ + "/usr/local/bin/docker-entrypoint.sh" \ +] +ENTRYPOINT ["docker-entrypoint.sh"] diff --git a/contrib/docker/dev/timesketch/celery-worker-docker-entrypoint.sh b/contrib/docker/dev/timesketch/celery-worker-docker-entrypoint.sh new file mode 100644 index 0000000000..2799317407 --- /dev/null +++ b/contrib/docker/dev/timesketch/celery-worker-docker-entrypoint.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +exec celery \ + -A timesketch.lib.tasks \ + worker \ + --loglevel debug diff --git a/contrib/docker/dev/timesketch/gunicorn-docker-entrypoint.sh b/contrib/docker/dev/timesketch/gunicorn-docker-entrypoint.sh new file mode 100644 index 0000000000..bc5b49c85a --- /dev/null +++ b/contrib/docker/dev/timesketch/gunicorn-docker-entrypoint.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh + +exec gunicorn \ + --reload \ + -b 0.0.0.0:5000 \ + --log-file - \ + --timeout 600 \ + -c /usr/local/src/timesketch/data/gunicorn_config.py \ + timesketch.wsgi:application diff --git a/contrib/docker/dev/timesketch/setup-docker-entrypoint.sh b/contrib/docker/dev/timesketch/setup-docker-entrypoint.sh new file mode 100644 index 0000000000..a71c94a1c2 --- /dev/null +++ b/contrib/docker/dev/timesketch/setup-docker-entrypoint.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +set -e + +# Add web user +if ! tsctl list-users | grep -q "${TIMESKETCH_USER}"; then + tsctl create-user --password "${TIMESKETCH_PASSWORD}" "${TIMESKETCH_USER}" +fi + +# Add Sigma rules +if [ -d "/usr/local/src/sigma/.git" ]; then + git -C /usr/local/src/sigma fetch --depth=1 + git -C /usr/local/src/sigma reset --hard "$(git -C /usr/local/src/sigma rev-parse --abbrev-ref --symbolic-full-name @{u})" +else + git clone --depth 1 https://github.com/SigmaHQ/sigma /usr/local/src/sigma +fi + +aggregated_rules_dir="$(mktemp -d)" + +# Create symbolic links to the rule files specified in sigma_rules.txt +while IFS= read -r rule_file_path; do + if [ -f "${rule_file_path}" ]; then + ln -s "${rule_file_path}" "${aggregated_rules_dir}/" + else + echo "Skipping non existing Sigma rule: ${rule_file_path}" + fi +done < "${TIMESKETCH_CONF_DIR}/sigma_rules.txt" + +# Loading all sigma rules at once +tsctl import-sigma-rules "${aggregated_rules_dir}" +rm -rf "${aggregated_rules_dir}" diff --git a/contrib/docker/dev/timesketch/sigma_rules.txt b/contrib/docker/dev/timesketch/sigma_rules.txt new file mode 100644 index 0000000000..699bc0fff3 --- /dev/null +++ b/contrib/docker/dev/timesketch/sigma_rules.txt @@ -0,0 +1,25 @@ +/usr/local/src/sigma/rules/application/rpc_firewall/rpc_firewall_sharphound_recon_sessions.yml +/usr/local/src/sigma/rules/application/sql/app_sqlinjection_errors.yml +/usr/local/src/sigma/other/godmode_sigma_rule.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_icmp_exfiltration.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_invoke_obfuscation_clip.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_invoke_obfuscation_obfuscated_iex.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_invoke_obfuscation_stdin.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_invoke_obfuscation_var.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_invoke_obfuscation_via_compress.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_invoke_obfuscation_via_rundll.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_invoke_obfuscation_via_stdin.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_invoke_obfuscation_via_use_clip.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_invoke_obfuscation_via_use_mhsta.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_invoke_obfuscation_via_use_rundll32.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_invoke_obfuscation_via_var.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_nishang_malicious_commandlets.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_powerview_malicious_commandlets.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_shellintel_malicious_commandlets.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_malicious_keywords.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_susp_keywords.yml +/usr/local/src/sigma/rules/windows/powershell/powershell_script/posh_ps_susp_ssl_keyword.yml +/usr/local/src/sigma/deprecated/windows/win_defender_disabled.yml +/usr/local/src/sigma/rules/windows/builtin/windefend/win_defender_tamper_protection_trigger.yml +/usr/local/src/sigma/rules/windows/builtin/windefend/win_defender_history_delete.yml +/usr/local/src/sigma/rules/windows/builtin/windefend/win_defender_threat.yml diff --git a/contrib/docker/dev/timesketch/timesketch.conf b/contrib/docker/dev/timesketch/timesketch.conf new file mode 100644 index 0000000000..4e84fde138 --- /dev/null +++ b/contrib/docker/dev/timesketch/timesketch.conf @@ -0,0 +1,492 @@ +# Timesketch configuration +import os + +# Show debug information. +# Note: It is a security risk to have this enabled in production. +DEBUG = False + +# Enable performance profiling for all backend requests. +# Profiling data will be written as .prof files to a `profiles/` directory +# in either the project root (dev) or /var/log/timesketch/ (release). +ENABLE_PROFILING = False + +# Key for signing cookies and for CSRF protection. +# +# This should be a unique random string. Don't share this with anyone. +# To generate a key, you can for example use openssl: +# $ openssl rand -base64 32 +SECRET_KEY = os.environ["SECRET_KEY"] + +# Setup the database. +# +# For more options, see the official documentation: +# https://pythonhosted.org/Flask-SQLAlchemy/config.html +# By default sqlite is used. +# +# NOTE: SQLite should only be used in development. Use PostgreSQL or MySQL in +# production. +SQLALCHEMY_DATABASE_URI = "postgresql://{USER}:{PASSWORD}@postgresql/{DATABASE}".format( + USER=os.environ['POSTGRES_USER'], + PASSWORD=os.environ['POSTGRES_PASSWORD'], + DATABASE=os.environ['POSTGRES_DB'] +) +SQLALCHEMY_ENGINE_OPTIONS = {} + +# Configure where your OpenSearch server is located. +# +# Make sure that the OpenSearch server is properly secured and not accessible +# from the internet. See the following link for more information: +# https://opensearch.org/docs/latest/getting-started/security/ +OPENSEARCH_HOSTS = [{"host": "opensearch", "port": 9200}] +OPENSEARCH_USER = None +OPENSEARCH_PASSWORD = None +OPENSEARCH_SSL = False +OPENSEARCH_VERIFY_CERTS = True +OPENSEARCH_CA_CERTS = None +OPENSEARCH_TIMEOUT = 10 +OPENSEARCH_FLUSH_INTERVAL = 5000 +OPENSEARCH_INDEX_WAIT_TIMEOUT = 10 +OPENSEARCH_MINIMUM_HEALTH = 'yellow' +# Be careful when increasing the upper limit since this will impact your +# OpenSearch clusters performance and storage requirements! +OPENSEARCH_MAPPING_BUFFER = 0.1 +OPENSEARCH_MAPPING_UPPER_LIMIT = 1000 + +# Define what labels should be defined that make it so that a sketch and +# timelines will not be deleted. This can be used to add a list of different +# labels that ensure that a sketch and it's associated timelines cannot be +# deleted. +LABELS_TO_PREVENT_DELETION = ['protected', 'preserved'] + +# Number of seconds before a timeout occurs in bulk operations in the +# OpenSearch client. +TIMEOUT_FOR_EVENT_IMPORT = 180 + +# Location for the configuration file of the data finder. +DATA_FINDER_PATH = f'{os.environ["TIMESKETCH_CONF_DIR"]}/data_finder.yaml' + +#------------------------------------------------------------------------------- +# Single Sign On (SSO) configuration. + +# Your web server can handle authentication for you by setting a environment +# variable when the user is successfully authenticated. The standard environment +# variable is REMOTE_USER and this is the default, but if your SSO system uses +# another name you can configure that here. + +SSO_ENABLED = False +SSO_USER_ENV_VARIABLE = 'REMOTE_USER' + +# Some SSO systems provides group information as environment variable. +# Timesketch can automatically create groups and add users as members. +# To enable this feature just provide the environment variable used in the SSO +# system of use. +SSO_GROUP_ENV_VARIABLE = None + +# Different systems use different separators in the string returned in the +# environment variable. +SSO_GROUP_SEPARATOR = ';' + +# Some SSO systems uses a special prefix for the group name to indicate that +# the user is not a member of that group. Set this if that is the case, i.e. +# '-'. +SSO_GROUP_NOT_MEMBER_SIGN = None + +#------------------------------------------------------------------------------- +# Google Cloud Identity-Aware Proxy (Cloud IAP) authentication configuration. + +# Cloud IAP controls access to your Timesketch server running on Google Cloud +# Platform. Cloud IAP works by verifying a user’s identity and determining if +# that user should be allowed to access the server. +# +# For this feature you will need to configure your Cloud IAP and HTTPS load- +# balancer. Follow the official documentation to get everything ready: +# https://cloud.google.com/iap/docs/enabling-compute-howto + +# Enable Cloud IAP authentication support. +GOOGLE_IAP_ENABLED = False + +# This information is available via the Google Cloud console: +# https://cloud.google.com/iap/docs/signed-headers-howto +GOOGLE_IAP_PROJECT_NUMBER = '' +GOOGLE_IAP_BACKEND_ID = '' + +# DON'T EDIT: Google IAP expected audience is based on Cloud project number and +# backend ID. +GOOGLE_IAP_AUDIENCE = '/projects/{}/global/backendServices/{}'.format( + GOOGLE_IAP_PROJECT_NUMBER, + GOOGLE_IAP_BACKEND_ID +) + +GOOGLE_IAP_ALGORITHM = 'ES256' +GOOGLE_IAP_ISSUER = 'https://cloud.google.com/iap' +GOOGLE_IAP_PUBLIC_KEY_URL = 'https://www.gstatic.com/iap/verify/public_key' + +#------------------------------------------------------------------------------- +# Google Cloud OpenID Connect (OIDC) authentication configuration. + +# Cloud OIDC controls access to your Timesketch server running on Google Cloud +# Platform. Cloud OIDC works by verifying a user’s identity and determining if +# that user should be allowed to access the server. + +# Enable Cloud OIDC authentication support. +# For Google's federated identity, leave AUTH_URI and DICOVERY_URL to None. +# For others, refer to your OIDC provider configuration. Configuration can be +# obtain from the discovery url. eg. https://accounts.google.com/.well-known/openid-configuration + +# Some OIDC providers expects a specific Algorithm. If so, specify in ALGORITHM. +# Eg. HS256, HS384, HS512, RS256, RS384, RS512. +# For Google, leave it to None + +GOOGLE_OIDC_ENABLED = False + +GOOGLE_OIDC_AUTH_URL = None +GOOGLE_OIDC_DISCOVERY_URL = None +GOOGLE_OIDC_ALGORITHM = None + +GOOGLE_OIDC_CLIENT_ID = None +GOOGLE_OIDC_CLIENT_SECRET = None + +# If you need to authenticate an API client using OIDC you need to create +# an OAUTH client for "other", or for native applications. +# https://developers.google.com/identity/protocols/OAuth2ForDevices +GOOGLE_OIDC_API_CLIENT_ID = None + +# List of additional allowed GOOGLE OIDC clients that can authenticate to the APIs +GOOGLE_OIDC_API_CLIENT_IDS = [] + +# Limit access to a specific Google GSuite domain. +GOOGLE_OIDC_HOSTED_DOMAIN = None + +# Additional Google GSuite domains allowed API access. +GOOGLE_OIDC_API_ALLOWED_DOMAINS = [] + +# If populated only these users (email addresses) will be able to login to +# this server. This can be used when access should be limited to a specific +# set of users. +GOOGLE_OIDC_ALLOWED_USERS = [] + +#------------------------------------------------------------------------------- +# Upload and processing of Plaso storage files. + +# To enable this feature you need to configure an upload directory and +# how to reach the Redis database used by the distributed task queue. +UPLOAD_ENABLED = True + +# Folder for temporarily storage of Plaso dump files before being processed and +# inserted into the datastore. +UPLOAD_FOLDER = '/tmp' + +# Celery broker configuration. You need to change ip/port to where your Redis +# server is running. +CELERY_BROKER_URL = 'redis://redis:6379' +CELERY_RESULT_BACKEND = 'redis://redis:6379' + +# File location to store the mappings used when OpenSearch indices are created +# for plaso files. +PLASO_MAPPING_FILE = f'{os.environ["TIMESKETCH_CONF_DIR"]}/plaso.mappings' +GENERIC_MAPPING_FILE = f'{os.environ["TIMESKETCH_CONF_DIR"]}/generic.mappings' + +# Override/extend Plaso default message string formatters. +PLASO_FORMATTERS = f'{os.environ["TIMESKETCH_CONF_DIR"]}/plaso_formatters.yaml' + +# Upper limits for the process memory that psort.py is allocated when ingesting +# plaso files. The size is in bytes, with the default value of +# 4294967296 or 4 GiB. +PLASO_UPPER_MEMORY_LIMIT = None + +# Directory to store Plaso (psort) log files. +# If this is set, psort will write execution logs here. +# If this is NOT set, logs will be discarded to /dev/null. +# WARNING: These logs can be large. Ensure you have log rotation configured! +PLASO_LOG_FOLDER = '/var/log/timesketch/psort/' + +#------------------------------------------------------------------------------- +# Analyzers. + +# Which analyzers to run automatically. +AUTO_SKETCH_ANALYZERS = [] + +# Optional specify any default arguments to pass to analyzers. +# The format is: +# {'analyzer1_name': { +# 'param1': 'value' +# }, +# {'analyzer2_name': { +# 'param1': 'value' +# } +# } +# } +AUTO_SKETCH_ANALYZERS_KWARGS = {} +ANALYZERS_DEFAULT_KWARGS = {} + +# Add all domains that are relevant to your enterprise here. +# All domains in this list are added to the list of watched +# domains and compared to other domains in the timeline to +# attempt to spot "phishy" domains. +DOMAIN_ANALYZER_WATCHED_DOMAINS = [] + +# Defines how deep into the most frequently visited top +# level domains the analyzer should include in its watch list. +DOMAIN_ANALYZER_WATCHED_DOMAINS_THRESHOLD = 10 + +# The minimum Jaccard distance for a domain to be considered +# similar to the domains in the watch list. The lower this number +# is the more domains will be included in the "phishy" domain +# category. +DOMAIN_ANALYZER_WATCHED_DOMAINS_SCORE_THRESHOLD = 0.75 + +# A list of domains that are frequent source of false positives +# in the "phishy" domain comparison, mostly CDNs and similar. +DOMAIN_ANALYZER_EXCLUDE_DOMAINS = ['ytimg.com', 'gstatic.com', 'yimg.com', 'akamaized.net', 'akamaihd.net', 's-microsoft.com', 'images-amazon.com', 'ssl-images-amazon.com', 'wikimedia.org', 'redditmedia.com', 'googleusercontent.com', 'googleapis.com', 'wikipedia.org', 'github.io', 'github.com'] + +# The threshold in minutes which the difference in timestamps has to cross in order to be +# detected as 'timestomping'. +NTFS_TIMESTOMP_ANALYZER_THRESHOLD = 10 + +# Safe Browsing API key for the URL analyzer. +SAFEBROWSING_API_KEY = '' + +# For the other possible values of the two settings below, please refer to +# the Safe Browsing API reference at: +# https://developers.google.com/safe-browsing/v4/reference/rest + +# Platforms to be looked at in Safe Browsing (PlatformType). +SAFEBROWSING_PLATFORMS = ['ANY_PLATFORM'] + +# Types to be looked at in Safe Browsing (ThreatType). +SAFEBROWSING_THREATTYPES = ['MALWARE'] + +#-- hashR integration --# +# https://github.com/google/hashr +# Uncomment and fill this section if you want to use the hashR lookup analyzer. +# Provide hashR postgres database connection information below: +# HASHR_DB_USER = 'hashRuser' +# HASHR_DB_PW = 'hashRpass' +# HASHR_DB_ADDR = '127.0.0.1' +# HASHR_DB_PORT = '5432' +# HASHR_DB_NAME = 'hashRdb' + +# The total number of unique hashes that are checked against the database is +# split into multiple batches. This number defines how many unique hashes are +# checked per query. 50000 is the default value. +# HASHR_QUERY_BATCH_SIZE = '50000' + +# Set as True if you want to add the source of the hash ([repo:imagename]) as +# an attribute to the event. WARNING: This will increase the processing time +# of the analyzer! +# HASHR_ADD_SOURCE_ATTRIBUTE = True + +# Threatintel Yeti analyzer-specific configuration +# https://yeti-platform.io/ +# URI root to Yeti's API, e.g. 'https://localhost:8000' +YETI_API_ROOT = '' + +# API key to authenticate requests +YETI_API_KEY = '' + +# Path to a TLS certificate that can be used to authenticate servers +# using self-signed certificates. Provide the full path to the .crt file. +YETI_TLS_CERTIFICATE = None + +# Labels to narrow down indicator selection +YETI_INDICATOR_LABELS = ['domain'] + +# Enable loading DFIQ templates from a Yeti instance. +# This requires DFIQ_ENABLED to be True and YETI_API_ROOT/YETI_API_KEY to be set. +# Yeti DFIQ entries will overwrite local entries if they share the same UUID! +YETI_DFIQ_ENABLED = False + +# Yeti instance web base URL used for links in the UI. +# (Falls back to YETI_API_ROOT if not set.) +YETI_WEB_ROOT = '' + +# Url to MISP instance +MISP_URL = '' + +# API key to authenticate requests +MISP_API_KEY = '' + +# Url to Hashlookup instance +HASHLOOKUP_URL = '' + +# GeoIP Analyzer Settings +# +# Disclaimer: Please note that the geolocation results obtained from this analyzer +# are indicative and based upon the accuracy of the configured datasource. +# This analyzer uses GeoLite2 data created by MaxMind, available from +# https://maxmind.com. + +# The path to a MaxMind GeoIP database +MAXMIND_DB_PATH = '' + +# The Account ID to access a MaxMind GeoIP web service +MAXMIND_WEB_ACCOUNT_ID = '' + +# The license key to access a MaxMind GeoIP web service +MAXMIND_WEB_LICENSE_KEY = '' + +# The host URL of a MaxMind GeoIP web service +MAXMIND_WEB_HOST = '' + +#------------------------------------------------------------------------------- +# Enable experimental UI features. + +ENABLE_EXPERIMENTAL_UI = False + +#------------------------------------------------------------------------------- +# Email notifications. + +ENABLE_EMAIL_NOTIFICATIONS = False +EMAIL_DOMAIN = 'localhost' +EMAIL_FROM_USER = 'nobody' +EMAIL_SMTP_SERVER = 'localhost' + +# Only send emails to these users. +EMAIL_RECIPIENTS = [] + +# Configuration to construct URLs for resources. +EXTERNAL_HOST_URL = 'https://localhost' + +# SSL/TLS support for emails +EMAIL_TLS = False +EMAIL_SSL = False + +# Email support for authentication +EMAIL_AUTH_USERNAME = "" +EMAIL_AUTH_PASSWORD = "" + +#------------------------------------------------------------------------------- +# Sigma Settings + +SIGMA_CONFIG = f'{os.environ["TIMESKETCH_CONF_DIR"]}/sigma_config.yaml' +SIGMA_TAG_DELAY = 5 + +#------------------------------------------------------------------------------- +# Flask Settings +# Everything mentioned in https://flask-wtf.readthedocs.io/en/latest/config/ can be used. +# Max age in seconds for CSRF tokens. Default is 3600. If set to None, the CSRF token is valid for the life of the session. +# WTF_CSRF_TIME_LIMIT = 7200 +WTF_CSRF_ENABLED = False # Set this to False for UI-development purposes + +# The number of proxies that are chained in front of the application. +# This is used by the ProxyFix middleware to trust the X-Forwarded-* headers. +# See: https://werkzeug.palletsprojects.com/en/stable/middleware/proxy_fix/ +REVERSE_PROXY_COUNT = 1 + +# Increase memory limit for form fields to support large legacy client uploads. +# Default: 200MB chunks +MAX_FORM_MEMORY_SIZE = 209715200 + +#------------------------------------------------------------------------------- +# DFIQ - Digital Forensics Investigation Questions +# How to set-up DFIQ: https://timesketch.org/guides/admin/load-dfiq/ +DFIQ_ENABLED = False +DFIQ_PATH = f'{os.environ["TIMESKETCH_CONF_DIR"]}/dfiq/' + +# Intelligence tag metadata configuration +INTELLIGENCE_TAG_METADATA = f'{os.environ["TIMESKETCH_CONF_DIR"]}/intelligence_tag_metadata.yaml' + +# Context links configuration +CONTEXT_LINKS_CONFIG_PATH = f'{os.environ["TIMESKETCH_CONF_DIR"]}/context_links.yaml' + +# LLM provider configs +LLM_PROVIDER_CONFIGS = { + # Configure a LLM provider for a specific LLM enabled feature, or the + # default provider will be used. + # Supported LLM Providers: + # - ollama: Self-hosted, open-source. + # To use the Ollama provider you need to download and run an Ollama server. + # See instructions at: https://ollama.ai/ + # - vertexai: Google Cloud Vertex AI. Requires Google Cloud Project. + # To use the Vertex AI provider you need to: + # 1. Create and export a Service Account Key from the Google Cloud Console. + # 2. Set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the full path + # to your service account private key file by adding it to the docker-compose.yml + # under environment: + # GOOGLE_APPLICATION_CREDENTIALS=/usr/local/src/timesketch/.json + # 3. Install the python libraries: $ pip3 install google-cloud-aiplatform + # - secgemini_log_analyzer_agent: SecGemini Log Analyzer Agent. + # To use the SecGemini Log Analyzer Agent you need to: + # 1. Obtain an API key from https://secgemini.com/ + # 2. pip3 install sec-gemini + # IMPORTANT: Private keys must be kept secret. If you expose your private key it is + # recommended to revoke it immediately from the Google Cloud Console. + # - aistudio: Google AI Studio (API key). Get API key from Google AI Studio website. + # To use Google's AI Studio simply obtain an API key from https://aistudio.google.com/ + # $ pip3 install google-generativeai + 'nl2q': { + 'vertexai': { + 'model': 'gemini-2.0-flash', + 'project_id': '', + }, + }, + 'llm_summarize': { + 'aistudio': { + 'model': 'gemini-2.0-flash', + 'api_key': '', + }, + }, + 'llm_synthesize': { + 'aistudio': { + 'model': 'gemini-2.0-flash', + 'api_key': '', + }, + }, + 'log_analyzer': + { + # NOTE: This feature will not work with default LLM providers and requires + # a dedicated log analyzer agent service! + # Read more: https://timesketch.org/developers/log-analyzer-agent/ + # Did you install `pip3 install sec-gemini` in your Timesketch containers? + 'secgemini_log_analyzer_agent': { + 'logs_processor_api_url': '', + 'api_key': '', + 'model': 'logs_analysis_agent-1.1', + 'base_url': '', + 'wss_url': '', + # Configuration for individual agents. This is a dictionary where the + # key is the agent name and the value is another dictionary with + # agent-specific configuration parameters. + # Example: + # 'agents_config': { + # 'logs_analysis_loop_agent': { + # 'max_iterations': 10, + # } + 'agents_config': {}, + } + }, + 'default': { + 'ollama': { + 'server_url': '', + 'model': '', + }, + } +} + + +# LLM nl2q configuration +DATA_TYPES_PATH = f'{os.environ["TIMESKETCH_CONF_DIR"]}/nl2q/data_types.csv' +PROMPT_NL2Q = f'{os.environ["TIMESKETCH_CONF_DIR"]}/nl2q/prompt_nl2q' +EXAMPLES_NL2Q = f'{os.environ["TIMESKETCH_CONF_DIR"]}/nl2q/examples_nl2q' + +# LLM event summarization configuration +PROMPT_LLM_SUMMARIZATION = f'{os.environ["TIMESKETCH_CONF_DIR"]}/llm_summarize/prompt.txt' +PROMPT_LLM_SYNTHESIZE = f'{os.environ["TIMESKETCH_CONF_DIR"]}/llm_summarize/prompt_llm_synthesize.txt' + +# LLM log_analyzer default prompt +LLM_LOG_ANALYZER_DEFAULT_PROMPT = ( + "Perform a forensics investigation on the provided logs. Determine if the " + "host has been compromised, and if so, reconstruct the complete attacker " + "timeline, from initial compromise to actions on objectives." +) + +#------------------------------------------------------------------------------- +# Timesketch UI Option + +# Get the search processing timelines setting. +# If set to True, the search processing timelines options will be displayed in the UI. +SEARCH_PROCESSING_TIMELINES = True + +# Enable the investigation view running on the v3 frontend. +# Set-up instructions: https://timesketch.org/guides/admin/investigation-view-setup/ +ENABLE_V3_INVESTIGATION_VIEW = False diff --git a/contrib/docker/dev/timesketch/timesketch.env b/contrib/docker/dev/timesketch/timesketch.env new file mode 100644 index 0000000000..126ca4d9cd --- /dev/null +++ b/contrib/docker/dev/timesketch/timesketch.env @@ -0,0 +1,12 @@ +TIMESKETCH_USER="${TIMESKETCH_USER?-}" +TIMESKETCH_PASSWORD="${TIMESKETCH_PASSWORD?-}" + +SECRET_KEY="${TIMESKETCH_SECRET_KEY?-}" +POSTGRES_USER="${POSTGRES_USER?-}" +POSTGRES_PASSWORD="${POSTGRES_PASSWORD?-}" +POSTGRES_DB="${POSTGRES_DB?-}" + +CHOKIDAR_USEPOLLING="true" +prometheus_multiproc_dir="/tmp/" + +BACKEND_URL="http://gunicorn:5000/" diff --git a/contrib/docker/dev/timesketch/vue-cli-service-docker-entrypoint.sh b/contrib/docker/dev/timesketch/vue-cli-service-docker-entrypoint.sh new file mode 100644 index 0000000000..41d2185498 --- /dev/null +++ b/contrib/docker/dev/timesketch/vue-cli-service-docker-entrypoint.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -e + +cd "/usr/local/src/timesketch/timesketch/${FRONTEND_VERSION:?}" + +yarn install + +if [[ "${FRONTEND_VERSION}" = "frontend" || "${FRONTEND_VERSION}" = "frontend-ng" ]]; then + exec yarn run serve +elif [[ "${FRONTEND_VERSION}" = "frontend-v3" ]]; then + exec yarn dev +else + echo "Unknown FRONTEND_VERSION value: \"${FRONTEND_VERSION}\"." >&2 + exit 1 +fi diff --git a/timesketch/frontend-ng/vue.config.js b/timesketch/frontend-ng/vue.config.js index 8a9310b853..94fb09e85e 100644 --- a/timesketch/frontend-ng/vue.config.js +++ b/timesketch/frontend-ng/vue.config.js @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +const backendUrl = process.env.BACKEND_URL || 'http://localhost:5000/'; + module.exports = { lintOnSave: false, publicPath: process.env.NODE_ENV === 'development' ? '/' : '/dist/', @@ -30,15 +32,15 @@ module.exports = { proxy: { '^/api': { autoRewrite: true, - target: 'http://localhost:5000/', + target: backendUrl, }, '^/dist': { autoRewrite: true, - target: 'http://localhost:5000/', + target: backendUrl, }, '^/login|logout': { autoRewrite: true, - target: 'http://localhost:5000/', + target: backendUrl, }, }, }, diff --git a/timesketch/frontend-v3/vite.config.mjs b/timesketch/frontend-v3/vite.config.mjs index 6a789afd9f..f92ea15676 100644 --- a/timesketch/frontend-v3/vite.config.mjs +++ b/timesketch/frontend-v3/vite.config.mjs @@ -11,6 +11,8 @@ import Vuetify, { transformAssetUrls } from "vite-plugin-vuetify"; import { defineConfig } from "vite"; import { fileURLToPath, URL } from "node:url"; +const backendUrl = process.env.BACKEND_URL || "http://localhost:5000/"; + // https://vitejs.dev/config/ export default defineConfig({ base: process.env.NODE_ENV === "development" ? "/" : "/v3/", @@ -58,12 +60,12 @@ export default defineConfig({ port: 5001, proxy: { "^/api": { - target: "http://127.0.0.1:5000/", + target: backendUrl, changeOrigin: true, secure: false, }, '^/login|logout': { - target: 'http://127.0.0.1:5000/', + target: backendUrl, changeOrigin: true, secure: false, }, diff --git a/timesketch/frontend/vue.config.js b/timesketch/frontend/vue.config.js index 601ffdfb99..c79e514427 100644 --- a/timesketch/frontend/vue.config.js +++ b/timesketch/frontend/vue.config.js @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +const backendUrl = process.env.BACKEND_URL || 'http://localhost:5000/'; + module.exports = { lintOnSave: false, publicPath: process.env.NODE_ENV === 'development' ? '/' : '/legacy/dist/', @@ -30,15 +32,15 @@ module.exports = { proxy: { '^/api': { autoRewrite: true, - target: 'http://localhost:5000/', + target: backendUrl, }, '^/dist': { autoRewrite: true, - target: 'http://localhost:5000/', + target: backendUrl, }, '^/login|logout': { autoRewrite: true, - target: 'http://localhost:5000/', + target: backendUrl, }, }, },