feat: CI/CD pour packages .deb et .rpm + tests d'installation
Nouveaux workflows GitHub Actions: - .github/workflows/build-deb.yml : Build et release DEB sur Ubuntu - .github/workflows/build-rpm.yml : Build et release RPM sur Fedora - Déclenchement sur tags v*, push main/master, workflow_dispatch - Upload des artifacts et création automatique de release Système de build de packages: - packaging/build-deb.sh : Script de build .deb avec sanitization version - packaging/build-rpm.sh : Script de build .rpm (via Docker) - packaging/Dockerfile.deb : Container Ubuntu 22.04 pour build DEB - packaging/Dockerfile.rpm : Container Go 1.24 + rpm pour build RPM Fichiers de configuration systemd: - packaging/systemd/ja4sentinel.service : Unit avec security hardening * NoNewPrivileges, ProtectSystem, ProtectHome * CAP_NET_RAW, CAP_NET_ADMIN pour packet capture - packaging/systemd/config.yml : Configuration par défaut Scripts mainteneur DEB: - packaging/deb/postinst : Création user/group, dirs, config - packaging/deb/prerm : Stop service avant upgrade/remove - packaging/deb/postrm : Cleanup complet en purge Spec file RPM: - packaging/rpm/ja4sentinel.spec : Spec complet avec dependencies * Requires: systemd, libpcap * %pre/%post/%preun/%postun scripts Tests d'installation dans containers: - packaging/test/test-deb.sh : Build + test Docker Ubuntu - packaging/test/test-rpm.sh : Build + test Docker Fedora - packaging/test/test-install-deb.sh : 11 tests automatisés - packaging/test/test-install-rpm.sh : 11 tests automatisés - Dockerfile.deb/rpm : Containers de test dédiés Makefile: - package-deb : Build .deb - package-rpm : Build .rpm via Docker (no-cache) - package : Build les deux - test-package-deb : Build + test installation DEB - test-package-rpm : Build + test installation RPM - test-package : Test les deux packages Tests: - ✅ DEB: 11/11 tests passés (binaire, config, service, user, dirs) - ✅ RPM: Build réussi (3.3 MB) - Version sanitization pour git tags (ex: efd4481-dirty → 0.0.0+efd4481-dirty) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
130
.github/workflows/build-deb.yml
vendored
Normal file
130
.github/workflows/build-deb.yml
vendored
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
name: Build DEB Package
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- 'go/**'
|
||||||
|
- 'cmd/**'
|
||||||
|
- 'internal/**'
|
||||||
|
- 'api/**'
|
||||||
|
- 'packaging/**'
|
||||||
|
- 'Makefile'
|
||||||
|
- 'go.mod'
|
||||||
|
- 'go.sum'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- 'go/**'
|
||||||
|
- 'cmd/**'
|
||||||
|
- 'internal/**'
|
||||||
|
- 'api/**'
|
||||||
|
- 'packaging/**'
|
||||||
|
- 'Makefile'
|
||||||
|
- 'go.mod'
|
||||||
|
- 'go.sum'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Version to build (e.g., 1.0.0)'
|
||||||
|
required: false
|
||||||
|
default: '1.0.0-dev'
|
||||||
|
|
||||||
|
env:
|
||||||
|
GO_VERSION: '1.24'
|
||||||
|
PACKAGE_NAME: ja4sentinel
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-deb:
|
||||||
|
name: Build DEB Package
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ${{ env.GO_VERSION }}
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
- name: Determine version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||||
|
VERSION="${{ github.event.inputs.version }}"
|
||||||
|
elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||||||
|
VERSION="${{ github.ref_name#v }}"
|
||||||
|
else
|
||||||
|
VERSION="0.0.0-$(git rev-parse --short HEAD)"
|
||||||
|
fi
|
||||||
|
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||||
|
echo "Building version: ${VERSION}"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
libpcap-dev \
|
||||||
|
dpkg-dev \
|
||||||
|
fakeroot \
|
||||||
|
lintian
|
||||||
|
|
||||||
|
- name: Build Go binary
|
||||||
|
run: |
|
||||||
|
make build-linux
|
||||||
|
ls -la dist/
|
||||||
|
|
||||||
|
- name: Build DEB package
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
./packaging/build-deb.sh "${VERSION}" "amd64"
|
||||||
|
|
||||||
|
- name: Run lintian checks
|
||||||
|
run: |
|
||||||
|
lintian build/deb/*.deb --suppress-tags "dir-or-file-in-/usr/share/doc" || true
|
||||||
|
|
||||||
|
- name: List build artifacts
|
||||||
|
run: |
|
||||||
|
echo "=== Build Artifacts ==="
|
||||||
|
ls -lah build/deb/
|
||||||
|
echo "=== Checksums ==="
|
||||||
|
cat build/deb/*.sha256 || true
|
||||||
|
|
||||||
|
- name: Upload DEB artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ja4sentinel-deb-amd64
|
||||||
|
path: build/deb/*.deb
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Upload checksum artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ja4sentinel-deb-checksums
|
||||||
|
path: build/deb/*.sha256
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Create release and upload assets (on tag)
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
build/deb/*.deb
|
||||||
|
build/deb/*.sha256
|
||||||
|
generate_release_notes: true
|
||||||
|
make_latest: true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
126
.github/workflows/build-rpm.yml
vendored
Normal file
126
.github/workflows/build-rpm.yml
vendored
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
name: Build RPM Package
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- 'go/**'
|
||||||
|
- 'cmd/**'
|
||||||
|
- 'internal/**'
|
||||||
|
- 'api/**'
|
||||||
|
- 'packaging/**'
|
||||||
|
- 'Makefile'
|
||||||
|
- 'go.mod'
|
||||||
|
- 'go.sum'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- 'go/**'
|
||||||
|
- 'cmd/**'
|
||||||
|
- 'internal/**'
|
||||||
|
- 'api/**'
|
||||||
|
- 'packaging/**'
|
||||||
|
- 'Makefile'
|
||||||
|
- 'go.mod'
|
||||||
|
- 'go.sum'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Version to build (e.g., 1.0.0)'
|
||||||
|
required: false
|
||||||
|
default: '1.0.0-dev'
|
||||||
|
|
||||||
|
env:
|
||||||
|
GO_VERSION: '1.24'
|
||||||
|
PACKAGE_NAME: ja4sentinel
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-rpm:
|
||||||
|
name: Build RPM Package
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ${{ env.GO_VERSION }}
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
- name: Determine version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||||
|
VERSION="${{ github.event.inputs.version }}"
|
||||||
|
elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||||||
|
VERSION="${{ github.ref_name#v }}"
|
||||||
|
else
|
||||||
|
VERSION="0.0.0-$(git rev-parse --short HEAD)"
|
||||||
|
fi
|
||||||
|
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||||
|
echo "Building version: ${VERSION}"
|
||||||
|
|
||||||
|
- name: Set up RPM build environment
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
rpm \
|
||||||
|
rpmbuild \
|
||||||
|
libpcap-dev \
|
||||||
|
libpcap0.8-dev
|
||||||
|
|
||||||
|
- name: Build Go binary
|
||||||
|
run: |
|
||||||
|
make build-linux
|
||||||
|
ls -la dist/
|
||||||
|
|
||||||
|
- name: Build RPM package
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
./packaging/build-rpm.sh "${VERSION}" "x86_64"
|
||||||
|
|
||||||
|
- name: List build artifacts
|
||||||
|
run: |
|
||||||
|
echo "=== Build Artifacts ==="
|
||||||
|
ls -lah build/rpm/
|
||||||
|
echo "=== Checksums ==="
|
||||||
|
cat build/rpm/*.sha256 || true
|
||||||
|
|
||||||
|
- name: Upload RPM artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ja4sentinel-rpm-x86_64
|
||||||
|
path: build/rpm/*.rpm
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Upload checksum artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ja4sentinel-rpm-checksums
|
||||||
|
path: build/rpm/*.sha256
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Create release and upload assets (on tag)
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
build/rpm/*.rpm
|
||||||
|
build/rpm/*.sha256
|
||||||
|
generate_release_notes: true
|
||||||
|
make_latest: true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
39
Makefile
39
Makefile
@ -1,4 +1,4 @@
|
|||||||
.PHONY: build build-docker test test-docker test-integration lint clean help docker-build-dev docker-build-runtime
|
.PHONY: build build-docker test test-docker test-integration lint clean help docker-build-dev docker-build-runtime package package-deb package-rpm
|
||||||
|
|
||||||
# Docker parameters
|
# Docker parameters
|
||||||
DOCKER=docker
|
DOCKER=docker
|
||||||
@ -15,9 +15,13 @@ TEST_SERVER_IMAGE=ja4sentinel-test-server:latest
|
|||||||
BINARY_NAME=ja4sentinel
|
BINARY_NAME=ja4sentinel
|
||||||
BINARY_PATH=./cmd/ja4sentinel
|
BINARY_PATH=./cmd/ja4sentinel
|
||||||
DIST_DIR=dist
|
DIST_DIR=dist
|
||||||
|
BUILD_DIR=build
|
||||||
|
|
||||||
|
# Package version (strip 'v' prefix from git tags)
|
||||||
|
PKG_VERSION=$(shell git describe --tags --always --dirty 2>/dev/null | sed 's/^v//')
|
||||||
|
|
||||||
# Build flags
|
# Build flags
|
||||||
VERSION=$(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
|
VERSION=$(PKG_VERSION)
|
||||||
BUILD_TIME=$(shell date -u '+%Y-%m-%d_%H:%M:%S')
|
BUILD_TIME=$(shell date -u '+%Y-%m-%d_%H:%M:%S')
|
||||||
GIT_COMMIT=$(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
GIT_COMMIT=$(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
||||||
|
|
||||||
@ -83,9 +87,40 @@ lint: docker-build-dev
|
|||||||
fmt:
|
fmt:
|
||||||
gofmt -w .
|
gofmt -w .
|
||||||
|
|
||||||
|
## package: Build all packages (deb + rpm)
|
||||||
|
package: package-deb package-rpm
|
||||||
|
|
||||||
|
## package-deb: Build DEB package
|
||||||
|
package-deb: build-linux
|
||||||
|
./packaging/build-deb.sh "$(PKG_VERSION)" "amd64"
|
||||||
|
|
||||||
|
## package-rpm: Build RPM package (requires Docker)
|
||||||
|
package-rpm: build-linux
|
||||||
|
mkdir -p build
|
||||||
|
docker build --no-cache -t ja4sentinel-packager-rpm \
|
||||||
|
--build-arg VERSION=$(PKG_VERSION) \
|
||||||
|
--build-arg ARCH=x86_64 \
|
||||||
|
-f packaging/Dockerfile.rpm .
|
||||||
|
@echo "Extracting RPM from Docker image..."
|
||||||
|
docker run --rm ja4sentinel-packager-rpm sh -c 'cat /packages/*.rpm' > build/ja4sentinel.rpm
|
||||||
|
@echo "RPM package created: build/ja4sentinel.rpm"
|
||||||
|
ls -la build/*.rpm
|
||||||
|
|
||||||
|
## test-package-deb: Test DEB package installation in Docker
|
||||||
|
test-package-deb: package-deb
|
||||||
|
./packaging/test/test-deb.sh
|
||||||
|
|
||||||
|
## test-package-rpm: Test RPM package installation in Docker
|
||||||
|
test-package-rpm: package-rpm
|
||||||
|
./packaging/test/test-rpm.sh
|
||||||
|
|
||||||
|
## test-package: Test all packages installation
|
||||||
|
test-package: test-package-deb test-package-rpm
|
||||||
|
|
||||||
## clean: Clean build artifacts and Docker images
|
## clean: Clean build artifacts and Docker images
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(DIST_DIR)/
|
rm -rf $(DIST_DIR)/
|
||||||
|
rm -rf $(BUILD_DIR)/
|
||||||
rm -f coverage.out coverage.html
|
rm -f coverage.out coverage.html
|
||||||
$(DOCKER) rmi $(DEV_IMAGE) 2>/dev/null || true
|
$(DOCKER) rmi $(DEV_IMAGE) 2>/dev/null || true
|
||||||
$(DOCKER) rmi $(RUNTIME_IMAGE) 2>/dev/null || true
|
$(DOCKER) rmi $(RUNTIME_IMAGE) 2>/dev/null || true
|
||||||
|
|||||||
23
packaging/Dockerfile.deb
Normal file
23
packaging/Dockerfile.deb
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Dockerfile for building DEB packages
|
||||||
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# Install build dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
golang-go \
|
||||||
|
git \
|
||||||
|
make \
|
||||||
|
libpcap-dev \
|
||||||
|
dpkg-dev \
|
||||||
|
fakeroot \
|
||||||
|
lintian \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Default command: build DEB package
|
||||||
|
CMD ["./packaging/build-deb.sh", "1.0.0", "amd64"]
|
||||||
35
packaging/Dockerfile.rpm
Normal file
35
packaging/Dockerfile.rpm
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Dockerfile for building RPM packages
|
||||||
|
# Use Go 1.24 as base to ensure correct Go version
|
||||||
|
FROM golang:1.24-bookworm AS builder
|
||||||
|
|
||||||
|
# Install RPM build tools
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
rpm \
|
||||||
|
rpm-common \
|
||||||
|
rpm2cpio \
|
||||||
|
libpcap-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build binary
|
||||||
|
ARG VERSION=1.0.0
|
||||||
|
RUN mkdir -p dist && \
|
||||||
|
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
|
||||||
|
go build -buildvcs=false -o dist/ja4sentinel-linux-amd64 ./cmd/ja4sentinel
|
||||||
|
|
||||||
|
# Build RPM
|
||||||
|
ARG ARCH=x86_64
|
||||||
|
RUN mkdir -p /app/packages && \
|
||||||
|
./packaging/build-rpm.sh "${VERSION}" "${ARCH}" && \
|
||||||
|
cp /app/build/rpm/*.rpm /app/packages/
|
||||||
|
|
||||||
|
# Final stage - minimal image with just the RPM
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
COPY --from=builder /app/packages/ /packages/
|
||||||
|
|
||||||
|
CMD ["ls", "-la", "/packages/"]
|
||||||
113
packaging/build-deb.sh
Executable file
113
packaging/build-deb.sh
Executable file
@ -0,0 +1,113 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Build script for .deb package
|
||||||
|
# Usage: ./build-deb.sh [version] [architecture]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Sanitize version for Debian package (must start with digit)
|
||||||
|
VERSION="${1:-1.0.0}"
|
||||||
|
ARCH="${2:-amd64}"
|
||||||
|
PACKAGE_NAME="ja4sentinel"
|
||||||
|
|
||||||
|
# Convert git version to Debian-compatible format
|
||||||
|
# e.g., "v1.0.0" -> "1.0.0", "efd4481-dirty" -> "0.0.0+efd4481"
|
||||||
|
if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
||||||
|
# Already a valid semver
|
||||||
|
DEB_VERSION="$VERSION"
|
||||||
|
elif [[ "$VERSION" =~ ^v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
|
||||||
|
# v-prefixed semver
|
||||||
|
DEB_VERSION="${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
# Git hash or other format -> use 0.0.0+<hash>
|
||||||
|
DEB_VERSION="0.0.0+${VERSION//[^a-zA-Z0-9+.-]/_}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== Building ${PACKAGE_NAME} ${DEB_VERSION} for ${ARCH} ==="
|
||||||
|
|
||||||
|
# Directories
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
BUILD_DIR="${PROJECT_ROOT}/build/deb"
|
||||||
|
PACKAGE_DIR="${BUILD_DIR}/${PACKAGE_NAME}_${DEB_VERSION}_${ARCH}"
|
||||||
|
|
||||||
|
# Clean and create build directory
|
||||||
|
rm -rf "${BUILD_DIR}"
|
||||||
|
mkdir -p "${PACKAGE_DIR}"
|
||||||
|
|
||||||
|
# Create package structure
|
||||||
|
mkdir -p "${PACKAGE_DIR}/usr/bin"
|
||||||
|
mkdir -p "${PACKAGE_DIR}/etc/ja4sentinel"
|
||||||
|
mkdir -p "${PACKAGE_DIR}/var/lib/ja4sentinel"
|
||||||
|
mkdir -p "${PACKAGE_DIR}/var/log/ja4sentinel"
|
||||||
|
mkdir -p "${PACKAGE_DIR}/var/run/ja4sentinel"
|
||||||
|
mkdir -p "${PACKAGE_DIR}/usr/lib/systemd/system"
|
||||||
|
mkdir -p "${PACKAGE_DIR}/usr/share/ja4sentinel"
|
||||||
|
mkdir -p "${PACKAGE_DIR}/DEBIAN"
|
||||||
|
|
||||||
|
# Copy binary (build if not exists)
|
||||||
|
if [ ! -f "${PROJECT_ROOT}/dist/ja4sentinel-linux-amd64" ]; then
|
||||||
|
echo "Building binary..."
|
||||||
|
cd "${PROJECT_ROOT}"
|
||||||
|
make build-linux
|
||||||
|
fi
|
||||||
|
cp "${PROJECT_ROOT}/dist/ja4sentinel-linux-amd64" "${PACKAGE_DIR}/usr/bin/ja4sentinel"
|
||||||
|
chmod 755 "${PACKAGE_DIR}/usr/bin/ja4sentinel"
|
||||||
|
|
||||||
|
# Copy systemd service
|
||||||
|
cp "${SCRIPT_DIR}/systemd/ja4sentinel.service" "${PACKAGE_DIR}/usr/lib/systemd/system/ja4sentinel.service"
|
||||||
|
chmod 644 "${PACKAGE_DIR}/usr/lib/systemd/system/ja4sentinel.service"
|
||||||
|
|
||||||
|
# Copy default config
|
||||||
|
cp "${SCRIPT_DIR}/systemd/config.yml" "${PACKAGE_DIR}/etc/ja4sentinel/config.yml.default"
|
||||||
|
cp "${SCRIPT_DIR}/systemd/config.yml" "${PACKAGE_DIR}/usr/share/ja4sentinel/config.yml"
|
||||||
|
chmod 640 "${PACKAGE_DIR}/etc/ja4sentinel/config.yml.default"
|
||||||
|
chmod 640 "${PACKAGE_DIR}/usr/share/ja4sentinel/config.yml"
|
||||||
|
|
||||||
|
# Copy maintainer scripts
|
||||||
|
cp "${SCRIPT_DIR}/deb/postinst" "${PACKAGE_DIR}/DEBIAN/postinst"
|
||||||
|
cp "${SCRIPT_DIR}/deb/prerm" "${PACKAGE_DIR}/DEBIAN/prerm"
|
||||||
|
cp "${SCRIPT_DIR}/deb/postrm" "${PACKAGE_DIR}/DEBIAN/postrm"
|
||||||
|
chmod 755 "${PACKAGE_DIR}/DEBIAN/postinst"
|
||||||
|
chmod 755 "${PACKAGE_DIR}/DEBIAN/prerm"
|
||||||
|
chmod 755 "${PACKAGE_DIR}/DEBIAN/postrm"
|
||||||
|
|
||||||
|
# Create control file
|
||||||
|
cat > "${PACKAGE_DIR}/DEBIAN/control" << EOF
|
||||||
|
Package: ${PACKAGE_NAME}
|
||||||
|
Version: ${DEB_VERSION}
|
||||||
|
Section: net
|
||||||
|
Priority: optional
|
||||||
|
Architecture: ${ARCH}
|
||||||
|
Depends: systemd, libpcap0.8
|
||||||
|
Maintainer: JA4Sentinel Team <team@example.com>
|
||||||
|
Description: JA4 TLS fingerprinting daemon
|
||||||
|
JA4Sentinel is a Go-based tool for capturing network traffic on Linux servers,
|
||||||
|
extracting client-side TLS handshakes, generating JA4 signatures, enriching
|
||||||
|
with IP/TCP metadata, and logging results to configurable outputs.
|
||||||
|
.
|
||||||
|
Features:
|
||||||
|
- Network packet capture with BPF filters
|
||||||
|
- TLS ClientHello extraction
|
||||||
|
- JA4/JA3 fingerprint generation
|
||||||
|
- IP/TCP metadata enrichment
|
||||||
|
- Multiple output formats (stdout, file, UNIX socket)
|
||||||
|
- Structured JSON logging for systemd/journald
|
||||||
|
Homepage: https://github.com/your-repo/ja4sentinel
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create conffiles
|
||||||
|
echo "/etc/ja4sentinel/config.yml.default" > "${PACKAGE_DIR}/DEBIAN/conffiles"
|
||||||
|
|
||||||
|
# Build the package
|
||||||
|
echo "Building .deb package..."
|
||||||
|
cd "${BUILD_DIR}"
|
||||||
|
dpkg-deb --build "${PACKAGE_NAME}_${DEB_VERSION}_${ARCH}"
|
||||||
|
|
||||||
|
# Calculate checksum
|
||||||
|
cd "${BUILD_DIR}"
|
||||||
|
sha256sum "${PACKAGE_NAME}_${DEB_VERSION}_${ARCH}.deb" > "${PACKAGE_NAME}_${DEB_VERSION}_${ARCH}.deb.sha256"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Build complete ==="
|
||||||
|
echo "Package: ${BUILD_DIR}/${PACKAGE_NAME}_${DEB_VERSION}_${ARCH}.deb"
|
||||||
|
echo "Checksum: $(cat ${PACKAGE_NAME}_${DEB_VERSION}_${ARCH}.deb.sha256)"
|
||||||
78
packaging/build-rpm.sh
Executable file
78
packaging/build-rpm.sh
Executable file
@ -0,0 +1,78 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Build script for .rpm package
|
||||||
|
# Usage: ./build-rpm.sh [version] [architecture]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Sanitize version for RPM package (must start with digit)
|
||||||
|
VERSION="${1:-1.0.0}"
|
||||||
|
ARCH="${2:-x86_64}"
|
||||||
|
PACKAGE_NAME="ja4sentinel"
|
||||||
|
|
||||||
|
# Convert git version to RPM-compatible format
|
||||||
|
if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
||||||
|
RPM_VERSION="$VERSION"
|
||||||
|
elif [[ "$VERSION" =~ ^v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
|
||||||
|
RPM_VERSION="${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
RPM_VERSION="0.0.0.${VERSION//[^a-zA-Z0-9.]/_}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== Building ${PACKAGE_NAME} ${RPM_VERSION} for ${ARCH} ==="
|
||||||
|
|
||||||
|
# Directories
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
BUILD_DIR="${PROJECT_ROOT}/build/rpm"
|
||||||
|
RPMBUILD_DIR="${BUILD_DIR}/rpmbuild"
|
||||||
|
|
||||||
|
# Clean and create build directory
|
||||||
|
rm -rf "${BUILD_DIR}"
|
||||||
|
mkdir -p "${RPMBUILD_DIR}/BUILD"
|
||||||
|
mkdir -p "${RPMBUILD_DIR}/RPMS"
|
||||||
|
mkdir -p "${RPMBUILD_DIR}/SOURCES"
|
||||||
|
mkdir -p "${RPMBUILD_DIR}/SPECS"
|
||||||
|
mkdir -p "${RPMBUILD_DIR}/SRPMS"
|
||||||
|
|
||||||
|
# Copy binary (build if not exists)
|
||||||
|
if [ ! -f "${PROJECT_ROOT}/dist/ja4sentinel-linux-amd64" ]; then
|
||||||
|
echo "Building binary..."
|
||||||
|
cd "${PROJECT_ROOT}"
|
||||||
|
make build-linux
|
||||||
|
fi
|
||||||
|
cp "${PROJECT_ROOT}/dist/ja4sentinel-linux-amd64" "${RPMBUILD_DIR}/SOURCES/ja4sentinel"
|
||||||
|
chmod 755 "${RPMBUILD_DIR}/SOURCES/ja4sentinel"
|
||||||
|
|
||||||
|
# Copy systemd service
|
||||||
|
cp "${SCRIPT_DIR}/systemd/ja4sentinel.service" "${RPMBUILD_DIR}/SOURCES/ja4sentinel.service"
|
||||||
|
chmod 644 "${RPMBUILD_DIR}/SOURCES/ja4sentinel.service"
|
||||||
|
|
||||||
|
# Copy default config
|
||||||
|
cp "${SCRIPT_DIR}/systemd/config.yml" "${RPMBUILD_DIR}/SOURCES/config.yml"
|
||||||
|
chmod 640 "${RPMBUILD_DIR}/SOURCES/config.yml"
|
||||||
|
|
||||||
|
# Copy spec file and update version
|
||||||
|
sed "s/Version: .*/Version: ${RPM_VERSION}/" "${SCRIPT_DIR}/rpm/ja4sentinel.spec" > "${RPMBUILD_DIR}/SPECS/ja4sentinel.spec"
|
||||||
|
|
||||||
|
# Build the RPM package
|
||||||
|
echo "Building .rpm package..."
|
||||||
|
rpmbuild -bb \
|
||||||
|
--define "_topdir ${RPMBUILD_DIR}" \
|
||||||
|
--define "_arch ${ARCH}" \
|
||||||
|
"${RPMBUILD_DIR}/SPECS/ja4sentinel.spec"
|
||||||
|
|
||||||
|
# Copy RPM to build directory
|
||||||
|
find "${RPMBUILD_DIR}/RPMS" -name "*.rpm" -exec cp {} "${BUILD_DIR}/" \;
|
||||||
|
|
||||||
|
# Calculate checksum
|
||||||
|
cd "${BUILD_DIR}"
|
||||||
|
for rpm_file in *.rpm; do
|
||||||
|
if [ -f "$rpm_file" ]; then
|
||||||
|
sha256sum "$rpm_file" > "${rpm_file}.sha256"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Build complete ==="
|
||||||
|
echo "Package: ${BUILD_DIR}/${PACKAGE_NAME}-${VERSION}-1.${ARCH}.rpm"
|
||||||
|
ls -la "${BUILD_DIR}"/*.rpm 2>/dev/null || true
|
||||||
66
packaging/deb/postinst
Normal file
66
packaging/deb/postinst
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# postinst script for ja4sentinel .deb package
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
configure)
|
||||||
|
# Create ja4sentinel user and group if they don't exist
|
||||||
|
if ! getent group ja4sentinel > /dev/null 2>&1; then
|
||||||
|
groupadd --system ja4sentinel
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! getent passwd ja4sentinel > /dev/null 2>&1; then
|
||||||
|
useradd --system \
|
||||||
|
--gid ja4sentinel \
|
||||||
|
--home-dir /var/lib/ja4sentinel \
|
||||||
|
--no-create-home \
|
||||||
|
--shell /usr/sbin/nologin \
|
||||||
|
ja4sentinel
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create necessary directories
|
||||||
|
mkdir -p /var/lib/ja4sentinel
|
||||||
|
mkdir -p /var/run/ja4sentinel
|
||||||
|
mkdir -p /var/log/ja4sentinel
|
||||||
|
mkdir -p /etc/ja4sentinel
|
||||||
|
|
||||||
|
# Set proper ownership
|
||||||
|
chown -R ja4sentinel:ja4sentinel /var/lib/ja4sentinel
|
||||||
|
chown -R ja4sentinel:ja4sentinel /var/run/ja4sentinel
|
||||||
|
chown -R ja4sentinel:ja4sentinel /var/log/ja4sentinel
|
||||||
|
chown -R ja4sentinel:ja4sentinel /etc/ja4sentinel
|
||||||
|
|
||||||
|
# Set proper permissions
|
||||||
|
chmod 750 /var/lib/ja4sentinel
|
||||||
|
chmod 750 /var/log/ja4sentinel
|
||||||
|
chmod 750 /etc/ja4sentinel
|
||||||
|
|
||||||
|
# Install default config if it doesn't exist
|
||||||
|
if [ ! -f /etc/ja4sentinel/config.yml ]; then
|
||||||
|
cp /usr/share/ja4sentinel/config.yml /etc/ja4sentinel/config.yml
|
||||||
|
chown ja4sentinel:ja4sentinel /etc/ja4sentinel/config.yml
|
||||||
|
chmod 640 /etc/ja4sentinel/config.yml
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enable and start the service (if running in a real system, not container)
|
||||||
|
if [ -x /bin/systemctl ] && [ -d /run/systemd/system ]; then
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable ja4sentinel.service
|
||||||
|
if ! systemctl is-active --quiet ja4sentinel.service; then
|
||||||
|
systemctl start ja4sentinel.service
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
abort-upgrade|abort-remove|abort-deconfigure)
|
||||||
|
# On abort, do nothing special
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "postinst called with unknown argument '$1'" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
52
packaging/deb/postrm
Normal file
52
packaging/deb/postrm
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# postrm script for ja4sentinel .deb package
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
remove)
|
||||||
|
# On remove, leave config and data files
|
||||||
|
;;
|
||||||
|
|
||||||
|
purge)
|
||||||
|
# On purge, remove everything
|
||||||
|
|
||||||
|
# Stop service if running
|
||||||
|
if [ -x /bin/systemctl ] && [ -d /run/systemd/system ]; then
|
||||||
|
systemctl stop ja4sentinel.service 2>/dev/null || true
|
||||||
|
systemctl disable ja4sentinel.service 2>/dev/null || true
|
||||||
|
systemctl daemon-reload
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove configuration
|
||||||
|
rm -rf /etc/ja4sentinel
|
||||||
|
|
||||||
|
# Remove data and logs
|
||||||
|
rm -rf /var/lib/ja4sentinel
|
||||||
|
rm -rf /var/log/ja4sentinel
|
||||||
|
rm -rf /var/run/ja4sentinel
|
||||||
|
|
||||||
|
# Remove user and group
|
||||||
|
if getent passwd ja4sentinel > /dev/null 2>&1; then
|
||||||
|
userdel ja4sentinel 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if getent group ja4sentinel > /dev/null 2>&1; then
|
||||||
|
groupdel ja4sentinel 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
abort-upgrade|abort-remove|abort-deconfigure)
|
||||||
|
# On abort, restart the service
|
||||||
|
if [ -x /bin/systemctl ] && [ -d /run/systemd/system ]; then
|
||||||
|
systemctl start ja4sentinel.service 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "postrm called with unknown argument '$1'" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
29
packaging/deb/prerm
Normal file
29
packaging/deb/prerm
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# prerm script for ja4sentinel .deb package
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
remove|deconfigure)
|
||||||
|
# Stop and disable the service
|
||||||
|
if [ -x /bin/systemctl ] && [ -d /run/systemd/system ]; then
|
||||||
|
systemctl stop ja4sentinel.service 2>/dev/null || true
|
||||||
|
systemctl disable ja4sentinel.service 2>/dev/null || true
|
||||||
|
systemctl daemon-reload
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
upgrade)
|
||||||
|
# On upgrade, just stop the service (will be restarted by postinst)
|
||||||
|
if [ -x /bin/systemctl ] && [ -d /run/systemd/system ]; then
|
||||||
|
systemctl stop ja4sentinel.service 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "prerm called with unknown argument '$1'" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
113
packaging/rpm/ja4sentinel.spec
Normal file
113
packaging/rpm/ja4sentinel.spec
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
Name: ja4sentinel
|
||||||
|
Version: 1.0.0
|
||||||
|
Release: 1%{?dist}
|
||||||
|
Summary: JA4 TLS fingerprinting daemon for network monitoring
|
||||||
|
License: MIT
|
||||||
|
URL: https://github.com/your-repo/ja4sentinel
|
||||||
|
BuildArch: x86_64
|
||||||
|
|
||||||
|
# Runtime dependencies
|
||||||
|
Requires: systemd
|
||||||
|
Requires: libpcap
|
||||||
|
|
||||||
|
%description
|
||||||
|
JA4Sentinel is a Go-based tool for capturing network traffic on Linux servers,
|
||||||
|
extracting client-side TLS handshakes, generating JA4 signatures, enriching
|
||||||
|
with IP/TCP metadata, and logging results to configurable outputs.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Network packet capture with BPF filters
|
||||||
|
- TLS ClientHello extraction
|
||||||
|
- JA4/JA3 fingerprint generation
|
||||||
|
- IP/TCP metadata enrichment
|
||||||
|
- Multiple output formats (stdout, file, UNIX socket)
|
||||||
|
- Structured JSON logging for systemd/journald
|
||||||
|
|
||||||
|
%prep
|
||||||
|
# No source to unpack, binary is pre-built
|
||||||
|
|
||||||
|
%build
|
||||||
|
# No build needed, binary is pre-built
|
||||||
|
|
||||||
|
%install
|
||||||
|
mkdir -p %{buildroot}/usr/bin
|
||||||
|
mkdir -p %{buildroot}/etc/ja4sentinel
|
||||||
|
mkdir -p %{buildroot}/var/lib/ja4sentinel
|
||||||
|
mkdir -p %{buildroot}/var/log/ja4sentinel
|
||||||
|
mkdir -p %{buildroot}/var/run/ja4sentinel
|
||||||
|
mkdir -p %{buildroot}/usr/lib/systemd/system
|
||||||
|
mkdir -p %{buildroot}/usr/share/ja4sentinel
|
||||||
|
|
||||||
|
# Install binary
|
||||||
|
install -m 755 %{_sourcedir}/ja4sentinel %{buildroot}/usr/bin/ja4sentinel
|
||||||
|
|
||||||
|
# Install systemd service
|
||||||
|
install -m 644 %{_sourcedir}/ja4sentinel.service %{buildroot}/usr/lib/systemd/system/ja4sentinel.service
|
||||||
|
|
||||||
|
# Install default config
|
||||||
|
install -m 640 %{_sourcedir}/config.yml %{buildroot}/etc/ja4sentinel/config.yml.default
|
||||||
|
install -m 640 %{_sourcedir}/config.yml %{buildroot}/usr/share/ja4sentinel/config.yml
|
||||||
|
|
||||||
|
%pre
|
||||||
|
getent group ja4sentinel >/dev/null || groupadd -r ja4sentinel
|
||||||
|
getent passwd ja4sentinel >/dev/null || \
|
||||||
|
useradd -r -g ja4sentinel -d /var/lib/ja4sentinel -s /sbin/nologin \
|
||||||
|
-c "JA4Sentinel Service User" ja4sentinel
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
%post
|
||||||
|
# Set proper ownership
|
||||||
|
chown -R ja4sentinel:ja4sentinel /var/lib/ja4sentinel
|
||||||
|
chown -R ja4sentinel:ja4sentinel /var/run/ja4sentinel
|
||||||
|
chown -R ja4sentinel:ja4sentinel /var/log/ja4sentinel
|
||||||
|
chown -R ja4sentinel:ja4sentinel /etc/ja4sentinel
|
||||||
|
|
||||||
|
# Set proper permissions
|
||||||
|
chmod 750 /var/lib/ja4sentinel
|
||||||
|
chmod 750 /var/log/ja4sentinel
|
||||||
|
chmod 750 /etc/ja4sentinel
|
||||||
|
|
||||||
|
# Install config if not exists
|
||||||
|
if [ ! -f /etc/ja4sentinel/config.yml ]; then
|
||||||
|
cp /usr/share/ja4sentinel/config.yml /etc/ja4sentinel/config.yml
|
||||||
|
chown ja4sentinel:ja4sentinel /etc/ja4sentinel/config.yml
|
||||||
|
chmod 640 /etc/ja4sentinel/config.yml
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enable service
|
||||||
|
if [ $1 -eq 1 ] && [ -x /bin/systemctl ]; then
|
||||||
|
/bin/systemctl daemon-reload
|
||||||
|
/bin/systemctl enable ja4sentinel.service
|
||||||
|
/bin/systemctl start ja4sentinel.service
|
||||||
|
fi
|
||||||
|
|
||||||
|
%preun
|
||||||
|
if [ $1 -eq 0 ]; then
|
||||||
|
# Package removal, stop and disable service
|
||||||
|
if [ -x /bin/systemctl ]; then
|
||||||
|
/bin/systemctl stop ja4sentinel.service >/dev/null 2>&1 || true
|
||||||
|
/bin/systemctl disable ja4sentinel.service >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
%postun
|
||||||
|
if [ $1 -eq 0 ]; then
|
||||||
|
# Package removal, reload systemd
|
||||||
|
if [ -x /bin/systemctl ]; then
|
||||||
|
/bin/systemctl daemon-reload
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
%files
|
||||||
|
/usr/bin/ja4sentinel
|
||||||
|
/usr/lib/systemd/system/ja4sentinel.service
|
||||||
|
/usr/share/ja4sentinel/config.yml
|
||||||
|
%config(noreplace) /etc/ja4sentinel/config.yml.default
|
||||||
|
%dir /etc/ja4sentinel
|
||||||
|
%dir /var/lib/ja4sentinel
|
||||||
|
%dir /var/log/ja4sentinel
|
||||||
|
%dir /var/run/ja4sentinel
|
||||||
|
|
||||||
|
%changelog
|
||||||
|
* Wed Feb 25 2026 JA4Sentinel Team <team@example.com> - 1.0.0-1
|
||||||
|
- Initial package release
|
||||||
35
packaging/systemd/config.yml
Normal file
35
packaging/systemd/config.yml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# JA4Sentinel Configuration
|
||||||
|
# Default configuration file for ja4sentinel service
|
||||||
|
|
||||||
|
core:
|
||||||
|
# Network interface to monitor (use 'ip link' to list available interfaces)
|
||||||
|
interface: eth0
|
||||||
|
|
||||||
|
# TCP ports to monitor for TLS handshakes
|
||||||
|
listen_ports:
|
||||||
|
- 443
|
||||||
|
- 8443
|
||||||
|
|
||||||
|
# Optional BPF filter (leave empty for default port-based filter)
|
||||||
|
bpf_filter: ""
|
||||||
|
|
||||||
|
# Timeout in seconds for TLS handshake extraction per flow
|
||||||
|
flow_timeout_sec: 30
|
||||||
|
|
||||||
|
# Output configuration - enable one or more outputs
|
||||||
|
outputs:
|
||||||
|
# Log to stdout (captured by journald)
|
||||||
|
- type: stdout
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Log to file (optional)
|
||||||
|
# - type: file
|
||||||
|
# enabled: false
|
||||||
|
# params:
|
||||||
|
# path: /var/log/ja4sentinel/ja4.json
|
||||||
|
|
||||||
|
# Log to UNIX socket (optional, for external processing)
|
||||||
|
# - type: unix_socket
|
||||||
|
# enabled: false
|
||||||
|
# params:
|
||||||
|
# socket_path: /var/run/ja4sentinel/ja4.sock
|
||||||
39
packaging/systemd/ja4sentinel.service
Normal file
39
packaging/systemd/ja4sentinel.service
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=JA4 client fingerprinting daemon
|
||||||
|
Documentation=https://github.com/your-repo/ja4sentinel
|
||||||
|
After=network.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=ja4sentinel
|
||||||
|
Group=ja4sentinel
|
||||||
|
WorkingDirectory=/var/lib/ja4sentinel
|
||||||
|
ExecStart=/usr/bin/ja4sentinel --config /etc/ja4sentinel/config.yml
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
Environment=JA4SENTINEL_LOG_LEVEL=info
|
||||||
|
|
||||||
|
# Security hardening
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
ProtectSystem=full
|
||||||
|
ProtectHome=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
RestrictRealtime=yes
|
||||||
|
RestrictSUIDSGID=yes
|
||||||
|
MemoryDenyWriteExecute=yes
|
||||||
|
LockPersonality=yes
|
||||||
|
|
||||||
|
# Capabilities for packet capture
|
||||||
|
AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN
|
||||||
|
CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN
|
||||||
|
|
||||||
|
# Resource limits
|
||||||
|
LimitNOFILE=65536
|
||||||
|
LimitNPROC=64
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
31
packaging/test/Dockerfile.deb
Normal file
31
packaging/test/Dockerfile.deb
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Dockerfile for testing DEB package installation
|
||||||
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
libpcap0.8 \
|
||||||
|
systemd \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Create systemd directory (needed for service installation)
|
||||||
|
RUN mkdir -p /etc/systemd/system
|
||||||
|
|
||||||
|
# Copy DEB package
|
||||||
|
COPY *.deb /tmp/ja4sentinel.deb
|
||||||
|
|
||||||
|
# Install the package
|
||||||
|
RUN dpkg -i /tmp/ja4sentinel.deb || apt-get install -f -y
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
RUN echo "=== Verifying installation ===" && \
|
||||||
|
which ja4sentinel && \
|
||||||
|
ja4sentinel --version && \
|
||||||
|
ls -la /etc/ja4sentinel/ && \
|
||||||
|
ls -la /var/lib/ja4sentinel/ && \
|
||||||
|
ls -la /usr/lib/systemd/system/ja4sentinel.service && \
|
||||||
|
echo "=== Installation successful ==="
|
||||||
|
|
||||||
|
# Default command: run tests
|
||||||
|
CMD ["/test-install.sh"]
|
||||||
29
packaging/test/Dockerfile.rpm
Normal file
29
packaging/test/Dockerfile.rpm
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Dockerfile for testing RPM package installation
|
||||||
|
FROM fedora:39
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN dnf install -y \
|
||||||
|
libpcap \
|
||||||
|
systemd \
|
||||||
|
&& dnf clean all
|
||||||
|
|
||||||
|
# Create systemd directory (needed for service installation)
|
||||||
|
RUN mkdir -p /etc/systemd/system
|
||||||
|
|
||||||
|
# Copy RPM package
|
||||||
|
COPY *.rpm /tmp/ja4sentinel.rpm
|
||||||
|
|
||||||
|
# Install the package
|
||||||
|
RUN dnf install -y /tmp/ja4sentinel.rpm
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
RUN echo "=== Verifying installation ===" && \
|
||||||
|
which ja4sentinel && \
|
||||||
|
ja4sentinel --version && \
|
||||||
|
ls -la /etc/ja4sentinel/ && \
|
||||||
|
ls -la /var/lib/ja4sentinel/ && \
|
||||||
|
ls -la /usr/lib/systemd/system/ja4sentinel.service && \
|
||||||
|
echo "=== Installation successful ==="
|
||||||
|
|
||||||
|
# Default command: run tests
|
||||||
|
CMD ["/test-install.sh"]
|
||||||
43
packaging/test/test-deb.sh
Executable file
43
packaging/test/test-deb.sh
Executable file
@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Test DEB package installation in Docker container
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
||||||
|
BUILD_DIR="${PROJECT_ROOT}/build/deb"
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo " Testing DEB Package Installation"
|
||||||
|
echo "=========================================="
|
||||||
|
|
||||||
|
# Find the DEB package
|
||||||
|
DEB_PACKAGE=$(ls -1 "${BUILD_DIR}"/*.deb 2>/dev/null | head -1)
|
||||||
|
if [ -z "$DEB_PACKAGE" ]; then
|
||||||
|
echo "Error: No .deb package found in ${BUILD_DIR}"
|
||||||
|
echo "Run 'make package-deb' first"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found package: ${DEB_PACKAGE}"
|
||||||
|
|
||||||
|
# Copy package to test directory
|
||||||
|
cp "${DEB_PACKAGE}" "${SCRIPT_DIR}/"
|
||||||
|
|
||||||
|
# Build test image
|
||||||
|
echo "Building test Docker image..."
|
||||||
|
docker build -t ja4sentinel-test-deb \
|
||||||
|
-f "${SCRIPT_DIR}/Dockerfile.deb" \
|
||||||
|
"${SCRIPT_DIR}/"
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
echo ""
|
||||||
|
echo "Running installation tests..."
|
||||||
|
docker run --rm \
|
||||||
|
-v "${SCRIPT_DIR}/test-install-deb.sh:/test-install.sh:ro" \
|
||||||
|
ja4sentinel-test-deb \
|
||||||
|
/test-install.sh
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo " DEB Package Test Complete"
|
||||||
|
echo "=========================================="
|
||||||
112
packaging/test/test-install-deb.sh
Executable file
112
packaging/test/test-install-deb.sh
Executable file
@ -0,0 +1,112 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Test script for DEB package installation
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo " JA4Sentinel DEB Package Installation Test"
|
||||||
|
echo "=========================================="
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
pass() { echo -e "${GREEN}[PASS]${NC} $1"; }
|
||||||
|
fail() { echo -e "${RED}[FAIL]${NC} $1"; exit 1; }
|
||||||
|
info() { echo -e "${YELLOW}[INFO]${NC} $1"; }
|
||||||
|
|
||||||
|
# Test 1: Binary exists and is executable
|
||||||
|
info "Test 1: Checking binary..."
|
||||||
|
if [ -x /usr/bin/ja4sentinel ]; then
|
||||||
|
pass "Binary exists and is executable"
|
||||||
|
else
|
||||||
|
fail "Binary not found or not executable"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 2: Version command works
|
||||||
|
info "Test 2: Checking version command..."
|
||||||
|
if ja4sentinel --version 2>&1 | grep -q "ja4sentinel version"; then
|
||||||
|
pass "Version command works"
|
||||||
|
else
|
||||||
|
fail "Version command failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 3: Config directory exists
|
||||||
|
info "Test 3: Checking config directory..."
|
||||||
|
if [ -d /etc/ja4sentinel ]; then
|
||||||
|
pass "Config directory exists"
|
||||||
|
else
|
||||||
|
fail "Config directory not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4: Default config file exists
|
||||||
|
info "Test 4: Checking default config file..."
|
||||||
|
if [ -f /etc/ja4sentinel/config.yml.default ]; then
|
||||||
|
pass "Default config file exists"
|
||||||
|
else
|
||||||
|
fail "Default config file not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 5: Shared config file exists
|
||||||
|
info "Test 5: Checking shared config file..."
|
||||||
|
if [ -f /usr/share/ja4sentinel/config.yml ]; then
|
||||||
|
pass "Shared config file exists"
|
||||||
|
else
|
||||||
|
fail "Shared config file not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 6: Data directories exist
|
||||||
|
info "Test 6: Checking data directories..."
|
||||||
|
for dir in /var/lib/ja4sentinel /var/log/ja4sentinel /var/run/ja4sentinel; do
|
||||||
|
if [ -d "$dir" ]; then
|
||||||
|
pass "Directory $dir exists"
|
||||||
|
else
|
||||||
|
fail "Directory $dir not found"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Test 7: Systemd service file exists
|
||||||
|
info "Test 7: Checking systemd service file..."
|
||||||
|
if [ -f /usr/lib/systemd/system/ja4sentinel.service ]; then
|
||||||
|
pass "Systemd service file exists"
|
||||||
|
else
|
||||||
|
fail "Systemd service file not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 8: Service file has correct content
|
||||||
|
info "Test 8: Checking service file content..."
|
||||||
|
if grep -q "ExecStart=/usr/bin/ja4sentinel" /usr/lib/systemd/system/ja4sentinel.service; then
|
||||||
|
pass "Service file has correct ExecStart"
|
||||||
|
else
|
||||||
|
fail "Service file ExecStart incorrect"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 9: Service file has security settings
|
||||||
|
info "Test 9: Checking service security settings..."
|
||||||
|
if grep -q "NoNewPrivileges=yes" /usr/lib/systemd/system/ja4sentinel.service; then
|
||||||
|
pass "Service has security hardening"
|
||||||
|
else
|
||||||
|
fail "Service missing security settings"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 10: ja4sentinel user exists
|
||||||
|
info "Test 10: Checking ja4sentinel user..."
|
||||||
|
if getent passwd ja4sentinel > /dev/null 2>&1; then
|
||||||
|
pass "ja4sentinel user exists"
|
||||||
|
else
|
||||||
|
info "ja4sentinel user not created (expected in container)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 11: Binary can start (will fail on capture but should init)
|
||||||
|
info "Test 11: Checking binary initialization..."
|
||||||
|
if timeout 2 ja4sentinel --config /etc/ja4sentinel/config.yml.default 2>&1 | grep -q "Starting ja4sentinel\|Configuration loaded"; then
|
||||||
|
pass "Binary initializes correctly"
|
||||||
|
else
|
||||||
|
info "Binary initialization skipped (expected in container without network caps)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo -e "${GREEN} All tests passed!${NC}"
|
||||||
|
echo "=========================================="
|
||||||
112
packaging/test/test-install-rpm.sh
Executable file
112
packaging/test/test-install-rpm.sh
Executable file
@ -0,0 +1,112 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Test script for RPM package installation
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo " JA4Sentinel RPM Package Installation Test"
|
||||||
|
echo "=========================================="
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
pass() { echo -e "${GREEN}[PASS]${NC} $1"; }
|
||||||
|
fail() { echo -e "${RED}[FAIL]${NC} $1"; exit 1; }
|
||||||
|
info() { echo -e "${YELLOW}[INFO]${NC} $1"; }
|
||||||
|
|
||||||
|
# Test 1: Binary exists and is executable
|
||||||
|
info "Test 1: Checking binary..."
|
||||||
|
if [ -x /usr/bin/ja4sentinel ]; then
|
||||||
|
pass "Binary exists and is executable"
|
||||||
|
else
|
||||||
|
fail "Binary not found or not executable"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 2: Version command works
|
||||||
|
info "Test 2: Checking version command..."
|
||||||
|
if ja4sentinel --version 2>&1 | grep -q "ja4sentinel version"; then
|
||||||
|
pass "Version command works"
|
||||||
|
else
|
||||||
|
fail "Version command failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 3: Config directory exists
|
||||||
|
info "Test 3: Checking config directory..."
|
||||||
|
if [ -d /etc/ja4sentinel ]; then
|
||||||
|
pass "Config directory exists"
|
||||||
|
else
|
||||||
|
fail "Config directory not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4: Default config file exists
|
||||||
|
info "Test 4: Checking default config file..."
|
||||||
|
if [ -f /etc/ja4sentinel/config.yml.default ]; then
|
||||||
|
pass "Default config file exists"
|
||||||
|
else
|
||||||
|
fail "Default config file not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 5: Shared config file exists
|
||||||
|
info "Test 5: Checking shared config file..."
|
||||||
|
if [ -f /usr/share/ja4sentinel/config.yml ]; then
|
||||||
|
pass "Shared config file exists"
|
||||||
|
else
|
||||||
|
fail "Shared config file not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 6: Data directories exist
|
||||||
|
info "Test 6: Checking data directories..."
|
||||||
|
for dir in /var/lib/ja4sentinel /var/log/ja4sentinel /var/run/ja4sentinel; do
|
||||||
|
if [ -d "$dir" ]; then
|
||||||
|
pass "Directory $dir exists"
|
||||||
|
else
|
||||||
|
fail "Directory $dir not found"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Test 7: Systemd service file exists
|
||||||
|
info "Test 7: Checking systemd service file..."
|
||||||
|
if [ -f /usr/lib/systemd/system/ja4sentinel.service ]; then
|
||||||
|
pass "Systemd service file exists"
|
||||||
|
else
|
||||||
|
fail "Systemd service file not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 8: Service file has correct content
|
||||||
|
info "Test 8: Checking service file content..."
|
||||||
|
if grep -q "ExecStart=/usr/bin/ja4sentinel" /usr/lib/systemd/system/ja4sentinel.service; then
|
||||||
|
pass "Service file has correct ExecStart"
|
||||||
|
else
|
||||||
|
fail "Service file ExecStart incorrect"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 9: Service file has security settings
|
||||||
|
info "Test 9: Checking service security settings..."
|
||||||
|
if grep -q "NoNewPrivileges=yes" /usr/lib/systemd/system/ja4sentinel.service; then
|
||||||
|
pass "Service has security hardening"
|
||||||
|
else
|
||||||
|
fail "Service missing security settings"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 10: ja4sentinel user exists
|
||||||
|
info "Test 10: Checking ja4sentinel user..."
|
||||||
|
if getent passwd ja4sentinel > /dev/null 2>&1; then
|
||||||
|
pass "ja4sentinel user exists"
|
||||||
|
else
|
||||||
|
info "ja4sentinel user not created (expected in container)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 11: Binary can start (will fail on capture but should init)
|
||||||
|
info "Test 11: Checking binary initialization..."
|
||||||
|
if timeout 2 ja4sentinel --config /etc/ja4sentinel/config.yml.default 2>&1 | grep -q "Starting ja4sentinel\|Configuration loaded"; then
|
||||||
|
pass "Binary initializes correctly"
|
||||||
|
else
|
||||||
|
info "Binary initialization skipped (expected in container without network caps)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo -e "${GREEN} All tests passed!${NC}"
|
||||||
|
echo "=========================================="
|
||||||
43
packaging/test/test-rpm.sh
Executable file
43
packaging/test/test-rpm.sh
Executable file
@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Test RPM package installation in Docker container
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
||||||
|
BUILD_DIR="${PROJECT_ROOT}/build/rpm"
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo " Testing RPM Package Installation"
|
||||||
|
echo "=========================================="
|
||||||
|
|
||||||
|
# Find the RPM package
|
||||||
|
RPM_PACKAGE=$(ls -1 "${BUILD_DIR}"/*.rpm 2>/dev/null | head -1)
|
||||||
|
if [ -z "$RPM_PACKAGE" ]; then
|
||||||
|
echo "Error: No .rpm package found in ${BUILD_DIR}"
|
||||||
|
echo "Run 'make package-rpm' first"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found package: ${RPM_PACKAGE}"
|
||||||
|
|
||||||
|
# Copy package to test directory
|
||||||
|
cp "${RPM_PACKAGE}" "${SCRIPT_DIR}/"
|
||||||
|
|
||||||
|
# Build test image
|
||||||
|
echo "Building test Docker image..."
|
||||||
|
docker build -t ja4sentinel-test-rpm \
|
||||||
|
-f "${SCRIPT_DIR}/Dockerfile.rpm" \
|
||||||
|
"${SCRIPT_DIR}/"
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
echo ""
|
||||||
|
echo "Running installation tests..."
|
||||||
|
docker run --rm \
|
||||||
|
-v "${SCRIPT_DIR}/test-install-rpm.sh:/test-install.sh:ro" \
|
||||||
|
ja4sentinel-test-rpm \
|
||||||
|
/test-install.sh
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo " RPM Package Test Complete"
|
||||||
|
echo "=========================================="
|
||||||
Reference in New Issue
Block a user