Site icon Planned Link

Deploying the Elastic Stack in an Air-Gapped environment – Part 1

Introduction and Preparation

In this series of blog posts, I will guide you through the first steps required to install the Elastic Stack—consisting of Elasticsearch, Logstash, Kibana and Fleet managed agents —in an air-gapped environment. By the end, you’ll be equipped with the knowledge to successfully deploy the Elastic Stack in isolated environments while ensuring the integrity and security of your deployment.

Why use Air-Gapped Networks?

Air-gapped environments serve as high-security fortresses in the digital world—completely isolated from external networks and the internet. This physical or logical separation drastically reduces the risk of remote attacks, data exfiltration, and lateral threat movement. Common in military, industrial, and SOC operations, they offer robust control, compliance advantages, and strategic resilience against cyber threats.

Prerequisites

For the purpose of this blog post I will be using Ubuntu Server 24.04.1, with Docker hosting the Elastic Package Registry, if you choose another OS ensure you consult the Elastic Support Matrix. I am assuming you have internet connected device to gather the required packages and the means to securely transfer them to your air-gapped environment.

Lab Set Up

Server 1 – Elastic, Kibana, EPR, Fleet Server, Agent Binaries.

Server 2 – Logstash (Optional)

Required Packages

The following packages must be gathered from your internet connected device and transferred to your air gapped environment:

Use the links below to download your preferred versions of Elasticsearch, Logstash (optional) and Kibana.

Elasticsearch | Logstash | Kibana

Agent Binaries can be downloaded individually using this link, I prefer to use the following script which downloads all beats and agent files, putting them in the correct directories. Simply replace X.X.X with your desired version and input your the output directory. You probably won’t need the majority of these files, especially for this blog series, but it’s nice to have them just in case (especially when air gapped).

#!/usr/bin/env bash
set -o nounset -o errexit -o pipefail

STACK_VERSION=X.X.X
ARTIFACT_DOWNLOADS_BASE_URL=https://artifacts.elastic.co/downloads

DOWNLOAD_BASE_DIR=<your-output-directory> 

COMMON_PACKAGE_PREFIXES="apm-server/apm-server beats/auditbeat/auditbeat beats/elastic-agent/elastic-agent beats/filebeat/filebeat beats/heartbeat/heartbeat beats/metricbeat/metricbeat beats/osquerybeat/osquerybeat beats/packetbeat/packetbeat cloudbeat/cloudbeat endpoint-dev/endpoint-security fleet-server/fleet-server"

WIN_ONLY_PACKAGE_PREFIXES="beats/winlogbeat/winlogbeat"

RPM_PACKAGES="beats/elastic-agent/elastic-agent"
DEB_PACKAGES="beats/elastic-agent/elastic-agent"

function download_packages() {
  local url_suffix="$1"
  local package_prefixes="$2"

  local _url_suffixes="$url_suffix ${url_suffix}.sha512 ${url_suffix}.asc"
  local _pkg_dir=""
  local _dl_url=""

  for _download_prefix in $package_prefixes; do
    for _pkg_url_suffix in $_url_suffixes; do
          _pkg_dir=$(dirname ${DOWNLOAD_BASE_DIR}/${_download_prefix})
          _dl_url="${ARTIFACT_DOWNLOADS_BASE_URL}/${_download_prefix}-${_pkg_url_suffix}"
          (mkdir -p $_pkg_dir && cd $_pkg_dir && curl -O "$_dl_url")
    done
  done
}

# and we download
for _os in linux windows; do
  case "$_os" in
    linux)
      PKG_URL_SUFFIX="${STACK_VERSION}-${_os}-x86_64.tar.gz"
      ;;
    windows)
      PKG_URL_SUFFIX="${STACK_VERSION}-${_os}-x86_64.zip"
      ;;
    *)
      echo "[ERROR] Something happened"
      exit 1
      ;;
  esac

  download_packages "$PKG_URL_SUFFIX" "$COMMON_PACKAGE_PREFIXES"

  if [[ "$_os" = "windows" ]]; then
    download_packages "$PKG_URL_SUFFIX" "$WIN_ONLY_PACKAGE_PREFIXES"
  fi

  if [[ "$_os" = "linux" ]]; then
    download_packages "${STACK_VERSION}-x86_64.rpm" "$RPM_PACKAGES"
    download_packages "${STACK_VERSION}-amd64.deb" "$DEB_PACKAGES"
  fi
done

Elastic Package Registry

Run the following docker commands on an internet connected device (if using podman, simply replace “docker” with “podman”):

#replace X.X.X with your desired version

docker pull docker.elastic.co/package-registry/distribution:X.X.X 
#used to pull latest version

docker save -o package-registry-X.X.X.tar docker.elastic.co/package-registry/distribution:X.X.X 
#save epr to transfer

Moving the files

Transfer all files to your air gapped network and add them to relevant servers.

If you are using separate servers for Elasticsearch, Kibana and Logstash, I recommend co-locating the EPR with the Kibana server.

The agent binaries can be hosted in your repo if you have one, if not, for continuity, place them on the Kibana server and we will configure a simple python http server in later steps.

Create a directory called agents:

mkdir agents

Move all agent binaries to the agents folder, ensuring they remain in the same folder structure the script put them in.

beats/elastic-agent/elastic-agent-9.1.1-linux-x86_64.tar.gz

Next time…

That’s the prep out of the way! In part 2, we will be installing Elasticsearch and Kibana.

Exit mobile version