Security Azure

Timesketch on Ubuntu 24.04 on Azure User Guide

| Product: Timesketch on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of Timesketch on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Timesketch is Google's open source platform for collaborative forensic timeline analysis. Investigators load timelines from evidence into a shared sketch, then search, filter and pivot across millions of combined events at once, recording findings on the timeline itself with tags, stars, comments and annotations. Saved views capture a line of enquiry so it can be handed over, and stories turn a sequence of events into a written narrative with the underlying evidence still attached.

The cloudimg image ships the free and open source, Apache-2.0 licensed Timesketch, deployed the officially supported way as the upstream container stack. The Timesketch web application, the Celery ingest worker, OpenSearch, PostgreSQL, Redis and nginx are all captured into the VM, so your instance starts in minutes rather than after an afternoon of orchestration. Because this platform holds evidence, nothing ships with a known secret: the administrator account, the database password, the Flask signing key and the task queue password are generated for each VM on first boot, before the application is reachable. The search cluster, database and task queue are bound to the internal container network only and are never reachable from the internet. Backed by 24/7 cloudimg support.

Timesketch, OpenSearch, PostgreSQL and Redis are trademarks of their respective owners. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by any of them. It ships the free and open source Apache-2.0 licensed self hosted software, unmodified.

The docker and timesketch services reported active, and all six containers running: the Timesketch web application, the ingest worker, OpenSearch, PostgreSQL, Redis and nginx

What is included:

  • Timesketch (Apache-2.0), pinned to the upstream release the image was built from
  • The Timesketch Celery ingest worker, which processes plaso, CSV and JSONL timelines
  • OpenSearch as the event search cluster, sized automatically from the VM's memory
  • PostgreSQL for sketches, timelines, annotations and access control
  • Redis as the task queue broker, password protected per instance
  • nginx as the single public front door on port 80
  • A per instance administrator account, generated on first boot

Prerequisites

  • Active Azure subscription, SSH public key, VNet + subnet in target region
  • Subscription to the Timesketch listing on Azure Marketplace

Recommended virtual machine size: Standard_B4ms (4 vCPU, 16 GB RAM). Upstream Timesketch documents a minimum of 8 GB RAM, and OpenSearch is given half the VM's memory as its heap, so 16 GB is a comfortable working size for real casework. For large plaso ingests or several concurrent analysts use Standard_D8s_v5 or larger. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web interface from the networks your analysts use.

Step 1: Deploy from the Azure Portal

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Timesketch by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create and Create.

Step 2: Deploy from the Azure CLI

RG="timesketch-prod"; LOCATION="eastus"; VM_NAME="timesketch-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/timesketch-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name ts-vnet --address-prefix 10.100.0.0/16 --subnet-name ts-subnet --subnet-prefix 10.100.1.0/24
az network nsg create -g "$RG" --name ts-nsg
az network nsg rule create -g "$RG" --nsg-name ts-nsg --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name ts-nsg --name allow-http --priority 110 \
  --destination-port-ranges 80 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
  --size Standard_B4ms --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name ts-vnet --subnet ts-subnet --nsg ts-nsg --public-ip-sku Standard

Step 3: Connect to your VM

ssh azureuser@<vm-ip>

Step 4: Confirm the services are running

The platform runs as six containers under one timesketch.service. On the very first boot, timesketch-firstboot.service generates this VM's secrets and only then releases the application to start, so allow a few minutes after launch before the web interface answers.

sudo systemctl is-active docker timesketch
sudo docker compose --project-directory /opt/timesketch ps

Both services report active, and all six containers report running. Only nginx publishes a port to the host; OpenSearch, PostgreSQL and Redis have no host port mapping at all and are reachable solely on the internal container network.

Step 5: Read the per instance credentials

The administrator account, the PostgreSQL password, the Flask signing key and the Redis password were all generated for this VM on its first boot, before the application was reachable, and written to a root only file:

sudo ls -l /root/timesketch-credentials.txt
sudo cat /root/timesketch-credentials.txt

The file (mode 0600 root:root) holds TIMESKETCH_URL, TIMESKETCH_USERNAME and TIMESKETCH_PASSWORD. None of these values ship in the image. The image contains no database, no search index and no configured secret at all: the live configuration is deleted at build time and rebuilt from secret free templates on first boot, so no two instances share a credential and there is no default login to change.

The timesketch-credentials.txt file listed at mode 0600 root root, alongside the credential key names only with their values withheld, and the running configuration files also at 0600

Step 6: Verify the security model

Because Timesketch holds evidence, the most important property of this image is that the search cluster is not reachable from outside the VM. Upstream runs OpenSearch with its security plugin disabled, which is safe only because the port is never published. Confirm that for yourself: list every listening socket, then try to reach OpenSearch on the VM's own routable address.

sudo ss -ltnp | awk '{printf "%-24s %s\n", $4, $6}'
HOST_IP=$(hostname -I | awk '{print $1}')
timeout 5 bash -c "</dev/tcp/$HOST_IP/80"  && echo "port 80   (web interface): reachable" || echo "port 80: unreachable"
timeout 5 bash -c "</dev/tcp/$HOST_IP/9200" && echo "port 9200 (OpenSearch): REACHABLE - unexpected" || echo "port 9200 (OpenSearch): refused, as intended"
timeout 5 bash -c "</dev/tcp/$HOST_IP/5432" && echo "port 5432 (PostgreSQL): REACHABLE - unexpected" || echo "port 5432 (PostgreSQL): refused, as intended"

Only 22 (SSH) and 80 (the web interface) are bound to a routable address. OpenSearch, PostgreSQL and Redis refuse connections because they are published to no host port whatsoever. The reachable port 80 result is the control that proves the test itself works: a probe that could not connect to anything would otherwise "prove" every port closed.

Listening sockets showing only ports 22 and 80 bound to a routable address, with the connection test confirming port 80 reachable while OpenSearch on 9200 and PostgreSQL on 5432 both refuse

OpenSearch is genuinely running and healthy; it is simply reachable only from sibling containers. Confirm both facts, and that Redis rejects an unauthenticated command:

sudo docker compose --project-directory /opt/timesketch exec -T nginx wget -qO- http://opensearch:9200/_cluster/health
sudo docker compose --project-directory /opt/timesketch exec -T redis redis-cli ping 2>&1 | head -1

The cluster reports a green or yellow status from inside the container network, and Redis answers NOAUTH Authentication required because this VM's task queue password is required.

OpenSearch reporting a healthy cluster status from inside the container network, and Redis rejecting an unauthenticated ping with NOAUTH Authentication required

Step 7: Sign in to the web interface

Browse to http://<vm-ip>/ to reach Timesketch. Sign in with the TIMESKETCH_USERNAME and TIMESKETCH_PASSWORD from Step 5.

The Timesketch sign in page served over the VM's public address, with the per instance username entered and the password field completed

Step 8: Create a sketch

A sketch is the shared workspace for an investigation: it holds one or more timelines plus every annotation your team makes on them. From the landing page choose to create a new sketch and give it a name that identifies the case.

The Timesketch landing page after signing in, offering to start a new investigation with a blank sketch and listing recent sketches with their creator and activity

Step 9: Ingest a timeline

Timesketch accepts plaso storage files (the output of log2timeline), and CSV or JSONL timelines. A CSV needs at minimum a datetime, a timestamp_desc and a message column; any further columns, such as hostname, become searchable fields in their own right. Upload the file into the sketch and the ingest worker indexes it in the background.

The Add timeline dialog inside a sketch, prompting for a Plaso, JSONL or CSV file to upload into the investigation

Step 10: Explore and search events

Open the timeline to explore it. Search across every ingested event with free text, or query a specific field such as hostname:WIN-FORENSIC-01. Results can be filtered by time range, starred, tagged and commented on, and a search worth keeping can be saved as a view.

The Timesketch explore view after searching, reporting 1 to 3 of 3 events and listing the ingested forensic events in time order with their UTC timestamps and messages, including the rundll32.exe process launch and the Run key persistence event

You can also confirm an ingest from the command line. This creates a sketch, uploads a small timeline and reads the events back:

sudo /usr/local/sbin/timesketch-selftest.sh

Step 11: Managing the service

sudo systemctl status timesketch --no-pager
sudo systemctl restart timesketch
sudo docker compose --project-directory /opt/timesketch logs --tail=50 timesketch-worker

The whole stack is managed as a single unit. timesketch.service brings the containers up and down together; the ingest worker's log is the place to look if a timeline does not appear after upload.

Step 12: How OpenSearch is sized

OpenSearch is given half of the VM's memory as its JVM heap, following upstream's own sizing rule, and this is calculated on first boot from the machine you actually launched rather than fixed when the image was built. A Standard_B4ms gives it an 8 GB heap; a larger VM gives it proportionally more, capped at 31 GB. To see what this instance chose:

sudo grep -E 'OPENSEARCH_MEM_USE_GB|NUM_WSGI_WORKERS' /opt/timesketch/config.env
sudo docker inspect opensearch --format '{{range .Config.Env}}{{println .}}{{end}}' | grep JAVA_OPTS
free -g | head -2

The configuration file is 0600 root:root because it carries this VM's database and task queue passwords, so these reads need sudo. The reported OPENSEARCH_MEM_USE_GB and the JVM's -Xms/-Xmx will agree with each other and with half the memory shown by free.

If you resize the VM later and want the heap recalculated to match, regenerate the configuration and restart:

sudo /usr/local/sbin/timesketch-gen-env.sh
sudo systemctl restart timesketch

Step 13: Add a user

Timesketch accounts are managed with tsctl. Add a colleague to this instance:

sudo docker compose --project-directory /opt/timesketch exec -T timesketch-web tsctl create-user analyst2

You are prompted for a password. Sketches are access controlled, so a new user sees only sketches shared with them; share a sketch from its Sharing menu in the web interface.

Step 14: Use your own domain and HTTPS (production)

The image serves plain HTTP on port 80, which is appropriate for a private network but not for evidence crossing an untrusted one. Before you upload real case data, put Timesketch behind TLS. Upstream documents the supported approach at timesketch.org/guides/admin/https; in short, point a DNS record at the VM, obtain a certificate, and add a TLS server block to /opt/timesketch/etc/nginx.conf listening on 443. Restart with sudo systemctl restart timesketch and restrict port 80 in your NSG once 443 is serving.

Step 15: Security recommendations

  • Put TLS in front of it before ingesting real evidence (Step 14). Timesketch serves plain HTTP by default.
  • Restrict port 80/443 in your NSG to the networks your analysts work from; this is not a service to expose to the public internet.
  • Change the first boot administrator password after your first sign in, from the user menu.
  • Keep OpenSearch unpublished. It runs with its security plugin disabled, so publishing port 9200 would expose every ingested event without authentication. Step 6 shows how to verify it is not.
  • Take disk snapshots of the VM; sketches, annotations and the search index all live on the OS disk.
  • Give each analyst their own account (Step 13) so annotations are attributed.

Step 16: Support and Licensing

Timesketch is free and open source software licensed under the Apache License 2.0. This cloudimg image bundles it unmodified, with 24/7 cloudimg support for the image and its deployment. For questions about Timesketch itself, see the upstream documentation and GitHub project.

Deploy on Azure

Find Timesketch on Ubuntu 24.04 LTS on the Azure Marketplace and deploy it in minutes with 24/7 cloudimg support.