Simplify installer and update CI (#95)

* simplified installer, removed ansible and remote installs, updated ci

* Update ci.yml
This commit is contained in:
Liza Tsibur
2026-03-26 08:56:02 -05:00
committed by GitHub
parent 736462070c
commit 309171c9aa
24 changed files with 750 additions and 1738 deletions
+18 -32
View File
@@ -1,61 +1,47 @@
name: Build Docker Images
name: Build Docker Image
# on: [pull_request,workflow_dispatch]
# pull_request: # TODO: delete this line
# TODO: comment this back in
on:
push:
branches:
- "main"
- "develop"
tags:
- "v*.*.*"
workflow_dispatch:
jobs:
build-image:
name: "Build Docker Images"
name: Build and Push Docker Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
# list of Docker images to use as base name for tags
images: |
ghcr.io/activecm/rita
# generate Docker tags based on the following events/attributes
images: ghcr.io/activecm/rita
tags: |
type=schedule
type=ref,event=branch
type=ref,event=pr
type=ref,event=tag
type=semver,pattern={{raw}}
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=sha
type=sha
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64,linux/arm64
# TODO: comment this back in
push: ${{ github.event_name != 'pull_request' }}
# push: true
push: true
tags: ${{ steps.meta.outputs.tags }}
+184
View File
@@ -0,0 +1,184 @@
name: CI
on:
pull_request:
push:
branches:
- main
- develop
tags-ignore:
- "v*"
workflow_call:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24"
- name: Mod tidy
run: |
go mod tidy
git diff --exit-code
- name: Verify dependencies
run: go mod verify
- name: Build
run: make build
unit-tests:
name: Unit Tests
runs-on:
labels: ubuntu-latest-m
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24"
- name: Download dependencies
run: go mod download
- name: Install tparse
run: go install github.com/mfridman/tparse@latest
- name: Run unit tests
run: go test --cover $(go list ./... | grep -v /integration | grep -v /database | grep -v /cmd | grep -v /viewer) -json 2>&1 | tee unit.out && tparse -all --file=unit.out
- name: Upload test output on failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: unit-test-output
path: unit.out
integration-tests:
name: Integration Tests
runs-on:
labels: ubuntu-latest-m
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24"
- name: Download dependencies
run: go mod download
- name: Install tparse
run: go install github.com/mfridman/tparse@latest
- name: Run integration tests
shell: 'script -q -e -c "bash {0}"'
run: go test ./integration/... -json -timeout 1800s 2>&1 | tee integration.out && tparse -all --file=integration.out
- name: Upload test output on failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: integration-test-output
path: integration.out
database-tests:
name: Database Tests
runs-on:
labels: ubuntu-latest-m
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24"
- name: Download dependencies
run: go mod download
- name: Install tparse
run: go install github.com/mfridman/tparse@latest
- name: Run database tests
shell: 'script -q -e -c "bash {0}"'
run: go test ./database/... -json -timeout 1800s 2>&1 | tee database.out && tparse -all --file=database.out
- name: Upload test output on failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: database-test-output
path: database.out
cmd-tests:
name: Cmd Tests
runs-on:
labels: ubuntu-latest-m
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24"
- name: Download dependencies
run: go mod download
- name: Install tparse
run: go install github.com/mfridman/tparse@latest
- name: Run cmd tests
shell: 'script -q -e -c "bash {0}"'
run: go test ./cmd/... -json -timeout 1800s 2>&1 | tee cmd.out && tparse -all --file=cmd.out
- name: Upload test output on failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: cmd-test-output
path: cmd.out
viewer-tests:
name: Viewer Tests
runs-on:
labels: ubuntu-latest-m
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24"
- name: Download dependencies
run: go mod download
- name: Install tparse
run: go install github.com/mfridman/tparse@latest
- name: Run viewer tests
shell: 'script -q -e -c "bash {0}"'
run: go test ./viewer/... -json -timeout 1800s 2>&1 | tee viewer.out && tparse -all --file=viewer.out
- name: Upload test output on failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: viewer-test-output
path: viewer.out
+70 -29
View File
@@ -1,37 +1,78 @@
name: Generate Installer
name: Release
on:
release:
types:
- published
#- unpublished
- created
- edited
#- deleted
- prereleased
- released
push:
tags:
- '*'
workflow_run:
workflows: ["Build Docker Images"]
branches: [main, develop]
types:
- completed
- "v*"
jobs:
upload-installer:
name: Upload Installer to Release
runs-on: ubuntu-22.04
ci:
name: CI
uses: ./.github/workflows/ci.yml
build-image:
name: Build and Push Docker Image
needs: ci
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- run: installer/generate_installer.sh
- uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@v6
with:
files: |
installer/rita-${{ github.ref_name }}.tar.gz
installer/install-rita-zeek-here.sh
fetch-depth: 0
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/activecm/rita
tags: |
type=semver,pattern={{raw}}
type=raw,value=latest,enable=${{ !contains(github.ref_name, '-') }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
generate-installer:
name: Generate Installer
needs:
- ci
- build-image
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Generate installer
run: installer/generate_installer.sh
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
prerelease: ${{ contains(github.ref_name, '-') }}
generate_release_notes: true
files: installer/rita-${{ github.ref_name }}.tar.gz
-140
View File
@@ -1,140 +0,0 @@
name: Tests
on:
pull_request:
push:
branches:
- 'main'
- 'develop'
# Temporarily splitting out tests into separate jobs for improving workflow execution time
jobs:
unit_test:
name: 'Run Unit Tests'
runs-on:
labels: ubuntu-latest-m
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
- name: Install dependencies
run: go get .
- name: Build
run: go build -v ./...
- name: Install tparse
run: go install github.com/mfridman/tparse@latest
- name: Unit Tests
run: go test --cover $(go list ./... | grep -v /integration | grep -v /database | grep -v /cmd | grep -v /viewer ) -json 2>&1 | tee unit.out && tparse -all --file=unit.out
- name: Upload test output on failure
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: media
path: unit.out
integration_test:
name: 'Run Integration Tests'
runs-on:
labels: ubuntu-latest-m
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
- name: Install dependencies
run: go get .
- name: Build
run: go build -v ./...
- name: Install tparse
run: go install github.com/mfridman/tparse@latest
- name: Integration Tests
shell: 'script -q -e -c "bash {0}"'
run: go test ./integration/... -json -timeout 1800s 2>&1 | tee integration.out && tparse -all --file=integration.out
- name: Upload test output on failure
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: media
path: integration.out
database_test:
name: 'Run Database Tests'
runs-on:
labels: ubuntu-latest-m
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
- name: Install dependencies
run: go get .
- name: Build
run: go build -v ./...
- name: Install tparse
run: go install github.com/mfridman/tparse@latest
- name: Database Tests
shell: 'script -q -e -c "bash {0}"'
run: go test ./database/... -json -timeout 1800s 2>&1 | tee database.out && tparse -all --file=database.out
- name: Upload test output on failure
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: media
path: database.out
cmd_test:
name: 'Run Cmd Tests'
runs-on:
labels: ubuntu-latest-m
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
- name: Install dependencies
run: go get .
- name: Build
run: go build -v ./...
- name: Install tparse
run: go install github.com/mfridman/tparse@latest
- name: Cmd Tests
shell: 'script -q -e -c "bash {0}"'
run: go test ./cmd/... -json -timeout 1800s 2>&1 | tee cmd.out && tparse -all --file=cmd.out
- name: Upload test output on failure
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: media
path: cmd.out
viewer_test:
name: 'Run Viewer Tests'
runs-on:
labels: ubuntu-latest-m
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
- name: Install dependencies
run: go get .
- name: Build
run: go build -v ./...
- name: Install tparse
run: go install github.com/mfridman/tparse@latest
- name: Viewer Tests
shell: 'script -q -e -c "bash {0}"'
run: go test ./viewer/... -json -timeout 1800s 2>&1 | tee viewer.out && tparse -all --file=viewer.out
- name: Upload test output on failure
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: media
path: viewer.out
+2 -3
View File
@@ -43,9 +43,8 @@
!/test_data/missing_host/*/*[.log.gz]
!/test_data/open_sni/*[.log.gz]
deployment/threat_intel_feeds/*
!deployment/threat_intel_feeds/.gitkeep
+24 -3
View File
@@ -5,6 +5,27 @@ CGO_ENABLED ?= 0
GOARCH ?= $(shell go env GOARCH)
GOOS ?= $(shell go env GOOS)
.PHONY: rita
rita:
CGO_ENABLED=$(CGO_ENABLED) GOARCH=$(GOARCH) GOOS=$(GOOS) go build $(LDFLAGS) -o rita
.PHONY: build test test-unit test-integration test-database test-cmd test-viewer clean
build:
CGO_ENABLED=$(CGO_ENABLED) GOARCH=$(GOARCH) GOOS=$(GOOS) go build $(LDFLAGS) -o rita
test: test-unit test-integration test-database test-cmd test-viewer
test-unit:
go test --cover $$(go list ./... | grep -v /integration | grep -v /database | grep -v /cmd | grep -v /viewer)
test-integration:
go test ./integration/... -timeout 1800s
test-database:
go test ./database/... -timeout 1800s
test-cmd:
go test ./cmd/... -timeout 1800s
test-viewer:
go test ./viewer/... -timeout 1800s
clean:
rm -f rita
+4 -18
View File
@@ -19,7 +19,7 @@ The framework ingests [Zeek Logs](https://www.zeek.org/) in TSV or JSON format,
- **Threat Intel Feed Checking**: Query threat intel feeds to search for suspicious domains and hosts
## Quick Start
Please see our recommended [System Requirements](docs/System%20Requirements.md).
Please see our recommended [System Requirements](docs/System%20Requirements.md). RITA requires [Docker Engine](https://docs.docker.com/engine/install/) with the Compose plugin.
1. Download the [RITA Installer](https://github.com/activecm/rita/releases) for the desired version.
@@ -27,15 +27,9 @@ The framework ingests [Zeek Logs](https://www.zeek.org/) in TSV or JSON format,
```
tar -xf rita-<version>-installer.tar.gz
```
3. Run the install script.
3. Run the install script.
```
./rita-<version>-installer/install_rita.sh <hosts to install on>
```
To install RITA on the local system, pass `localhost` to the installer.
To install RITA on one or more remote systems, pass a comma separated list of IPs or `user@ip` or FQDNs to the installer.
For example:
```
./rita-<version>-installer/install_rita.sh "root@4.4.4.4,8.8.8.8,mydomain.com"
./rita-<version>-installer/install_rita.sh
```
@@ -51,15 +45,7 @@ The following operating systems/versions and CPU architectures are supported:
| Ubuntu | `22.04`, `24.04` | `amd64` |
## Installing Zeek
If you do not already have Zeek installed, it can be installed from [docker-zeek](https://github.com/activecm/docker-zeek).
```
sudo wget -O /usr/local/bin/zeek https://raw.githubusercontent.com/activecm/docker-zeek/master/zeek
sudo chmod +x /usr/local/bin/zeek
zeek start
```
RITA requires [Zeek](https://www.zeek.org/) to generate network logs. If you do not already have Zeek installed, install [docker-zeek](https://github.com/activecm/docker-zeek/releases) and run `zeek start`.
## Importing
Import data into RITA using the `import` command:
+1 -1
View File
@@ -4,7 +4,7 @@ networks:
rita-network: {}
services:
rita:
image: ghcr.io/activecm/rita:latest
image: ghcr.io/activecm/rita:${RITA_VERSION:-latest}
build: .
depends_on:
clickhouse:
+1
View File
@@ -4,6 +4,7 @@ networks:
rita-network: {}
services:
rita:
image: ghcr.io/activecm/rita:${RITA_VERSION:-latest}
build: .
depends_on:
clickhouse:
-88
View File
@@ -1,88 +0,0 @@
## RITA/Zeek Installer
#### Generated installer directory
```
rita-<version>.tar.gz
│ install_rita.yml
│ install_rita.sh
| install_zeek.yml
| install_pre.yml
└───/scripts
│ │ ansible-installer.sh
│ │ helper.sh
│ │ sshprep.sh
└───/files
│ │
│ │ rita-<version>-image.tar
│ │ docker-compose
│ │
│ └───/opt
│ │ │ docker-compose.yml
│ │ │ .env
│ │ │ README
│ │ │ LICENSE
│ │ │ rita.sh
| | | zeek
│ │
│ └───/etc
│ │ config.hjson
│ │ config.xml
│ │ http_extensions_list.csv
│ │ logger-cron
│ │ syslog-ng.conf
│ │ timezone.xml
│ └───/threat_intel_feeds
```
### Generating an installer
Note: generating the installer on a branch that has no tag when running `git describe --always --abbrev=0 --tags` will generate a broken installer.
Run:
`./installer/generate_installer.sh`
The script will generate an installer tar file in the `installer` folder, named `rita-v<version number>-installer.tar.gz`.
Verify that all files in the above directory tree exist in the generated tar file.
Verify that all occurences of "REPLACE_ME" within scripts and/or playbooks got updated with the proper version number that is expected.
The version for RITA that gets replaced should match the current tag.
The version for Zeek that gets replaced should be the desired version of docker-zeek to be used in this release.
The docker-zeek repo pushes a built multi-architecture image of zeek to DockerHub using Github Actions. The generate_installer script should specify which tag version on [Dockerhub](https://hub.docker.com/r/activecm/zeek/tags) you wish to include with this release. Multi-architecture tags require all architectures to finish building before being merged into one tag, so if the build actions are in progress, please be patient and wait for them to finish before attempting to install it.
### Running the installer
To install RITA on the current system, run:
`./rita-v<version>-installer/install_rita.sh localhost`.
To install RITA on a remote system, run:
`./rita-v<version>-installer/install_rita.sh root@8.8.8.8`.
### Updating the installer
Each file that is expected to be in the installer must be explicitly copied to the installer within the `./installer/generate_installer.sh` script.
If any new Ansible playbook or script that uses the "REPLACE_ME" string to insert a version is added, the generate_installer script must be updated to replace that string with the proper version.
Any versions for RITA should NOT be hard-coded. The version should be retrieved by the generate_installer script automatically. The only hard-coded versions in the generator should be for external projects.
### "One-line installer"
To make installing both RITA and Zeek easier, a one-line installer is created and uploaded to the release artifacts on Github. This installer is generated with the generate_installer.sh script as well, but is uploaded to the release within the Generate Installer Github Action.
This one line installer is a single script (not a tar file). It installs RITA & Zeek on the local system and does NOT require passing any arguments to it.
### Zeek
There are multiple moving parts in order to build Zeek and include it in a RITA install bundle.
The main Zeek repo is [docker-zeek](https://github.com/activecm/docker-zeek). This repository contains the Dockerfile definition needed to build the docker image of Zeek that includes custom modifications like timeouts and the [zeek-open-connections](https://github.com/activecm/zeek-open-connections) plugin.
The docker-zeek repo is responsible for building the multi-arch image for Zeek in Github Actions. The actions automatically upload the image to Dockerhub. In order to test changes locally without uploading them to Dockerhub, the docker-zeek image must be built on your local system and tagged with a name that is NOT similar to `activecm/zeek:<any version>`. To test the zeek script with this custom-built image, the `zeek` script in the docker-zeek repo must be updated to use your custom tag instead of whatever is listed in the `IMAGE_NAME` variable.
The zeek-open-connections plugin must have an updated tag in order to be recognized by the Zeek package manager (zkg). Follow the instructions in that repo's README for more details.
The RITA installer includes an Ansible playbook that pulls the desired version of `activecm/zeek` from Dockerhub and creates the necessary directories needed to run Zeek. The installer generator also pulls the `zeek` script from the `docker-zeek` repo and includes it in the installer, along with listing the proper image version in the `IMAGE_NAME` variable. Aside from these two items, Zeek and RITA are independent of each other.
+142
View File
@@ -0,0 +1,142 @@
#!/usr/bin/env bash
set -euo pipefail
# creates a dev installer with locally built Docker images
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
RITA_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# load helper functions
HELPER_FILE="$SCRIPT_DIR/helper.sh"
[[ -f "$HELPER_FILE" ]] || { echo "Helper functions script not found: $HELPER_FILE" >&2; exit 1; }
# shellcheck disable=SC1090
source "$HELPER_FILE"
status "Creating RITA dev installer..."
ARM=false
while [[ $# -gt 0 ]]; do
case "$1" in
--arm)
ARM=true
shift
;;
-*|--*)
echo "Unknown option $1" >&2
exit 1
;;
*)
shift
;;
esac
done
# -------------------------
# create base installer
# -------------------------
"$SCRIPT_DIR/generate_installer.sh"
# -------------------------
# determine docker command
# -------------------------
if docker info >/dev/null 2>&1; then
DOCKER_CMD=(docker)
else
DOCKER_CMD=(sudo docker)
fi
# -------------------------
# determine version
# -------------------------
if VERSION="$(git -C "$RITA_DIR" describe --tags --exact-match 2>/dev/null)"; then
:
elif VERSION="$(git -C "$RITA_DIR" describe --tags --dirty --always 2>/dev/null)"; then
:
else
fail "Unable to determine RITA version."
fi
[[ -n "$VERSION" ]] || fail "VERSION not set"
# paths
TARBALL="${SCRIPT_DIR}/rita-${VERSION}.tar.gz"
INSTALLER_DIR="${SCRIPT_DIR}/rita-${VERSION}-installer"
ENV_FILE="${INSTALLER_DIR}/files/opt/.env"
COMPOSE_FILE="${RITA_DIR}/docker-compose.yml"
# -------------------------
# unpack installer tarball
# -------------------------
require_file "$TARBALL"
tar -xzf "$TARBALL" -C "$SCRIPT_DIR"
# parse clickhouse version from .env
require_file "$ENV_FILE"
CLICKHOUSE_VERSION="$(awk -F= '$1=="CLICKHOUSE_VERSION"{v=$2} END{gsub(/^[[:space:]]+|[[:space:]]+$/,"",v); gsub(/^"|"$|^\047|\047$/,"",v); print v}' "$ENV_FILE")"
[[ -n "${CLICKHOUSE_VERSION:-}" ]] || fail "CLICKHOUSE_VERSION is missing from $ENV_FILE"
status "Using CLICKHOUSE_VERSION=$CLICKHOUSE_VERSION"
# determine target platform
PLATFORM="linux/amd64"
if [ "${ARM}" = "true" ]; then
PLATFORM="linux/arm64/v8"
fi
# verify docker is available
"${DOCKER_CMD[@]}" version >/dev/null 2>&1 || fail "docker is required and must be runnable"
# verify gzip is available
command -v gzip >/dev/null 2>&1 || fail "gzip is required"
# -------------------------
# build rita image
# -------------------------
status "Building RITA image for ${PLATFORM}..."
export DOCKER_BUILDKIT=1
export RITA_VERSION="${VERSION}"
if [ "${DOCKER_CMD[0]}" = "sudo" ]; then
sudo env DOCKER_DEFAULT_PLATFORM="$PLATFORM" BUILDX_NO_DEFAULT_ATTESTATIONS=1 docker compose --project-directory "$RITA_DIR" -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build rita
else
DOCKER_DEFAULT_PLATFORM="$PLATFORM" BUILDX_NO_DEFAULT_ATTESTATIONS=1 docker compose --project-directory "$RITA_DIR" -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build rita
fi
# -------------------------
# save images to disk
# -------------------------
INSTALL_OPT="${INSTALLER_DIR}/files/opt"
require_dir "$INSTALL_OPT"
RITA_IMAGE="ghcr.io/activecm/rita:${VERSION}"
# docker save --platform requires Docker 26+
status "Saving RITA image..."
"${DOCKER_CMD[@]}" save --platform "$PLATFORM" "$RITA_IMAGE" | gzip -c > "$INSTALL_OPT/rita-${VERSION}-image.tar.gz"
save_pulled_image() {
local name="$1" ref="$2" outfile="$3"
status "Saving ${name} image..."
"${DOCKER_CMD[@]}" pull --platform "$PLATFORM" "$ref"
"${DOCKER_CMD[@]}" save --platform "$PLATFORM" "$ref" | gzip -c > "$outfile"
}
save_pulled_image "syslog-ng" "lscr.io/linuxserver/syslog-ng:latest" "$INSTALL_OPT/syslog-ng-latest.tar.gz"
save_pulled_image "ClickHouse" "clickhouse/clickhouse-server:${CLICKHOUSE_VERSION}" "$INSTALL_OPT/clickhouse-${CLICKHOUSE_VERSION}.tar.gz"
# sanity checks
require_nonempty_file "$INSTALL_OPT/rita-${VERSION}-image.tar.gz"
require_nonempty_file "$INSTALL_OPT/syslog-ng-latest.tar.gz"
require_nonempty_file "$INSTALL_OPT/clickhouse-${CLICKHOUSE_VERSION}.tar.gz"
# -------------------------
# repack tarball
# -------------------------
if [ "$(uname -s)" = "Darwin" ]; then
tar --no-xattrs --disable-copyfile -czf "$TARBALL" -C "$SCRIPT_DIR" "$(basename "$INSTALLER_DIR")"
else
tar -czf "$TARBALL" -C "$SCRIPT_DIR" "$(basename "$INSTALLER_DIR")"
fi
# clean up
remove_dir "$INSTALLER_DIR"
status "Dev installer created: $(basename "$TARBALL")"
status "Platform: ${PLATFORM}"
+23 -55
View File
@@ -1,22 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
# This script generates the RITA installer by creating a temporary folder in the current directory named 'stage'
# and copies files that must be in the installer into the stage folder.
# Once all directories are placed in stage, it is compressed and stage is deleted.
ZEEK_VERSION=6.2.1
# this script generates the RITA installer
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
RITA_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# load helper functions
HELPER_FILE="$SCRIPT_DIR/install_scripts/helper.sh"
HELPER_FILE="$SCRIPT_DIR/helper.sh"
[[ -f "$HELPER_FILE" ]] || { echo "Helper functions script not found: $HELPER_FILE" >&2; exit 1; }
# shellcheck disable=SC1090
source "$HELPER_FILE"
# get RITA version from git
if VERSION="$(git -C "$RITA_DIR" describe --tags --exact-match 2>/dev/null)"; then
: # release / ci
@@ -29,9 +24,6 @@ fi
status "Generating installer for RITA $VERSION..."
# change working directory to directory of this script
# pushd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" > /dev/null
# create staging directory
INSTALLER_DIR="${SCRIPT_DIR}/rita-$VERSION-installer"
OUTPUT_TARBALL="${SCRIPT_DIR}/rita-$VERSION.tar.gz"
@@ -39,74 +31,50 @@ remove_dir "$INSTALLER_DIR"
remove_file "$OUTPUT_TARBALL"
create_new_dir "$INSTALLER_DIR"
# create ansible subfolders
# create subfolders
SCRIPTS="$INSTALLER_DIR/scripts"
ANSIBLE_FILES="$INSTALLER_DIR/files"
FILES="$INSTALLER_DIR/files"
create_new_dir "$SCRIPTS"
create_new_dir "$ANSIBLE_FILES"
create_new_dir "$FILES"
# create subfolders (for files that installed RITA will contain)
INSTALL_OPT="$ANSIBLE_FILES"/opt
INSTALL_ETC="$ANSIBLE_FILES"/etc
INSTALL_OPT="$FILES/opt"
INSTALL_ETC="$FILES/etc"
create_new_dir "$INSTALL_OPT"
create_new_dir "$INSTALL_ETC"
# copy files in base dir
copy_file "${SCRIPT_DIR}/install-rita-zeek-here.sh" "$INSTALLER_DIR"
copy_file "${SCRIPT_DIR}/install_scripts/install_zeek.yml" "$INSTALLER_DIR"
copy_file "${SCRIPT_DIR}/install_scripts/install_rita.yml" "$INSTALLER_DIR"
copy_file "${SCRIPT_DIR}/install_scripts/install_pre.yml" "$INSTALLER_DIR"
# copy entrypoint
copy_file "${SCRIPT_DIR}/install_rita.sh" "$INSTALLER_DIR"
copy_file "${SCRIPT_DIR}/install_scripts/install_rita.sh" "$INSTALLER_DIR" # entrypoint
# copy helper scripts
copy_file "${SCRIPT_DIR}/helper.sh" "$SCRIPTS"
# copy files to helper script folder
copy_file "${SCRIPT_DIR}/install_scripts/ansible-installer.sh" "$SCRIPTS"
copy_file "${SCRIPT_DIR}/install_scripts/helper.sh" "$SCRIPTS"
copy_file "${SCRIPT_DIR}/install_scripts/sshprep.sh" "$SCRIPTS"
# copy files to the ansible files folder
copy_file "${SCRIPT_DIR}/install_scripts/docker-compose" "$ANSIBLE_FILES" # docker-compose v1 backwards compatibility script
# copy over configuration files to /files/etc
# copy configuration files to /files/etc
copy_dir_contents "${RITA_DIR}/deployment" "$INSTALL_ETC"
rm -f "$INSTALL_ETC/threat_intel_feeds/.gitkeep"
copy_file "${RITA_DIR}/default_config.hjson" "$INSTALL_ETC/config.hjson"
# copy over installed files to /opt
# copy installed files to /files/opt
copy_file "${RITA_DIR}/rita.sh" "$INSTALL_OPT"
curl --fail --silent --show-error -o "${INSTALL_OPT}/zeek" https://raw.githubusercontent.com/activecm/docker-zeek/master/zeek
chmod +x "${INSTALL_OPT}/zeek"
curl --fail --silent --show-error -o "${INSTALL_OPT}/zeek_log_transport.sh" https://raw.githubusercontent.com/activecm/zeek-log-transport/refs/heads/master/zeek_log_transport.sh
chmod +x "${INSTALL_OPT}/zeek_log_transport.sh"
copy_file "${RITA_DIR}/.env.production" "${INSTALL_OPT}/.env"
copy_file "${RITA_DIR}/docker-compose.prod.yml" "${INSTALL_OPT}/docker-compose.yml"
copy_file "${RITA_DIR}/LICENSE" "${INSTALL_OPT}/LICENSE"
copy_file "${RITA_DIR}/README.md" "${INSTALL_OPT}/README"
# update version variables for files that need them
# update version variables
if [[ "$(uname)" == "Darwin" ]]; then
sed -i'.bak' "s/RITA_REPLACE_ME/${VERSION}/g" "${INSTALLER_DIR}/install-rita-zeek-here.sh"
sed -i'.bak' "s/REPLACE_ME/${VERSION}/g" "${INSTALLER_DIR}/install_rita.yml"
sed -i'.bak' "s/REPLACE_ME/${ZEEK_VERSION}/g" "${INSTALLER_DIR}/install_zeek.yml"
sed -i'.bak' "s/REPLACE_ME/${VERSION}/g" "${INSTALLER_DIR}/install_rita.sh"
sed -i'.bak' "s#ghcr.io/activecm/rita:latest#ghcr.io/activecm/rita:${VERSION}#g" "${INSTALL_OPT}/docker-compose.yml"
remove_file "${INSTALLER_DIR}/install-rita-zeek-here.sh.bak"
remove_file "${INSTALLER_DIR}/install_rita.yml.bak"
remove_file "${INSTALLER_DIR}/install_zeek.yml.bak"
remove_file "${INSTALLER_DIR}/install_rita.sh.bak"
remove_file "${INSTALL_OPT}/docker-compose.yml.bak"
else
sed -i "s/RITA_REPLACE_ME/${VERSION}/g" "${INSTALLER_DIR}/install-rita-zeek-here.sh"
sed -i "s/REPLACE_ME/${VERSION}/g" "${INSTALLER_DIR}/install_rita.yml"
sed -i "s/REPLACE_ME/${ZEEK_VERSION}/g" "${INSTALLER_DIR}/install_zeek.yml"
sed -i "s/REPLACE_ME/${VERSION}/g" "${INSTALLER_DIR}/install_rita.sh"
sed -i "s#ghcr.io/activecm/rita:latest#ghcr.io/activecm/rita:${VERSION}#g" "${INSTALL_OPT}/docker-compose.yml"
else
sed -i "s/REPLACE_ME/${VERSION}/g" "${INSTALLER_DIR}/install_rita.sh"
fi
# create tarball from staging folder
# write RITA_VERSION into the env file so docker compose resolves the image tag
printf 'RITA_VERSION=%s\n' "${VERSION}" >> "${INSTALL_OPT}/.env"
# create tarball
tar -czf "$OUTPUT_TARBALL" -C "$SCRIPT_DIR" "$(basename "$INSTALLER_DIR")"
# delete staging folder
# clean up staging directory
remove_dir "$INSTALLER_DIR"
status "Finished generating installer."
status "Finished generating installer: $(basename "$OUTPUT_TARBALL")"
@@ -8,8 +8,6 @@ fi
RED=""
YELLOW=""
BLUE=""
GREEN=""
NORMAL=""
# SUDO and SUDO_E are intentionally initialized to empty here.
@@ -25,44 +23,56 @@ verbose="yes"
# use colors if terminal supports it
if [[ -t 1 ]] && command -v tput >/dev/null 2>&1; then
RED=$(tput setaf 1)
YELLOW=$(tput setaf 3)
BLUE=$(tput setaf 4)
GREEN=$(tput setaf 2)
NORMAL=$(tput sgr0)
RED=$(tput setaf 1)
YELLOW=$(tput setaf 3)
NORMAL=$(tput sgr0)
fi
# something failed, exit
fail() {
echo "${RED}$*, exiting.${NORMAL}" >&2
exit 1
echo "${RED}$*, exiting.${NORMAL}" >&2
exit 1
}
# print status message if verbose is enabled
status() {
if [[ "${verbose:-}" == "yes" ]]; then
echo "== $*" >&2
fi
if [[ "${verbose:-}" == "yes" ]]; then
echo "== $*" >&2
fi
}
# ensure script is run with sudo privileges
# ensure script is run with sudo privileges
require_sudo() {
# check if running as root
if [[ "$EUID" -eq 0 ]]; then
if [[ "${EUID:-$(id -u)}" -eq 0 ]]; then
SUDO=""
SUDO_E=""
export SUDO SUDO_E
return 0
fi
# check that we are able to run commands with sudo (non-interactive)
if sudo -v </dev/null 2>/dev/null; then
# sudo must exist
require_cmd "sudo"
# check for passwordless sudo
if sudo -n true >/dev/null 2>&1; then
SUDO="sudo"
SUDO_E="sudo -E"
export SUDO SUDO_E
return 0
fi
# check for password-required sudo if in an interactive shell
if [[ -t 0 ]]; then
status "Administrator privileges required. You may be prompted for your sudo password."
if sudo -v; then
SUDO="sudo"
SUDO_E="sudo -E"
export SUDO SUDO_E
return 0
fi
fi
fail "Missing administrator privileges. Please run with an account that has sudo privileges."
}
@@ -73,17 +83,17 @@ require_cmd() {
# require that a file exists
require_file() {
[[ -f "$1" ]] || { fail "Required file not found: $1"; }
[[ -f "$1" ]] || fail "Required file not found: $1"
}
# require that a file exists and is not empty
require_nonempty_file() {
[[ -s "$1" ]] || fail "Required file missing or empty: $1"
}
# require that a directory exists
require_dir() {
[[ -d "$1" ]] || { fail "Required directory not found: $1"; }
}
# require that an environment variable is set and non-empty
require_env() {
[[ -n "${!1:-}" ]] || fail "Required environment variable not set or empty: $1"
[[ -d "$1" ]] || fail "Required directory not found: $1"
}
# copy a file, ensuring the source exists and the destination parent directory exists
@@ -93,33 +103,27 @@ copy_file() {
require_file "$src"
# if user provided a directory as destination, copy into that directory
if [[ -d "$dst" ]]; then
cp -- "$src" "$dst" || fail "Failed to copy file from $src to $dst"
require_file "$dst/$(basename "$src")"
else
# if user provided a full destination path (including filename), ensure parent directory exists
require_dir "$(dirname "$dst")"
cp -- "$src" "$dst" || fail "Failed to copy file from $src to $dst"
require_file "$dst"
fi
}
# copy a directory recursively, ensuring the source exists and the destination parent directory exists
copy_dir() {
require_dir "$1"
require_dir "$2"
cp -r -- "$1" "$2" || fail "Failed to copy directory from $1 to $2"
}
# this version does not copy the dir itself, nor any dotfiles inside it
# copy the contents of a directory (not the directory itself or dotfiles)
copy_dir_contents() {
require_nonempty_dir "$1"
require_dir "$1"
if ! find "$1" -mindepth 1 -maxdepth 1 ! -name '.*' -print -quit | grep -q .; then
fail "Directory is empty: $1"
fi
require_dir "$2"
cp -r -- "$1"/* "$2" || fail "Failed to copy contents from $1 to $2"
}
# create a new directory, failing if it already exists
create_new_dir() {
if [[ -e "$1" ]]; then
fail "Failed to create new directory, path already exists: $1"
@@ -128,62 +132,19 @@ create_new_dir() {
require_dir "$1"
}
# check if a directory exists and is empty
dir_is_empty() {
[[ -d "$1" ]] || return 1
[[ -z "$(ls -A "$1" 2>/dev/null)" ]]
}
# ensure a directory exists; create it if missing
ensure_dir() {
[[ -d "$1" ]] && return 0
create_new_dir "$1"
}
# ensure a directory exists (create if not) and is empty
ensure_empty_dir() {
ensure_dir "$1"
if ! dir_is_empty "$1"; then
clear_dir "$1"
fi
}
# require that a directory has at least one non-dot entry (file or dir)
require_nonempty_dir() {
require_dir "$1"
if ! find "$1" -mindepth 1 -maxdepth 1 ! -name '.*' -print -quit | grep -q .; then
fail "Directory is empty: $1"
fi
}
# remove a file (or symlink) if it exists
remove_file() {
local path="$1"
if [[ -e "$path" || -L "$path" ]]; then
rm -f -- "$path" || fail "Failed to remove file: $path"
rm -f -- "$path" || fail "Failed to remove file: $path"
fi
}
# remove a directory if it exists
remove_dir() {
if [[ -d "$1" ]]; then
rm -rf -- "$1" || fail "Failed to remove directory: $1"
rm -rf -- "$1" || fail "Failed to remove directory: $1"
elif [[ -e "$1" || -L "$1" ]]; then
fail "Expected directory but found non-directory: $1"
fi
}
# delete everything inside a directory, but keep the directory itself
clear_dir() {
[[ -n "${1:-}" ]] || fail "clear_dir: missing dir"
local dir="$1"
if [[ -e "$dir" || -L "$dir" ]]; then
[[ -d "$dir" ]] || fail "Expected directory but found non-directory: $dir"
else
ensure_dir "$dir"
fi
# Delete contents (including dotfiles) but not the directory itself.
find -- "$dir" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} + || fail "Failed to clear directory: $dir"
}
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# This script downloads and installs rita, zeek, and all dependencies on the current system
export RITA_VERSION="RITA_REPLACE_ME"
export zeek_release='latest'
export PATH="$PATH:/usr/local/bin/"
echo 'export PATH=$PATH:/usr/local/bin/' | sudo tee -a /etc/profile.d/localpath.sh
if [[ "$EUID" -ne 0 ]]; then
SUDO="/usr/bin/sudo "
fi
$SUDO mkdir -p /usr/local/bin/
echo "==== Installing rita $RITA_VERSION ====" >&2
cd
wget https://github.com/activecm/rita/releases/download/${RITA_VERSION}/rita-${RITA_VERSION}.tar.gz
tar -xzvf rita-${RITA_VERSION}.tar.gz
cd rita-${RITA_VERSION}-installer
./install_rita.sh localhost </dev/stderr
rita help </dev/stderr
echo "==== Installing zeek $zeek_release ====" >&2
$SUDO wget -O /usr/local/bin/zeek https://raw.githubusercontent.com/activecm/docker-zeek/master/zeek
$SUDO chmod +x /usr/local/bin/zeek
/usr/local/bin/zeek pull </dev/stderr
sleep 2
/usr/local/bin/zeek stop </dev/stderr
echo "Please run 'zeek start' if you want to start zeek running in the background." >&2
echo 'If your system has trouble locating either zeek or rita we recommend logging out and logging back in.' >&2
+226
View File
@@ -0,0 +1,226 @@
#!/usr/bin/env bash
set -euo pipefail
# RITA installer script
# this installer must be run directly on the system where RITA will be installed
# -------------------------
# constants
# -------------------------
RITA_VERSION="REPLACE_ME"
# -------------------------
# paths inside installer
# -------------------------
INSTALLER="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
SRC_FILES="${INSTALLER}/files"
SRC_OPT="${SRC_FILES}/opt"
SRC_ETC="${SRC_FILES}/etc"
# -------------------------
# paths on target system
# -------------------------
ETC="/etc/rita"
OPT="/opt/rita"
# -------------------------
# load helper functions
# -------------------------
HELPER_FILE="${INSTALLER}/scripts/helper.sh"
[[ -f "$HELPER_FILE" ]] || { echo "Helper functions script not found: $HELPER_FILE" >&2; exit 1; }
# shellcheck disable=SC1090
source "$HELPER_FILE"
# ensure /usr/local/bin is in PATH
export PATH="/usr/local/bin:${PATH}"
# -------------------------
# local functions
# -------------------------
show_help() {
cat >&2 <<EOF
Usage: $(basename "$0") [options]
This installer installs RITA on the local system.
Options:
-h, --help Show this help message and exit
Example:
$(basename "$0")
EOF
exit 0
}
require_docker_ready() {
command -v docker >/dev/null 2>&1 || fail "Docker is required. Install Docker Engine + the compose plugin, then re-run."
"${DSUDO[@]}" docker info >/dev/null 2>&1 || fail "Docker daemon is not reachable. Ensure docker is running (systemctl enable --now docker)."
if command -v docker-compose >/dev/null 2>&1 && ! "${DSUDO[@]}" docker compose version >/dev/null 2>&1; then
fail "'docker-compose' (v1) is installed, but RITA requires Docker Compose v2 ('docker compose')."
fi
"${DSUDO[@]}" docker compose version >/dev/null 2>&1 || fail "Docker Compose v2 is required (the 'docker compose' plugin). Install Docker Compose v2, then re-run."
}
# -------------------------
# early help check
# -------------------------
if [[ ${1:-} == "-h" || ${1:-} == "--help" ]]; then
show_help
fi
# --------------------------
# requirements checks
# --------------------------
status "Verifying system requirements..."
# sudo checks and setup
require_sudo
# special-case sudo for docker commands
DSUDO=()
if [[ "$(uname)" != "Darwin" && -n "${SUDO:-}" ]]; then
DSUDO=("$SUDO")
fi
# docker checks
require_docker_ready
# command checks
require_cmd install
require_cmd cp
# --------------------------
# begin installation
# --------------------------
status "Installing RITA..."
# verify required source directories exist
require_dir "$SRC_FILES"
require_dir "$SRC_OPT"
require_dir "$SRC_ETC"
# -------------------------
# stop running old system
# -------------------------
status "Checking for existing RITA installation..."
if command -v rita >/dev/null 2>&1; then
status "Stopping existing RITA..."
"${DSUDO[@]}" rita down >/dev/null 2>&1 || true
elif [[ -x /usr/local/bin/rita ]]; then
status "Stopping existing RITA..."
"${DSUDO[@]}" /usr/local/bin/rita down >/dev/null 2>&1 || true
fi
# ---------------------------
# create required directories
# ---------------------------
status "Creating directories..."
"${DSUDO[@]}" mkdir -p "$ETC" "$OPT"
"${DSUDO[@]}" chmod 0755 "$ETC" "$OPT"
# -------------------------
# install rita command
# -------------------------
status "Installing rita..."
require_file "${SRC_OPT}/rita.sh"
RITA_BIN="/usr/local/bin/rita"
"${DSUDO[@]}" install -m 0755 "${SRC_OPT}/rita.sh" "$RITA_BIN"
command -v "$RITA_BIN" >/dev/null 2>&1 || fail "rita was not installed correctly to ${RITA_BIN}"
# -------------------------
# copy opt
# -------------------------
"${DSUDO[@]}" cp -a "${SRC_OPT}/." "${OPT}/"
# --------------------------------------------
# copy etc and preserve existing config.hjson
# --------------------------------------------
CFG_DST="${ETC}/config.hjson"
CFG_BACKUP=""
cleanup_cfg_backup() {
if [[ -n "${CFG_BACKUP:-}" && -f "${CFG_BACKUP:-}" ]]; then
"${DSUDO[@]}" rm -f "$CFG_BACKUP" || true
fi
}
trap cleanup_cfg_backup EXIT
if [[ -f "$CFG_DST" ]]; then
status "Preserving existing config.hjson..."
CFG_BACKUP="$(mktemp /tmp/rita_config.hjson.XXXXXX)"
"${DSUDO[@]}" cp -f "$CFG_DST" "$CFG_BACKUP"
if [[ "$(uname)" == "Darwin" ]]; then
"${DSUDO[@]}" chown root:wheel "$CFG_BACKUP" || true
else
"${DSUDO[@]}" chown root:root "$CFG_BACKUP" || true
fi
"${DSUDO[@]}" chmod 0644 "$CFG_BACKUP" || true
fi
"${DSUDO[@]}" cp -a "${SRC_ETC}/." "${ETC}/"
if [[ -n "$CFG_BACKUP" && -f "$CFG_BACKUP" ]]; then
status "Restoring preserved config.hjson..."
"${DSUDO[@]}" cp -f "$CFG_BACKUP" "$CFG_DST"
if [[ "$(uname)" == "Darwin" ]]; then
"${DSUDO[@]}" chown root:wheel "$CFG_DST" || true
else
"${DSUDO[@]}" chown root:root "$CFG_DST" || true
fi
"${DSUDO[@]}" chmod 0644 "$CFG_DST" || true
"${DSUDO[@]}" rm -f "$CFG_BACKUP" || true
CFG_BACKUP=""
fi
trap - EXIT
# -------------------------
# load installer env
# -------------------------
ENV="${OPT}/.env"
require_file "$ENV"
set -a
# shellcheck disable=SC1090
source "$ENV"
set +a
# -------------------------
# pull rita docker image
# -------------------------
status "Pulling RITA docker image..."
RITA_IMAGE="ghcr.io/activecm/rita:${RITA_VERSION}"
load_image() {
local name="$1" pull_ref="$2" tar_path="$3"
status "Loading ${name} image..."
if [[ -f "$tar_path" ]]; then
"${DSUDO[@]}" docker load -i "$tar_path"
elif "${DSUDO[@]}" docker pull "$pull_ref"; then
:
else
fail "Unable to load ${name} image. No local image at ${tar_path} and pull failed."
fi
}
load_image "RITA" "$RITA_IMAGE" "${OPT}/rita-${RITA_VERSION}-image.tar.gz"
load_image "syslog-ng" "lscr.io/linuxserver/syslog-ng:latest" "${OPT}/syslog-ng-latest.tar.gz"
load_image "ClickHouse" "clickhouse/clickhouse-server:${CLICKHOUSE_VERSION}" "${OPT}/clickhouse-${CLICKHOUSE_VERSION}.tar.gz"
echo \
"
░█▀▀█ ▀█▀ ▀▀█▀▀ ─█▀▀█
░█▄▄▀ ░█─ ─░█── ░█▄▄█
░█─░█ ▄█▄ ─░█── ░█─░█ ${RITA_VERSION}
Brought to you by Active CounterMeasures©
"
cat >&2 <<EOF
Installation complete!
EOF
@@ -1,267 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# Ansible Install Script
# This script installs Ansible on the current system using pipx.
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
# cd to the directory where this script is located
pushd "$SCRIPT_DIR" > /dev/null
# load helper functions
HELPER_FILE="$SCRIPT_DIR/helper.sh"
[[ -f "$HELPER_FILE" ]] || { echo "Helper functions script not found: $HELPER_FILE" >&2; exit 1; }
# shellcheck disable=SC1090
source "$HELPER_FILE"
# verify that user has sudo privileges
require_sudo
# enable any needed repositories
enable_repositories() {
status "Enabling necessary package repositories..."
if [[ ! -s /etc/os-release ]]; then
fail "Unable to read /etc/os-release"
else
. /etc/os-release
case "$ID/$VERSION_ID" in
rocky/8*|almalinux/8*)
$SUDO dnf config-manager --set-enabled powertools
$SUDO dnf install -y epel-release
;;
rocky/9*)
$SUDO dnf config-manager --set-enabled crb
$SUDO dnf install -y epel-release
;;
centos/9)
$SUDO dnf config-manager --set-enabled crb
$SUDO dnf install -y epel-release epel-next-release
;;
rhel/8*)
$SUDO subscription-manager repos --enable codeready-builder-for-rhel-8-$(arch)-rpms
$SUDO dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
;;
rhel/9*)
$SUDO subscription-manager repos --enable codeready-builder-for-rhel-9-$(arch)-rpms
$SUDO dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
;;
ubuntu/*)
$SUDO apt update
;;
*)
fail "Unsupported Distribution $ID/$VERSION_ID"
;;
esac
fi
}
# Install a required executable
# This function attempts to install a system package only if the corresponding binary does
# not already exist in PATH. This is only intended for tools that provide a real executable
# $1 = binary name to check for (e.g., "python3", "pip3", "curl")
# $2 = space-separated list of package names that provide the binary (preferred packages first)
install_tool() {
binary="$1"
potential_packages="$2"
# if the binary already exists, nothing to do
if type -path "$binary" >/dev/null 2>&1; then
status "== $binary executable is already installed"
return 0
fi
status "== Installing package that contains $binary"
# Ubuntu
if command -v apt-get >/dev/null 2>&1; then
for pkg in $potential_packages; do
if ! type -path "$binary" >/dev/null 2>&1; then
$SUDO apt-get -q -y install "$pkg"
fi
done
# RHEL / CentOS / Rocky / Alma
elif command -v yum >/dev/null 2>&1; then
for pkg in $potential_packages; do
if ! type -path "$binary" >/dev/null 2>&1; then
$SUDO yum -y -q -e 0 install "$pkg"
fi
done
else
fail "Unable to install packages: unsupported package manager"
fi
# final verification
if type -path "$binary" >/dev/null 2>&1; then
return 0
else
echo "WARNING: Unable to install $binary from system package" >&2
return 1
fi
}
install_ansible() {
# Make sure venv support actually works on this system.
if ! python3 -m venv --help >/dev/null 2>&1; then
fail "Python venv support is missing on this system. Cannot continue."
fi
# Bootstrap a local virtualenv whose only job is to host pipx,
# so we never touch system Python even on PEP 668 distros.
python3 -m venv .ansenv || fail "Unable to create Python virtual environment"
# shellcheck disable=SC1091
source .ansenv/bin/activate
# Make sure pip in the venv is up to date before installing pipx
python3 -m pip install --upgrade pip || fail "Unable to upgrade pip in virtual environment"
# install pipx into this bootstrap venv
python3 -m pip install pipx || fail "Unable to install pipx in virtual environment"
# ask pipx to ensure ~/.local/bin is added to future shells' PATH
pipx ensurepath --prepend || true
# install a specific, pinned ansible-core via pipx.
# pipx will create/own its own venv under ~/.local/pipx/venvs/ansible-core
# and expose the entrypoints (ansible, ansible-playbook, ansible-galaxy)
# under ~/.local/bin/.
pipx install "ansible-core==2.15.13" --force || fail "Unable to install ansible-core with pipx"
deactivate
# After deactivating, PATH is restored to its previous value, so any edits we
# made inside the venv are lost. Re-ensure ~/.local/bin is on PATH for the rest
# of this script so ansible/ansible-playbook/ansible-galaxy are visible.
case ":$PATH:" in
*":$HOME/.local/bin:"*) ;;
*) export PATH="$HOME/.local/bin:$PATH" ;;
esac
# Sanity check: make sure the expected Ansible CLIs are now visible
for bin in ansible ansible-playbook ansible-galaxy; do
if ! command -v "$bin" >/dev/null 2>&1; then
fail "$bin not found in PATH after pipx installation"
fi
done
# Link Ansible binaries globally so they work for root, users, cron, and systemd
status "Linking Ansible binaries globally..."
for bin in ansible ansible-playbook ansible-galaxy; do
SRC="$(command -v "$bin" || true)"
if [ -n "$SRC" ]; then
$SUDO ln -sf "$SRC" "/usr/local/bin/$bin"
else
fail "Unable to locate $bin for global linking"
fi
done
# install requisite ansible collections
status "Installing required Ansible collections..."
ansible-galaxy collection install community.general community.docker --force
}
# ======== main script starts here ========
# require sudo privileges
require_sudo
# check if macOS, and install ansible via brew if so
if [[ "$(uname)" == "Darwin" ]]; then
# check if ansible is installed
which -s ansible
if [[ $? != 0 ]] ; then
# check if homebrew is installed
which -s brew
if [[ $? != 0 ]] ; then
fail "Homebrew is required to install Ansible."
fi
# install ansible via homebrew
echo "Installing Ansible via brew..."
brew install ansible
else
echo "== Ansible is already installed."
fi
else # assume linux
# enable necessary repositories
enable_repositories
status "Installing required tools..."
# install python dependencies
install_tool python3 "python3"
install_tool pip3 "python3-pip"
# ensure python venv support is available - cannot use install_tool for this since
# venv is a module, not a binary
# Ensure `python3 -m venv` actually works on Debian/Ubuntu.
# On these systems the stdlib venv module requires the extra
# `python3-venv` package (which provides ensurepip).
# This is safe to run even if it's already installed: `apt-get install`
# is idempotent and will return success in that case.
if command -v apt-get >/dev/null 2>&1; then
status "Ensuring python3-venv is installed for virtualenv support"
$SUDO apt-get -q -y install python3-venv
fi
# sanity check after install
if ! python3 -m venv --help >/dev/null 2>&1; then
fail "python3 venv module is still not available after installation"
fi
# verify that pip is functional
if ! python3 -m pip -V >/dev/null 2>&1; then
fail "Unable to run python3's pip"
fi
# install other dependencies
install_tool wget "wget"
install_tool curl "curl"
# install ansible
install_ansible
fi
# ensure /usr/local/bin is in PATH
status "Ensuring /usr/local/bin is in PATH..."
if ! printf '%s\n' "$PATH" | grep -qE '(^|:)/usr/local/bin(:|$)'; then
echo "Adding /usr/local/bin to PATH" >&2
# For current session
export PATH="$PATH:/usr/local/bin"
# For future logins (prefer system-wide drop-in if available)
if [ -d /etc/profile.d ]; then
echo 'export PATH="$PATH:/usr/local/bin"' | $SUDO tee /etc/profile.d/local-bin-path.sh >/dev/null
elif [ -s /etc/profile ]; then
echo 'export PATH="$PATH:/usr/local/bin"' | $SUDO tee -a /etc/profile >/dev/null
else
echo "Warning: Unable to persist /usr/local/bin in PATH" >&2
fi
fi
# switch back to original working directory
popd > /dev/null
status "Final verification..."
# verify the binary is resolvable
if ! command -v ansible-playbook >/dev/null 2>&1; then
fail "ansible-playbook is not in PATH after installation"
fi
# verify ansible-playbook executes
if ! ansible-playbook --version >/dev/null 2>&1; then
fail "ansible-playbook is present but failed to execute"
fi
# verify ansible-galaxy executes
if ! ansible-galaxy --version >/dev/null 2>&1; then
fail "ansible-galaxy is present but failed to execute"
fi
status "Ansible installation complete"
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
docker compose "$@"
-234
View File
@@ -1,234 +0,0 @@
# ansible playbook that performs pre-installation tasks for RITA
- name: "RITA Pre: System prep and checks"
hosts: "{{ install_hosts }}"
become: true
vars:
ansible_python_interpreter: /bin/python3
# early tasks for checking system compatibility
pre_tasks:
- name: "RITA Pre: Checking Linux distribution"
ansible.builtin.fail:
msg: "Distribution {{ ansible_distribution }} is not supported, please see the documentation for supported distributions."
when:
- ansible_distribution != 'CentOS'
- ansible_distribution != 'Rocky'
- ansible_distribution != 'Ubuntu'
- ansible_distribution != 'RedHat'
- ansible_distribution != 'AlmaLinux' #NOTE: legacy support for AlmaLinux 8 only
tags:
- linux
- name: "RITA Pre: Checking Linux distribution version"
ansible.builtin.fail:
msg: "Distribution {{ ansible_distribution }} {{ ansible_distribution_major_version }} is not supported."
when:
- >
(ansible_distribution == 'Ubuntu' and ansible_distribution_major_version not in ['22', '24']) or
(ansible_distribution == 'CentOS' and ansible_distribution_major_version != '9') or
(ansible_distribution == 'RedHat' and ansible_distribution_major_version not in ['8', '9']) or
(ansible_distribution == 'AlmaLinux' and ansible_distribution_major_version != '8')
tags:
- linux
- name: "RITA Pre: Check CPU architecture"
ansible.builtin.fail:
msg: "CPU architecture {{ ansible_architecture }} is not supported."
when: ( ansible_architecture != "x86_64" ) #and ansible_architecture != "aarch64" ) # "aarch64" for pi. #pi0w is armv6l. i386. amd64?
tasks:
# Add repositories
# Note that apt-key is deprecated and that directly downloading the key to trusted.gpg.d WITH A .asc EXTENSION is the correct way now.
- name: "RITA Pre: Download Docker Ubuntu GPG apt key"
block:
- name: "RITA Pre: Download Docker Ubuntu GPG apt key with get_url module"
get_url:
url: https://download.docker.com/linux/ubuntu/gpg
dest: /etc/apt/trusted.gpg.d/docker-ubuntu.asc
mode: "0644"
force: true
rescue:
- name: "RITA Pre: Download failed with get_url module. Falling back to curl"
shell: curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/trusted.gpg.d/docker-ubuntu.asc
when: ansible_distribution == 'Ubuntu'
tags:
- packages
- linux
- linuxdeb
- name: "RITA Pre: Add Docker Repository to Ubuntu"
apt_repository:
repo: deb https://download.docker.com/linux/{{ ansible_distribution|lower }} {{ ansible_distribution_release }} stable
state: present
when: ansible_distribution == 'Ubuntu'
tags:
- packages
- linux
- linuxdeb
- name: "RITA Pre: Add Docker Repository to Centos and Rocky distributions" #Alma is included for legacy support
yum_repository:
name: docker-ce
description: Docker package repository
gpgkey: https://download.docker.com/linux/centos/gpg
baseurl: https://download.docker.com/linux/centos/$releasever/$basearch/stable/
state: present
enabled: true
when: ansible_distribution == 'CentOS' or ansible_distribution == 'Rocky' or ansible_distribution == 'AlmaLinux'
tags:
- packages
- linux
- linuxrpm
- name: "RITA Pre: Add Docker Repository to RHEL distribution"
yum_repository:
name: docker-ce
description: Docker package repository
gpgkey: https://download.docker.com/linux/rhel/gpg
baseurl: https://download.docker.com/linux/rhel/$releasever/$basearch/stable/
state: present
enabled: true
when: ansible_distribution == 'RedHat'
tags:
- packages
- linux
- linuxrpm
# Install docker
- name: "RITA Pre: Install docker on Ubuntu"
block:
- name: "RITA Pre: Uninstall unofficial docker packages on Ubuntu"
apt:
name:
- docker-client
- docker-client-latest
- docker-common
- docker-compose
- docker-compose-v2
- docker-doc
- docker-engine
- docker-latest
- docker-latest-logrotate
- docker-logrotate
- docker.io
- podman-docker
state: absent
update_cache: true
cache_valid_time: 3600
tags:
- docker
- linux
- linuxdeb
- name: "RITA Pre: Install docker-ce on Ubuntu"
apt:
name:
- docker-ce
- docker-ce-cli
- docker-compose-plugin
- containerd.io
state: latest
update_cache: true
cache_valid_time: 3600
tags:
- docker
- linux
- linuxdeb
- name: "RITA Pre: Install docker modules for Python on Ubuntu"
apt:
name:
- python3-docker
- python3-requests
tags:
- docker
- linux
- linuxdeb
when: ansible_distribution == 'Ubuntu'
- name: "RITA Pre: Install docker on rpm-based distributions"
block:
- name: "RITA Pre: Uninstall unofficial docker packages on rpm-based distributions"
yum:
name:
- docker-client
- docker-client-latest
- docker-common
- docker-compose
- docker-compose-v2
- docker-doc
- docker-engine
- docker-latest
- docker-latest-logrotate
- docker-logrotate
- docker.io
- docker
- podman-docker
- podman
- runc
state: absent
update_cache: true
tags:
- docker
- linux
- linuxrpm
- name: "RITA Pre: Install docker-ce on rpm-based distributions"
yum:
name:
- docker-ce
- docker-ce-cli
- docker-buildx-plugin
- docker-compose-plugin
- containerd.io
state: latest
update_cache: true
tags:
- docker
- linux
- linuxrpm
when: ansible_distribution == 'CentOS' or ansible_distribution == 'RedHat' or ansible_distribution == 'Rocky' or ansible_distribution == 'AlmaLinux'
- name: "RITA Pre: Start and enable docker in systemd"
systemd:
name: docker
state: started
enabled: yes
tags:
- docker
- linux
- linuxdeb
- linuxrpm
- name: "RITA Pre: Transfer docker-compose script to target system for backwards compatibility"
copy:
src: docker-compose
dest: /usr/local/bin/docker-compose
owner: root
group: root
mode: 0755
tags:
- docker
- linux
- linuxdeb
- linuxrpm
# TODO: Fix and add this back in after RITA#65 is done
# #This keypair will be used for all transfers between all zeek sensors and all achunter systems.
# #Note that if there's an existing key it will not be overwritten. Keypair will be placed in the playbook directory.
# #Note: formerly used {{ playbook_dir }}
# - name: "RITA Pre: Generate ssh keypair for log transfers"
# local_action:
# module: "user"
# name: "{{ lookup('env','USER') }}"
# generate_ssh_key: true
# ssh_key_type: "rsa"
# ssh_key_bits: 4096
# ssh_key_file: "{{ lookup('env', 'PWD') }}/id_rsa_dataimport"
# # when: ansible_connection != "local"
# tags:
# - docker
# - linux
# - linuxdeb
# - linuxrpm
-101
View File
@@ -1,101 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# RITA Install Script
# This script installs Ansible and uses it to install RITA and Zeek on a target system.
RITA_VERSION="REPLACE_ME"
_INSTALL_ZEEK=true
# Function `show_help` displays usage information
show_help() {
echo "Usage: $0 [--disable-zeek] <target_hostname_or_ip>" >&2
echo "Example: $0 127.0.0.1" >&2
exit 1
}
# No arguments provided
if [[ $# -eq 0 ]]; then
show_help
fi
# Parse optional flag
if [[ "${1:-}" = "--disable-zeek" ]]; then
_INSTALL_ZEEK=false
shift
fi
# Hostname/IP must now be present
if [[ $# -eq 0 ]]; then
show_help
fi
install_target="$1"
shift
# If someone puts --disable-zeek after the host, still support it:
if [[ "${1:-}" = "--disable-zeek" ]]; then
_INSTALL_ZEEK=false
shift
fi
# Change working directory to directory of this script
pushd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" > /dev/null
# Load helper functions
source ./scripts/helper.sh
# Install ansible
./scripts/ansible-installer.sh
# Install rita
status "Installing rita via ansible on $install_target"
if [[ "$install_target" = "localhost" || "$install_target" = "127.0.0.1" || "$install_target" = "::1" ]]; then
if [[ "$(uname)" = "Darwin" ]]; then
# TODO support macOS install target
echo "${YELLOW}Installing RITA via Ansible on the local system is not yet supported on MacOS.${NORMAL}"
exit 1
fi
status "When prompted for a BECOME password, enter your sudo password. If your user does not need one for sudo, just press Enter."
if [[ "$_INSTALL_ZEEK" = 'true' ]]; then
ansible-playbook --connection=local -K -i "127.0.0.1," -e "install_hosts=127.0.0.1," install_pre.yml install_rita.yml install_zeek.yml
else
ansible-playbook --connection=local -K -i "127.0.0.1," -e "install_hosts=127.0.0.1," install_pre.yml install_rita.yml
fi
else
status "Setting up future ssh connections to $install_target . You may be asked to provide your ssh password to $install_target ."
./scripts/sshprep.sh "$install_target"
status "When prompted for a BECOME password, enter your sudo password for $install_target. If your user does not need one for sudo, just press Enter."
if [[ "$_INSTALL_ZEEK" = 'true' ]]; then
# TODO: fix and re-implement cron setup after RITA#65 is resolved
# status "Creating Zeek log transport Cron file"
# rm -f zeek_log_transport.cron ; touch zeek_log_transport.cron
# #NON_ROOT_ACCOUNT_NAME will be replaced after being placed on the target system (by an ansible recipe in install_zeek.yml
# echo "5 * * * * NON_ROOT_ACCOUNT_NAME /usr/local/bin/zeek_log_transport.sh --dest $install_target" >>zeek_log_transport.cron
ansible-playbook -K -i "${install_target}," -e "install_hosts=${install_target}," install_pre.yml install_rita.yml install_zeek.yml
else
ansible-playbook -K -i "${install_target}," -e "install_hosts=${install_target}," install_pre.yml install_rita.yml
fi
fi
echo \
"
░█▀▀█ ▀█▀ ▀▀█▀▀ ─█▀▀█
░█▄▄▀ ░█─ ─░█── ░█▄▄█
░█─░█ ▄█▄ ─░█── ░█─░█ ${RITA_VERSION}
Brought to you by Active CounterMeasures©
"
echo "Installation complete!"
echo ""
if [[ "$_INSTALL_ZEEK" = 'true' ]]; then
echo "Please run the following commands on any new zeek sensors" >&2
echo " zeek start ; zeek enable" >&2
echo "" >&2
fi
# switch back to original working directory
popd > /dev/null
-106
View File
@@ -1,106 +0,0 @@
# ansible install playbook for rita
- name: "RITA Install: RITA installer."
hosts: "{{ install_hosts }}"
become: true
vars:
rita_version: "REPLACE_ME"
rita_container_image: "ghcr.io/activecm/rita:{{ rita_version }}"
clickhouse_container_image: clickhouse/clickhouse-server:latest
ansible_python_interpreter: /bin/python3
# the install_pre.yml script should already have been run by this point
tasks:
# make directories
- name: "RITA Install: Create configuration directories"
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: 0755
loop:
- /etc/rita/
- /opt/rita/
tags:
- docker
- rita
- linux
- linuxdeb
- linuxrpm
# install RITA
# the following pulls right from dockerhub. We may not be able to do this if the system is airgapped
- name: "RITA Install: Install {{ rita_container_image }} docker image"
block:
- name: "Pull from Github.io Container repo"
community.docker.docker_image:
name: "{{ rita_container_image }}"
source: pull
force_source: true
rescue:
- name: "RITA Install: Transfer RITA container image to target system"
copy:
src: "rita-{{ rita_version }}-image.tar"
dest: /opt/rita
owner: root
group: root
mode: 0644
- name: "Install {{ rita_container_name }} container image from file"
community.docker.docker_image_load:
path: "/opt/rita/rita-{{ rita_version }}-image.tar"
register: load_result
# this final one prints a list of the loaded images if we use the above 2 stanzas to load from a file
- name: "RITA Install: Print loaded image names"
ansible.builtin.debug:
msg: "Loaded the following images: {{ load_result.image_names | join(', ') }}"
tags:
- docker
- rita
- linux
- linuxdeb
- linuxrpm
- name: "RITA Install: Transfer rita shell script to target system"
copy:
src: ./opt/rita.sh
dest: /usr/local/bin/rita
owner: root
group: root
mode: 0755
tags:
- docker
- rita
- linux
- linuxdeb
- linuxrpm
- name: "RITA Install: Transfer rita install files to /opt/rita"
copy:
src: ./opt/
dest: /opt/rita
owner: root
group: root
mode: 0755
tags:
- docker
- rita
- linux
- linuxdeb
- linuxrpm
- name: "RITA Install: Transfer rita user files to /etc/rita"
copy:
src: ./etc/
dest: /etc/rita
owner: root
group: root
mode: 0755
tags:
- docker
- rita
- linux
- linuxdeb
- linuxrpm
-216
View File
@@ -1,216 +0,0 @@
# ansible install playbook for docker-zeek
- name: "Zeek Install: Zeek installer"
hosts: "{{ install_hosts }}"
become: true
vars:
zeek_version: "REPLACE_ME"
zeek_container_image: "activecm/zeek:{{ zeek_version }}"
clickhouse_container_image: clickhouse/clickhouse-server:latest
ansible_python_interpreter: /bin/python3
local_known_hosts: "{{ lookup('env', 'HOME') }}/.ssh/known_hosts"
# the install_pre.yml script should already have been run by this point
tasks:
# make directories
- name: "Zeek Install: Create zeek directories"
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: 0755
loop:
- /opt/zeek/
- /opt/zeek/etc/
- /opt/zeek/logs/
- /opt/zeek/logs/stats/
- /opt/zeek/manual-logs/
- /opt/zeek/share/
- /opt/zeek/share/zeek/
- /opt/zeek/share/zeek/site/
- /opt/zeek/share/zeek/site/autoload/
- /opt/zeek/spool/
- /opt/zeek/spool/installed-scripts-do-not-touch/
- /opt/zeek/spool/manager/
- /opt/zeek/spool/proxy-1/
- /opt/zeek/spool/tmp/
tags:
- docker
- zeek
- linux
- linuxdeb
- linuxrpm
# install Zeek
# the following pulls right from dockerhub. We may not be able to do this if the system is airgapped
- name: "Pull from dockerhub container repo"
community.docker.docker_image:
name: "{{ zeek_container_image }}"
source: pull
force_source: true
tags:
- docker
- zeek
- linux
- linuxdeb
- linuxrpm
- name: "Zeek Install: Transfer zeek shell script to target system"
copy:
src: ./opt/zeek
dest: /usr/local/bin/zeek
owner: root
group: root
mode: 0755
tags:
- docker
- zeek
- linux
- linuxdeb
- linuxrpm
# TODO: Fix and add this back in after RITA#65 is done
# - name: "Zeek Install: Transfer zeek_log_transport.sh shell script to target system."
# copy:
# src: ./opt/zeek_log_transport.sh
# dest: /usr/local/bin/zeek_log_transport.sh
# owner: root
# group: root
# mode: 0755
# when: ansible_connection != "local"
# tags:
# - docker
# - zeek
# - linux
# - linuxdeb
# - linuxrpm
# TODO: Fix and add this back in after RITA#65 is done
# - name: "Zeek Install: Transfer zeek_log_transport.cron to target system."
# copy:
# src: "{{ lookup('env', 'PWD') }}/zeek_log_transport.cron"
# dest: /etc/cron.d/zeek_log_transport
# owner: root
# group: root
# mode: 0644
# when: ansible_connection != "local"
# tags:
# - docker
# - zeek
# - linux
# - linuxdeb
# - linuxrpm
# TODO: Fix and add this back in after RITA#65 is done
# - name: "Zeek Install: Place name of remote user in zeek_log_transport cron file"
# ansible.builtin.replace:
# path: /etc/cron.d/zeek_log_transport
# regexp: "^(.*)NON_ROOT_ACCOUNT_NAME(.*)$"
# replace: '\1{{ lookup("env", "USER") }}\2'
# mode: 0644
# owner: root
# group: root
# when: ansible_connection != "local"
# tags:
# - docker
# - zeek
# - linux
# - linuxdeb
# - linuxrpm
- name: "Zeek Install: check whether node.cfg exists to decide if this is the first time running zeek"
stat:
path: /opt/zeek/etc/node.cfg
register: node_stat
# this cannot be run on the first install, as zeekcfg needs to be run first to let the user select their interface(s).
# the user will need to run this by hand after the install completes.
- name: "Zeek Install: Start Zeek"
shell: "/usr/local/bin/zeek start"
when: (node_stat.stat.exists == true)
tags:
- docker
- hunt
- linux
- linuxdeb
- linuxrpm
# this cannot be run on the first install, as zeekcfg needs to be run first to let the user select their interface(s).
# the user will need to run this by hand after the install completes.
- name: "Zeek Install: Start Zeek on future reboots"
shell: "/usr/local/bin/zeek enable"
when: (node_stat.stat.exists == true)
tags:
- docker
- hunt
- linux
- linuxdeb
- linuxrpm
# TODO: Fix and add this back in after RITA#65 is done
# - name: "Zeek Install: Create .ssh directory."
# ansible.builtin.file:
# path: "{{ lookup('env', 'HOME') }}/.ssh"
# state: directory
# owner: "{{ lookup('env', 'USER') }}"
# mode: 0700
# when: ansible_connection != "local"
# tags:
# - docker
# - zeek
# - linux
# - linuxdeb
# - linuxrpm
# TODO: Fix and add this back in after RITA#65 is done
# - name: "Zeek Install: Transfer dataimport private key to .ssh"
# copy:
# src: "{{ lookup('env', 'PWD') }}/id_rsa_dataimport"
# dest: "{{ lookup('env', 'HOME') }}/.ssh/"
# owner: "{{ lookup('env', 'USER') }}"
# mode: 0600
# backup: true
# when: ansible_connection != "local"
# tags:
# - docker
# - zeek
# - linux
# - linuxdeb
# - linuxrpm
# TODO: Fix and add this back in after RITA#65 is done
# - name: "Zeek Install: Transfer dataimport public key to .ssh"
# copy:
# src: "{{ lookup('env', 'PWD') }}/id_rsa_dataimport.pub"
# dest: "{{ lookup('env', 'HOME') }}/.ssh/"
# owner: "{{ lookup('env', 'USER') }}"
# mode: 0644
# backup: true
# when: ansible_connection != "local"
# tags:
# - docker
# - zeek
# - linux
# - linuxdeb
# - linuxrpm
# TODO: Fix and add this back in after RITA#65 is done
# - name: "Zeek Install: Update known_hosts with entries from known_hosts on this system"
# ansible.builtin.blockinfile:
# block: "{{ lookup('ansible.builtin.file', '{{ local_known_hosts }}') }}"
# path: "{{ lookup('env', 'HOME') }}/.ssh/known_hosts"
# marker: "# {mark} ANSIBLE MANAGED BLOCK APPENDED DURING ZEEK INSTALL"
# owner: "{{ lookup('env', 'USER') }}"
# backup: true
# create: true
# mode: 0644
# when: ansible_connection != "local"
# tags:
# - docker
# - zeek
# - linux
# - linuxdeb
# - linuxrpm
-314
View File
@@ -1,314 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
askYN() {
# Prints a question mark, reads repeatedly until the user
# responds with t/T/y/Y or f/F/n/N.
TESTYN=""
while [ "$TESTYN" != 'Y' ] && [ "$TESTYN" != 'N' ] ; do
echo -n '? ' >&2
read -e TESTYN <&2 || :
case $TESTYN in
T*|t*|Y*|y*) TESTYN='Y' ;;
F*|f*|N*|n*) TESTYN='N' ;;
esac
done
if [ "$TESTYN" = 'Y' ]; then
return 0 #True
else
return 1 #False
fi
}
fail() {
echo "$@ , exiting." >&2
exit 1
}
require_util() {
#Returns true if all binaries listed as parameters exist somewhere in the path, False if one or more missing.
while [ -n "$1" ]; do
if ! type -path "$1" >/dev/null 2>/dev/null ; then
echo Missing utility "$1". Please install it. >&2
return 1 #False, app is not available.
fi
shift
done
return 0 #True, app is there.
} #End of require_util
status() {
if [ "$verbose" = 'yes' ]; then
echo "== $@" >&2
fi
}
step() {
if [ "$step" = 'yes' ]; then
echo "About to $@ , continue" >&2
if askYN ; then
return
else
exit 99
fi
fi
}
show_help() {
echo "This tool does all the setup needed to ssh to one or more hosts using ssh keys." >&2
echo "Usage:" >&2
echo >&2
echo "$0 [host] [host] [host]..." >&2
echo -e "\tDo basic setup and install ssh key on all hosts specified on the command line." >&2
echo -e "\tHost may optionally include a username, like bob@www.example.com" >&2
echo "$0 -v [host]..." >&2
echo -e "\tBe verbose - show steps to be taken." >&2
echo "$0 -s [host]..." >&2
echo -e "\tSingle step mode - ask permission before making changes (verbose mode is automatically enabled in single step mode)." >&2
echo "$0 --authpath /etc/ssh/keys-root/ [host]..." >&2
echo -e "\tLocation of the authorized_keys file (useful for VMWare ESXi). This path is used for _all_ hosts on this command line." >&2
echo "$0 -h" >&2
echo "$0 --help" >&2
echo -e "\tShow this help text." >&2
exit 0
}
load_params() {
target_list=''
if [ "z$1" = "z" ]; then
echo "No target systems requested, this will perform basic ssh setup only." >&2
fi
while [ -n "$1" ]; do
case "$1" in
-v|--verbose)
verbose='yes'
;;
-s|--step)
verbose='yes'
step='yes'
;;
-h|--help)
show_help
;;
--authpath)
authpath="$2"
shift
;;
*)
target_list="$target_list $1"
;;
esac
shift
done
}
check_requirements() {
status "Checking system requirements"
require_util awk basename chmod cp date dig egrep head mkdir mktemp ssh ssh-add ssh-keygen touch uniq || fail "Missing one or more tools, please install and rerun"
step "perform system checks"
mkdir -p "$HOME/.ssh"
chmod -R go-rwx "$HOME/.ssh"
if [ -z "$SSH_AUTH_SOCK" ] || [ ! -S "$SSH_AUTH_SOCK" ]; then
fail 'We do not appear to have an ssh-agent to talk to. You may want to add the following 3 lines to ~/.bashrc on this system :
if [ -z "$SSH_AUTH_SOCK" ] || [ ! -S "$SSH_AUTH_SOCK" ]; then
eval $(ssh-agent -s)
fi
(and then logout, log back in, and try again.)'
fi
[ -w "$HOME/.ssh" ] || fail "User .ssh directory does not appear to be writeable"
[ -s "$HOME/.ssh/config" ] || touch "$HOME/.ssh/config"
}
check_key() {
status "Checking that we have a keypair available to load into the agent"
#Confirm we have an ssh RSA keypair; create if not.
if [ ! -s "$HOME/.ssh/id_rsa" ] && [ ! -s "$HOME/.ssh/id_rsa.pub" ]; then
status "No SSH RSA keypair, creating one. We strongly encourage you to provide a strong passphrase."
step "create ssh rsa keypair"
ssh-keygen -t rsa-sha2-512 -b 4096 -f "$HOME/.ssh/id_rsa" #We don't force the comment; "user@host" is the default
elif [ -s "$HOME/.ssh/id_rsa" ] && [ ! -s "$HOME/.ssh/id_rsa.pub" ]; then
status "private key available but no public; we'll create the public key. You will be asked for your id_rsa private key passphrase."
step "regenerate public key from private"
ssh-keygen -y -f "$HOME/.ssh/id_rsa" >"$HOME/.ssh/id_rsa.pub"
elif [ ! -s "$HOME/.ssh/id_rsa" ] && [ -s "$HOME/.ssh/id_rsa.pub" ]; then
status "Public key available, but no private"
fail "Missing private key, please provide $HOME/.ssh/id_rsa"
elif [ -s "$HOME/.ssh/id_rsa" ] && [ -s "$HOME/.ssh/id_rsa.pub" ]; then
status "Both private and public keys available"
fi
}
check_agent() {
status "Checking that keys are loaded into the agent"
#Confirm we have at least one key loaded into ssh-agent.
if [ $(ssh-add -l | wc -l) -eq 0 ] || [ "$(ssh-add -l)" = 'The agent has no identities.' ]; then
#No keys currently loaded into the agent, load at least one
[ -s "$HOME/.ssh/id_rsa" -a -s "$HOME/.ssh/id_rsa.pub" ] || check_key
status "About to load keys into ssh-agent; you may be asked for your passphrase(s)"
step "load keys into agent"
ssh-add
fi
if [ $(ssh-add -l | wc -l) -eq 0 ] || [ "$(ssh-add -l)" = 'The agent has no identities.' ]; then
fail "Unable to get any keys into the ssh-agent"
fi
status "ssh-agent has at least one key loaded, continuing"
}
prepend_config_block() {
#We prepend new stanzas since the end of the file may contain a default stanza like "Host * ......"
if [ -s "$HOME/.ssh/config" ]; then
cp -p "$HOME/.ssh/config" "$HOME/.ssh/config.$(date '+%Y%m%d%H%M%S%N')"
elif [ ! -e "$HOME/.ssh/config" ]; then
touch "$HOME/.ssh/config"
fi
new_config=$(mktemp -q /tmp/$(basename $0).XXXXXX) || fail "Unable to make temporary file during ~/.ssh/config update."
echo -e "$1" >"$new_config"
cat "$HOME/.ssh/config" >>"$new_config"
step "move new config file ($new_config) over existing $HOME/.ssh/config"
mv "$new_config" "$HOME/.ssh/config"
}
check_config_block() {
#Make sure we have a config block for this host, and add one if not
#Param 1: user, Param 2: host, Param 3: optional hostname or ip address to use as "Hostname"
status "Checking if we have a config block for $2"
if [ -n "$1" ]; then
user_line=" User ${1}"
else
user_line=''
fi
if egrep -q "(^Host.*[[:space:]]${2}[[:space:]]|^Host.*[[:space:]]${2}\$)" "$HOME/.ssh/config" ; then
status "$2 has a configuration block in ~/.ssh/config"
else
status "$2 does not have a configuration block in ~/.ssh/config, adding one."
if [ -n "$3" ]; then
status "User-supplied target IP or hostname ($3) for $2"
prepend_config_block "\nHost ${2} ${3}\n\tHostname\t\t${3}\n\tHostKeyAlias\t\t${2}\n${user_line}\n"
elif [ -n "$(which getent 2>/dev/null)" ] && [ -n "$(getent hosts "$2")" ]; then
status "running on non-mac, appears we have an entry in /etc/hosts"
ip4=$(getent ahostsv4 "$2" | awk '{print $1}' | uniq | head -1)
ip6=$(getent ahostsv6 "$2" | awk '{print $1}' | uniq | head -1)
if [ -n "$ip4" -a -n "$ip6" ]; then
status "Have both ipv4 and ipv6 addresses for $2, adding both blocks"
prepend_config_block "\nHost ${2} ${ip4}\n\tHostname\t\t$ip4\n\tHostKeyAlias\t\t${2}\n${user_line}\n\nHost ${2}-v6 ${ip6}\n\tHostname\t\t$ip6\n\tHostKeyAlias\t\t${2}\n${user_line}\n"
elif [ -n "$ip4" -o -n "$ip6" ]; then #One or the other but not both, which is why we can have both ip4 and ip6 on the Hostname line.
prepend_config_block "\nHost ${2} ${ip4}${ip6}\n\tHostname\t\t${ip4}${ip6}\n\tHostKeyAlias\t\t${2}\n${user_line}\n"
#We don't include a block for neither ip4 nor ip6 set as "getent hosts" did successfully get one or the other.
fi
elif [ -n "$(which dscacheutil 2>/dev/null)" ] && [ -n "$(dscacheutil -q host -a name "$2")" ]; then
status "running on mac, appears we have an entry in /etc/hosts"
local_ip=$(dscacheutil -q host -a name "$2" | grep '^ip_address' | awk '{print $2}')
prepend_config_block "\nHost ${2} ${local_ip}\n\tHostname\t\t${local_ip}\n\tHostKeyAlias\t\t${2}\n${user_line}\n"
else
status "No user-supplied target IP or hostname for $2, look one up"
ip4=$(dig +nocomment +short ${2} A 2>/dev/null | sed -e 's/;;.*//' | grep -v '^$' | head -1)
ip6=$(dig +nocomment +short ${2} AAAA 2>/dev/null | sed -e 's/;;.*//' | grep -v '^$' | head -1)
if [ -n "$ip4" ]; then
if [ -n "$ip6" ]; then
status "Have both ipv4 and ipv6 addresses for $2, adding both blocks"
prepend_config_block "\nHost ${2} ${ip4}\n\tHostname\t\t${ip4}\n\tHostKeyAlias\t\t${2}\n${user_line}\n\nHost ${2}-v6 ${ip6}\n\tHostname\t\t${ip6}\n\tHostKeyAlias\t\t${2}\n${user_line}\n"
else
status "Have an IPv4 address only for $2"
prepend_config_block "\nHost ${2} ${ip4}\n\tHostname\t\t${ip4}\n\tHostKeyAlias\t\t${2}\n${user_line}\n"
fi
else
if [ -n "$ip6" ]; then
status "Have an ipv6 address only for $2"
prepend_config_block "\nHost ${2} ${2}-v6 ${ip6}\n\tHostname\t\t${ip6}\n\tHostKeyAlias\t\t${2}\n${user_line}\n"
else
#echo "== No ipv4 or ipv6 address found for ${2}. Please enter an IP address to use:" >&2
#read ip_or_hostname <&2
#if [ -z "$ip_or_hostname" ]; then
# ip_or_hostname="$2"
#fi
status "We could not lookup a DNS address for $2 so we will just use $2 in the ssh Hostname field."
ip_or_hostname="$2"
prepend_config_block "\nHost ${2} ${ip_or_hostname}\n\tHostname\t\t${ip_or_hostname}\n\tHostKeyAlias\t\t${2}\n${user_line}\n"
fi
fi
fi
fi
}
sshprep_main() {
load_params "$@"
check_requirements
check_agent
status "Retrieving public key(s)"
pub_key_content="$(ssh-add -L)"
retval=0
for one_target in $target_list ; do
step "start setting up $one_target"
status "Setting up ssh connection to $one_target"
if echo "$one_target" | grep -q '@' - ; then
#We have an "@" in the target, so this is "user@host"
one_user="${one_target%%@*}"
one_host="${one_target##*@}"
else
#No "@" in target, so this is just a host
one_user=''
one_host="$one_target"
fi
#Check if we have a config block for this target; create one if not, place at top of file
check_config_block "$one_user" "$one_host" #FIXME - arrange a way for the user to supply an optional hostname/ip as param3
status "Checking that we can now connect without using a password."
status "If this is your first time connecting to $one_target, you may be asked to verify a fingerprint. To see the correct value, run 'ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub' on that system's console."
step "confirm $one_target reachable by key"
if ssh -o 'PasswordAuthentication=no' -o 'PreferredAuthentications=publickey' "$one_target" 'touch .ssh/reached-by-key ; true' 2>/dev/null ; then
status "Key appears to be successfully installed on $one_target"
else
status "Sending public key(s) over to $one_target . You may be asked for your password for this account."
step "send public keys over to $one_target"
if [ -n "$authpath" ]; then
echo "$pub_key_content" \
| ssh "$one_target" 'mkdir -p '"$authpath"' ; cat >>'"$authpath"'/authorized_keys ; chmod go-rwx '"$authpath"' '"$authpath"'/authorized_keys'
else
echo "$pub_key_content" \
| ssh "$one_target" 'mkdir -p .ssh ; cat >>.ssh/authorized_keys ; chmod go-rwx ./ .ssh/ .ssh/authorized_keys'
fi
step "confirm $one_target reachable by key"
if ssh -o 'PasswordAuthentication=no' -o 'PreferredAuthentications=publickey' "$one_target" 'touch .ssh/reached-by-key ; true' 2>/dev/null ; then
status "Key is now successfully installed on $one_target"
else
status "Unable to reach $one_target by ssh key."
retval=2
fi
fi
done
exit "$retval"
}
sshprep_main "$@"
+14 -17
View File
@@ -1,12 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
# dev script for testing the installer on a remote droplet.
# generates the installer, copies it to the droplet, runs it, then runs tests.
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
RITA_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
droplet_ip="$1"
if [[ -z "$droplet_ip" ]]; then
echo "droplet ip was not provided"
echo "Usage: $0 <droplet_ip>" >&2
exit 1
fi
@@ -16,30 +19,24 @@ if VERSION="$(git -C "$RITA_DIR" describe --tags --exact-match 2>/dev/null)"; th
elif VERSION="$(git -C "$RITA_DIR" describe --tags --dirty --always 2>/dev/null)"; then
: # dev
else
fail "Unable to determine RITA_VERSION."
echo "Unable to determine RITA_VERSION." >&2
exit 1
fi
[[ -n "$VERSION" ]] || { echo "Unable to determine RITA_VERSION." >&2; exit 1; }
# generate dev installer (bundles Docker images for offline install)
"${SCRIPT_DIR}/create-dev-installer.sh"
# generate installer
"${SCRIPT_DIR}/generate_installer.sh"
INSTALLER_TARBALL="${SCRIPT_DIR}/rita-${VERSION}.tar.gz"
[[ -f "$INSTALLER_TARBALL" ]] || { echo "RITA installer tarball not found." >&2; exit 1; }
INSTALLER_DIR="${SCRIPT_DIR}/rita-${VERSION}-installer"
# verify tar ball exists
[[ -f "${INSTALLER_DIR}.tar.gz" ]] || { echo "RITA installer tarball not found." >&2; exit 1; }
tar -xf "${INSTALLER_DIR}.tar.gz"
[[ -f "${INSTALLER_DIR}/install_rita.sh" ]] || { echo "RITA installer script not found." >&2; exit 1; }
"${INSTALLER_DIR}/install_rita.sh" "root@$droplet_ip"
# # # # ansible-playbook -i digitalocean_inventory.py -e "install_hosts=${droplet_ip}" "./rita-${VERSION}-installer/install_rita.yml"
# copy tarball to droplet, extract, and run
scp "$INSTALLER_TARBALL" "root@$droplet_ip":/root/
ssh -t "root@$droplet_ip" "cd /root && tar -xf rita-${VERSION}.tar.gz && cd rita-${VERSION}-installer && ./install_rita.sh"
# copy over test data
scp -r "${RITA_DIR}/test_data/open_sni" "root@$droplet_ip":/root/sample_logs
# # copy over test script
# copy over test script and run it
scp "${SCRIPT_DIR}/test_installed.sh" "root@$droplet_ip":/root/test_installed.sh
# run test script
ssh -t "root@$droplet_ip" /root/test_installed.sh "$VERSION"
#