mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-06-29 21:38:35 +00:00
Compare commits
7 Commits
2a3708d00c
...
9965f800af
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9965f800af | ||
| b62adc5421 | |||
| 8e2262f502 | |||
|
|
39b0665678 | ||
|
|
5e8a2b02c1 | ||
| 531fd878a6 | |||
| 4a73cc245c |
38
.gitea/workflows/README.md
Normal file
38
.gitea/workflows/README.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Gitea Actions workflows
|
||||||
|
|
||||||
|
This directory mirrors `.github/workflows/` for the self-hosted Gitea
|
||||||
|
Actions runner at `gitea.gk2.secubox.in` (board-internal CI).
|
||||||
|
|
||||||
|
## Source of truth
|
||||||
|
|
||||||
|
`.github/workflows/*.yml` is the source of truth — workflows are edited
|
||||||
|
there and mirrored here. The two trees should stay byte-identical;
|
||||||
|
divergence is a bug.
|
||||||
|
|
||||||
|
Eventual goal: a single CI definition tree consumed by both runners.
|
||||||
|
For now, the mirror is mechanical (cp) and tracked in PRs that touch
|
||||||
|
both directories together.
|
||||||
|
|
||||||
|
## Compatibility notes
|
||||||
|
|
||||||
|
- `runs-on: ubuntu-latest` works because the `act_runner` registered on
|
||||||
|
the dev box advertises the `ubuntu-latest` label (Phase B of the
|
||||||
|
migration, separate ticket).
|
||||||
|
- `uses: actions/*` references are auto-fetched from `github.com` thanks
|
||||||
|
to `DEFAULT_ACTIONS_URL = github` in
|
||||||
|
`/var/lib/gitea/custom/conf/app.ini` (set during Phase A1).
|
||||||
|
- Secrets are NOT carried by repo mirroring — re-add in
|
||||||
|
Gitea Settings → Actions → Secrets per repo:
|
||||||
|
`GPG_PRIVATE_KEY`, `DEPLOY_SSH_KEY`, `DEPLOY_KNOWN_HOSTS`, etc.
|
||||||
|
- Some workflows use `gh` (GitHub CLI) which is not installed by default
|
||||||
|
on `act_runner`. Those jobs will fail visibly until they are migrated
|
||||||
|
to `tea` (Gitea CLI) — to be addressed per-workflow.
|
||||||
|
|
||||||
|
## Refs
|
||||||
|
|
||||||
|
- Phase A1: Gitea Actions enabled in app.ini (done 2026-05-17)
|
||||||
|
- Phase A2: pull mirror github → gitea — blocked on
|
||||||
|
[#176](https://github.com/CyberMind-FR/secubox-deb/issues/176)
|
||||||
|
(missing `giteactl repo mirror` verb)
|
||||||
|
- Phase A3: this directory ([#177](https://github.com/CyberMind-FR/secubox-deb/issues/177))
|
||||||
|
- Phase B: install + register `act_runner` (separate ticket TBD)
|
||||||
324
.gitea/workflows/build-all-live-usb.yml
Normal file
324
.gitea/workflows/build-all-live-usb.yml
Normal file
|
|
@ -0,0 +1,324 @@
|
||||||
|
name: Build SecuBox Live USB (All Platforms)
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
platform:
|
||||||
|
description: 'Target platform(s)'
|
||||||
|
type: string
|
||||||
|
default: 'all'
|
||||||
|
secrets:
|
||||||
|
GPG_PRIVATE_KEY:
|
||||||
|
required: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
platform:
|
||||||
|
description: 'Target platform'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- all
|
||||||
|
- x64
|
||||||
|
- mochabin
|
||||||
|
- rpi400
|
||||||
|
default: all
|
||||||
|
compress:
|
||||||
|
description: 'Compress output (gzip)'
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
DEBIAN_SUITE: bookworm
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Matrix build for all platforms
|
||||||
|
build-live-usb:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
# x64 (amd64) - GRUB UEFI/BIOS boot
|
||||||
|
- platform: x64
|
||||||
|
arch: amd64
|
||||||
|
script: build-live-usb.sh
|
||||||
|
artifact_name: secubox-live-amd64
|
||||||
|
output_pattern: "secubox-live-amd64-*.img*"
|
||||||
|
needs_qemu: false
|
||||||
|
embed_image: false
|
||||||
|
|
||||||
|
# MOCHAbin (arm64) - U-Boot distroboot
|
||||||
|
- platform: mochabin
|
||||||
|
arch: arm64
|
||||||
|
script: build-mochabin-live-usb.sh
|
||||||
|
artifact_name: secubox-mochabin-live-usb
|
||||||
|
output_pattern: "secubox-mochabin-live-usb.img*"
|
||||||
|
needs_qemu: true
|
||||||
|
embed_image: true
|
||||||
|
|
||||||
|
# Raspberry Pi 400 (arm64) - Native Pi firmware boot
|
||||||
|
- platform: rpi400
|
||||||
|
arch: arm64
|
||||||
|
script: build-rpi-usb.sh
|
||||||
|
artifact_name: secubox-rpi-arm64
|
||||||
|
output_pattern: "secubox-rpi-arm64-*.img*"
|
||||||
|
needs_qemu: true
|
||||||
|
embed_image: false
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# Skip job if platform doesn't match (workflow_dispatch only)
|
||||||
|
- name: Check platform filter
|
||||||
|
id: filter
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "push" ]]; then
|
||||||
|
echo "skip=false" >> $GITHUB_OUTPUT
|
||||||
|
elif [[ "${{ inputs.platform }}" == "all" || "${{ inputs.platform }}" == "" || "${{ inputs.platform }}" == "${{ matrix.platform }}" ]]; then
|
||||||
|
echo "skip=false" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "skip=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "Skipping ${{ matrix.platform }} (requested: ${{ inputs.platform }})"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
if: steps.filter.outputs.skip != 'true'
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Free disk space
|
||||||
|
if: steps.filter.outputs.skip != 'true'
|
||||||
|
run: |
|
||||||
|
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/share/boost
|
||||||
|
sudo apt-get clean
|
||||||
|
df -h
|
||||||
|
|
||||||
|
- name: Setup QEMU for ARM64 cross-compilation
|
||||||
|
if: steps.filter.outputs.skip != 'true' && matrix.needs_qemu
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
with:
|
||||||
|
platforms: arm64
|
||||||
|
|
||||||
|
- name: Install build dependencies
|
||||||
|
if: steps.filter.outputs.skip != 'true'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y -qq \
|
||||||
|
debootstrap qemu-user-static binfmt-support \
|
||||||
|
parted dosfstools e2fsprogs squashfs-tools \
|
||||||
|
grub-efi-amd64-bin grub-pc-bin grub-efi-amd64-signed shim-signed \
|
||||||
|
mtools rsync pv xorriso curl wget u-boot-tools
|
||||||
|
|
||||||
|
- name: Download SecuBox packages
|
||||||
|
if: steps.filter.outputs.skip != 'true'
|
||||||
|
uses: dawidd6/action-download-artifact@v3
|
||||||
|
with:
|
||||||
|
workflow: build-packages.yml
|
||||||
|
name: secubox-debs-all
|
||||||
|
path: output/debs/
|
||||||
|
if_no_artifact_found: warn
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: List available packages
|
||||||
|
if: steps.filter.outputs.skip != 'true'
|
||||||
|
run: |
|
||||||
|
echo "=== Available SecuBox packages ==="
|
||||||
|
ls output/debs/secubox-*.deb 2>/dev/null | wc -l || echo "0"
|
||||||
|
ls output/debs/secubox-*.deb 2>/dev/null | xargs -I{} basename {} | sed 's/_.*$//' | sort -u || echo "No packages found"
|
||||||
|
|
||||||
|
- name: Build embedded eMMC image (MOCHAbin only)
|
||||||
|
if: steps.filter.outputs.skip != 'true' && matrix.embed_image
|
||||||
|
run: |
|
||||||
|
echo "=== Building embedded eMMC target image ==="
|
||||||
|
sudo -E bash image/build-image.sh \
|
||||||
|
--board mochabin \
|
||||||
|
--suite ${{ env.DEBIAN_SUITE }} \
|
||||||
|
--out output/ \
|
||||||
|
--slipstream
|
||||||
|
echo "=== Checking eMMC image for embedding ==="
|
||||||
|
# build-image.sh already compresses to .img.gz, just verify it exists
|
||||||
|
if [[ -f output/secubox-mochabin-bookworm.img.gz ]]; then
|
||||||
|
ls -lh output/secubox-mochabin-bookworm.img.gz
|
||||||
|
elif [[ -f output/secubox-mochabin-bookworm.img ]]; then
|
||||||
|
echo "Compressing uncompressed image..."
|
||||||
|
gzip -k output/secubox-mochabin-bookworm.img
|
||||||
|
ls -lh output/secubox-mochabin-bookworm.img.gz
|
||||||
|
else
|
||||||
|
echo "ERROR: No eMMC image found!"
|
||||||
|
ls -la output/
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
timeout-minutes: 60
|
||||||
|
|
||||||
|
- name: Build ${{ matrix.platform }} Live USB
|
||||||
|
if: steps.filter.outputs.skip != 'true'
|
||||||
|
env:
|
||||||
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
run: |
|
||||||
|
echo "=== Building ${{ matrix.platform }} Live USB ==="
|
||||||
|
|
||||||
|
COMPRESS_OPT=""
|
||||||
|
if [[ "${{ inputs.compress }}" == "false" ]]; then
|
||||||
|
COMPRESS_OPT="--no-compress"
|
||||||
|
fi
|
||||||
|
|
||||||
|
SLIPSTREAM_OPT=""
|
||||||
|
if ls output/debs/secubox-*.deb >/dev/null 2>&1; then
|
||||||
|
SLIPSTREAM_OPT="--slipstream"
|
||||||
|
echo "Slipstream enabled: $(ls output/debs/secubox-*.deb | wc -l) packages"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo -E bash image/${{ matrix.script }} \
|
||||||
|
--suite ${{ env.DEBIAN_SUITE }} \
|
||||||
|
$SLIPSTREAM_OPT \
|
||||||
|
$COMPRESS_OPT
|
||||||
|
|
||||||
|
sudo chown -R $(id -u):$(id -g) output/
|
||||||
|
timeout-minutes: 120
|
||||||
|
|
||||||
|
- name: List output files
|
||||||
|
if: steps.filter.outputs.skip != 'true'
|
||||||
|
run: |
|
||||||
|
echo "=== Build output ==="
|
||||||
|
ls -lh output/
|
||||||
|
|
||||||
|
- name: Generate checksums
|
||||||
|
if: steps.filter.outputs.skip != 'true'
|
||||||
|
run: |
|
||||||
|
cd output/
|
||||||
|
echo "=== Generating SHA256 checksums ==="
|
||||||
|
for f in ${{ matrix.output_pattern }}; do
|
||||||
|
if [[ -f "$f" ]]; then
|
||||||
|
sha256sum "$f" >> SHA256SUMS
|
||||||
|
echo " $f"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
cat SHA256SUMS
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
if: steps.filter.outputs.skip != 'true'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.artifact_name }}-${{ env.DEBIAN_SUITE }}
|
||||||
|
path: |
|
||||||
|
output/${{ matrix.output_pattern }}
|
||||||
|
output/SHA256SUMS
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
# Collect all artifacts and create release on tags
|
||||||
|
release:
|
||||||
|
needs: build-live-usb
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download all artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: live-usb/
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: List downloaded files
|
||||||
|
run: |
|
||||||
|
echo "=== Downloaded artifacts ==="
|
||||||
|
ls -lh live-usb/
|
||||||
|
|
||||||
|
- name: Generate combined checksums
|
||||||
|
run: |
|
||||||
|
cd live-usb/
|
||||||
|
rm -f SHA256SUMS
|
||||||
|
echo "=== Generating combined SHA256SUMS ==="
|
||||||
|
sha256sum *.img* 2>/dev/null | sort > SHA256SUMS || echo "No images found"
|
||||||
|
cat SHA256SUMS
|
||||||
|
|
||||||
|
- name: Sign checksums
|
||||||
|
env:
|
||||||
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
if: env.GPG_PRIVATE_KEY != ''
|
||||||
|
run: |
|
||||||
|
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||||
|
cd live-usb/
|
||||||
|
gpg --clearsign SHA256SUMS
|
||||||
|
mv SHA256SUMS.asc SHA256SUMS.gpg
|
||||||
|
echo "=== Checksums signed ==="
|
||||||
|
|
||||||
|
- name: Delete existing release assets (if any)
|
||||||
|
run: |
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
if gh release view "$VERSION" &>/dev/null; then
|
||||||
|
echo "Release $VERSION exists, deleting duplicate assets..."
|
||||||
|
for file in live-usb/*.img* live-usb/SHA256SUMS live-usb/SHA256SUMS.gpg; do
|
||||||
|
[ -f "$file" ] || continue
|
||||||
|
name=$(basename "$file")
|
||||||
|
gh release delete-asset "$VERSION" "$name" --yes 2>/dev/null || true
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
live-usb/*.img*
|
||||||
|
live-usb/SHA256SUMS
|
||||||
|
live-usb/SHA256SUMS.gpg
|
||||||
|
body: |
|
||||||
|
## SecuBox Live USB Images ${{ github.ref_name }}
|
||||||
|
|
||||||
|
Bootable live USB images with integrated disk flasher tools for permanent installation.
|
||||||
|
|
||||||
|
### Available Images
|
||||||
|
|
||||||
|
| Image | Platform | Architecture | Boot Method | Flasher Tool |
|
||||||
|
|-------|----------|--------------|-------------|--------------|
|
||||||
|
| `secubox-live-amd64-bookworm.img.gz` | x64 PC | amd64 | GRUB UEFI/BIOS | `secubox-flash-disk` (press `d`) |
|
||||||
|
| `secubox-mochabin-live-usb.img` | MOCHAbin | arm64 | U-Boot extlinux | `secubox-flash-emmc` (press `f`) |
|
||||||
|
| `secubox-rpi-arm64-bookworm.img.gz` | Raspberry Pi 400 | arm64 | Pi firmware | Manual install |
|
||||||
|
|
||||||
|
### Flash to USB Drive
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# x64 PC
|
||||||
|
zcat secubox-live-amd64-bookworm.img.gz | sudo dd of=/dev/sdX bs=4M status=progress
|
||||||
|
|
||||||
|
# MOCHAbin
|
||||||
|
sudo dd if=secubox-mochabin-live-usb.img of=/dev/sdX bs=4M status=progress
|
||||||
|
|
||||||
|
# Raspberry Pi 400
|
||||||
|
zcat secubox-rpi-arm64-bookworm.img.gz | sudo dd of=/dev/sdX bs=4M status=progress
|
||||||
|
```
|
||||||
|
|
||||||
|
### Boot & Install to Internal Storage
|
||||||
|
|
||||||
|
| Platform | Boot Method | Install Command |
|
||||||
|
|----------|-------------|-----------------|
|
||||||
|
| **x64** | Boot USB, select "SecuBox Live" | Press `d` in TUI or run `secubox-flash-disk` |
|
||||||
|
| **MOCHAbin** | Serial console (115200 8N1), U-Boot auto-boots | Press `f` in TUI or run `secubox-flash-emmc` |
|
||||||
|
| **Raspberry Pi** | HDMI/serial console, auto-boots | Manual: `dd` to SD card |
|
||||||
|
|
||||||
|
### Default Credentials
|
||||||
|
|
||||||
|
- **Console/SSH:** `root` / `secubox`
|
||||||
|
- **Web UI:** `https://localhost` (or LAN IP)
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify checksums
|
||||||
|
sha256sum -c SHA256SUMS
|
||||||
|
|
||||||
|
# Verify GPG signature (if available)
|
||||||
|
gpg --verify SHA256SUMS.gpg
|
||||||
|
```
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- **All SecuBox packages** pre-installed (40+ security modules)
|
||||||
|
- **Persistent storage** partition for configuration
|
||||||
|
- **Kiosk mode** with Chromium dashboard
|
||||||
|
- **Console TUI** for headless operation
|
||||||
|
- **Network auto-detection** (DHCP on all interfaces)
|
||||||
|
|
||||||
|
draft: false
|
||||||
|
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }}
|
||||||
190
.gitea/workflows/build-eye-remote.yml
Normal file
190
.gitea/workflows/build-eye-remote.yml
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
name: Build Eye Remote Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
wifi_ssid:
|
||||||
|
description: 'WiFi SSID (optional)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
wifi_psk:
|
||||||
|
description: 'WiFi password (optional)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
hostname:
|
||||||
|
description: 'Device hostname'
|
||||||
|
required: false
|
||||||
|
default: 'secubox-round'
|
||||||
|
create_release:
|
||||||
|
description: 'Create GitHub release'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- 'true'
|
||||||
|
- 'false'
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'eye-remote-v*'
|
||||||
|
paths:
|
||||||
|
- 'remote-ui/round/**'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'remote-ui/round/**'
|
||||||
|
|
||||||
|
env:
|
||||||
|
VERSION: '2.2.1'
|
||||||
|
RPI_OS_URL: 'https://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2024-11-19/2024-11-19-raspios-bookworm-armhf-lite.img.xz'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Run menu system tests first
|
||||||
|
test-menu-system:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Install test dependencies
|
||||||
|
run: |
|
||||||
|
pip install pytest pytest-asyncio pillow
|
||||||
|
|
||||||
|
- name: Run menu system tests
|
||||||
|
run: |
|
||||||
|
cd remote-ui/round
|
||||||
|
python -m pytest tests/ -v --tb=short
|
||||||
|
continue-on-error: true # Don't block build on test failures for now
|
||||||
|
|
||||||
|
build-eye-remote:
|
||||||
|
needs: test-menu-system
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Free disk space
|
||||||
|
run: |
|
||||||
|
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc
|
||||||
|
df -h
|
||||||
|
|
||||||
|
- name: Install build tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y -qq \
|
||||||
|
parted dosfstools e2fsprogs \
|
||||||
|
xz-utils wget rsync \
|
||||||
|
qemu-user-static binfmt-support
|
||||||
|
|
||||||
|
# Ensure ARM binfmt is registered
|
||||||
|
sudo systemctl restart binfmt-support || true
|
||||||
|
sudo update-binfmts --enable qemu-arm || true
|
||||||
|
|
||||||
|
# Verify QEMU is working
|
||||||
|
if [ -f /proc/sys/fs/binfmt_misc/qemu-arm ]; then
|
||||||
|
echo "✓ QEMU ARM binfmt registered"
|
||||||
|
else
|
||||||
|
echo "⚠ QEMU ARM binfmt not found, attempting manual registration"
|
||||||
|
sudo update-binfmts --import qemu-arm || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Download Raspberry Pi OS Lite
|
||||||
|
run: |
|
||||||
|
echo "Downloading Raspberry Pi OS Lite (armhf)..."
|
||||||
|
wget -q -O /tmp/raspios-lite.img.xz "${{ env.RPI_OS_URL }}"
|
||||||
|
ls -lh /tmp/raspios-lite.img.xz
|
||||||
|
|
||||||
|
- name: Build Eye Remote image (OFFLINE MODE)
|
||||||
|
run: |
|
||||||
|
cd remote-ui/round
|
||||||
|
|
||||||
|
# Build options
|
||||||
|
BUILD_ARGS="-i /tmp/raspios-lite.img.xz -o /tmp"
|
||||||
|
|
||||||
|
if [ -n "${{ github.event.inputs.wifi_ssid }}" ]; then
|
||||||
|
BUILD_ARGS="$BUILD_ARGS -s '${{ github.event.inputs.wifi_ssid }}' -p '${{ github.event.inputs.wifi_psk }}'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
HOSTNAME="${{ github.event.inputs.hostname || 'secubox-round' }}"
|
||||||
|
BUILD_ARGS="$BUILD_ARGS -h $HOSTNAME"
|
||||||
|
|
||||||
|
echo "Building with: $BUILD_ARGS"
|
||||||
|
sudo bash ./build-eye-remote-image.sh $BUILD_ARGS
|
||||||
|
|
||||||
|
sudo chown $(id -u):$(id -g) /tmp/secubox-eye-remote-*.img
|
||||||
|
timeout-minutes: 60
|
||||||
|
|
||||||
|
- name: Compress image
|
||||||
|
run: |
|
||||||
|
cd /tmp
|
||||||
|
echo "Compressing Eye Remote image..."
|
||||||
|
xz -9 -v secubox-eye-remote-${{ env.VERSION }}.img
|
||||||
|
ls -lh secubox-eye-remote-${{ env.VERSION }}.img.xz
|
||||||
|
|
||||||
|
- name: Generate checksums
|
||||||
|
run: |
|
||||||
|
cd /tmp
|
||||||
|
sha256sum secubox-eye-remote-${{ env.VERSION }}.img.xz > secubox-eye-remote-${{ env.VERSION }}.sha256
|
||||||
|
cat secubox-eye-remote-${{ env.VERSION }}.sha256
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-eye-remote-${{ env.VERSION }}
|
||||||
|
path: |
|
||||||
|
/tmp/secubox-eye-remote-${{ env.VERSION }}.img.xz
|
||||||
|
/tmp/secubox-eye-remote-${{ env.VERSION }}.sha256
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Upload to release (on tag or manual)
|
||||||
|
if: startsWith(github.ref, 'refs/tags/eye-remote-v') || github.event.inputs.create_release == 'true'
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
tag_name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || format('eye-remote-v{0}', env.VERSION) }}
|
||||||
|
name: Eye Remote v${{ env.VERSION }}
|
||||||
|
files: |
|
||||||
|
/tmp/secubox-eye-remote-${{ env.VERSION }}.img.xz
|
||||||
|
/tmp/secubox-eye-remote-${{ env.VERSION }}.sha256
|
||||||
|
body: |
|
||||||
|
|
||||||
|
## Eye Remote v${{ env.VERSION }} — Radial Menu Edition
|
||||||
|
|
||||||
|
### 🎯 What's New
|
||||||
|
- **Radial Menu System** — 6-slice pie menu for touchscreen control
|
||||||
|
- **Touch Handler** — Full gesture support (tap, long-press, swipe)
|
||||||
|
- **Action Executor** — Modular command dispatcher
|
||||||
|
- **Local Settings API** — Display brightness, network config, system info
|
||||||
|
|
||||||
|
### 📦 Image Details
|
||||||
|
|
||||||
|
| Image | Hardware | Description |
|
||||||
|
|-------|----------|-------------|
|
||||||
|
| `secubox-eye-remote-${{ env.VERSION }}.img.xz` | RPi Zero W + HyperPixel 2.1 Round | USB Gadget Controller |
|
||||||
|
|
||||||
|
**🔌 OFFLINE MODE:** All packages pre-installed. No internet required at boot!
|
||||||
|
|
||||||
|
### 🎮 Radial Menu Gestures
|
||||||
|
| Gesture | Action |
|
||||||
|
|---------|--------|
|
||||||
|
| Long-press center | Enter menu mode |
|
||||||
|
| Tap slice | Select item |
|
||||||
|
| Tap center | Go back |
|
||||||
|
| 3-finger tap | Emergency exit |
|
||||||
|
|
||||||
|
### 💾 Flash to SD card
|
||||||
|
```bash
|
||||||
|
xzcat secubox-eye-remote-${{ env.VERSION }}.img.xz | sudo dd of=/dev/sdX bs=4M status=progress
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🔐 Default credentials
|
||||||
|
- User: `pi`
|
||||||
|
- Password: `raspberry`
|
||||||
|
|
||||||
|
### ⏱️ First boot
|
||||||
|
~60s (no package download needed)
|
||||||
|
|
||||||
|
See [Eye-Remote Wiki](https://github.com/CyberMind-FR/secubox-deb/wiki/Eye-Remote) for full documentation.
|
||||||
274
.gitea/workflows/build-image.yml
Normal file
274
.gitea/workflows/build-image.yml
Normal file
|
|
@ -0,0 +1,274 @@
|
||||||
|
name: Build SecuBox System Images
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
board:
|
||||||
|
description: 'Target board'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: 'all'
|
||||||
|
size:
|
||||||
|
description: 'Image size (empty = use board default)'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
secrets:
|
||||||
|
GPG_PRIVATE_KEY:
|
||||||
|
required: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
board:
|
||||||
|
description: 'Target board'
|
||||||
|
required: true
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- mochabin
|
||||||
|
- espressobin-v7
|
||||||
|
- espressobin-ultra
|
||||||
|
- vm-x64
|
||||||
|
- vm-arm64
|
||||||
|
- rpi400
|
||||||
|
- all
|
||||||
|
default: vm-x64
|
||||||
|
size:
|
||||||
|
description: 'Image size (empty = use board default)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
DEBIAN_SUITE: bookworm
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Build system images
|
||||||
|
build-image:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# Handle all event types: push (tags), workflow_call, workflow_dispatch
|
||||||
|
board: ${{ (github.event_name == 'push' || (inputs.board == 'all' || inputs.board == '')) && fromJson('["mochabin","espressobin-v7","espressobin-ultra","vm-x64","rpi400"]') || (github.event.inputs.board == 'all' && fromJson('["mochabin","espressobin-v7","espressobin-ultra","vm-x64","rpi400"]') || fromJson(format('["{0}"]', inputs.board || github.event.inputs.board || 'vm-x64'))) }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Free disk space
|
||||||
|
run: |
|
||||||
|
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc
|
||||||
|
df -h
|
||||||
|
|
||||||
|
- name: Setup QEMU + binfmt
|
||||||
|
if: contains(matrix.board, 'arm') || contains(matrix.board, 'espresso') || contains(matrix.board, 'mocha')
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
with:
|
||||||
|
platforms: arm64
|
||||||
|
|
||||||
|
- name: Install build tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y -qq \
|
||||||
|
debootstrap qemu-user-static binfmt-support \
|
||||||
|
parted dosfstools e2fsprogs rsync \
|
||||||
|
gzip pigz xz-utils
|
||||||
|
|
||||||
|
- name: Download package artifacts
|
||||||
|
uses: dawidd6/action-download-artifact@v3
|
||||||
|
with:
|
||||||
|
workflow: build-packages.yml
|
||||||
|
name: secubox-debs-all
|
||||||
|
path: output/debs/
|
||||||
|
if_no_artifact_found: warn
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: List available packages
|
||||||
|
run: |
|
||||||
|
echo "=== SecuBox packages to slipstream ==="
|
||||||
|
ls output/debs/secubox-*.deb 2>/dev/null | wc -l || echo "0"
|
||||||
|
ls output/debs/secubox-*.deb 2>/dev/null | xargs -I{} basename {} | sed 's/_.*$//' | sort -u || echo "No packages found"
|
||||||
|
|
||||||
|
- name: Determine architecture
|
||||||
|
id: arch
|
||||||
|
run: |
|
||||||
|
case "${{ matrix.board }}" in
|
||||||
|
vm-x64)
|
||||||
|
echo "arch=amd64" >> $GITHUB_OUTPUT
|
||||||
|
echo "is_arm=false" >> $GITHUB_OUTPUT
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "arch=arm64" >> $GITHUB_OUTPUT
|
||||||
|
echo "is_arm=true" >> $GITHUB_OUTPUT
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
- name: Build image for ${{ matrix.board }}
|
||||||
|
env:
|
||||||
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
run: |
|
||||||
|
# Use slipstream if packages are available
|
||||||
|
SLIPSTREAM_OPT=""
|
||||||
|
if ls output/debs/secubox-*.deb >/dev/null 2>&1; then
|
||||||
|
SLIPSTREAM_OPT="--slipstream"
|
||||||
|
echo "Slipstream enabled: $(ls output/debs/secubox-*.deb | wc -l) packages"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build with board-specific size (from board/*/config.mk)
|
||||||
|
SIZE_OPT=""
|
||||||
|
if [ -n "${{ inputs.size || github.event.inputs.size }}" ]; then
|
||||||
|
SIZE_OPT="--size ${{ inputs.size || github.event.inputs.size }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo -E bash image/build-image.sh \
|
||||||
|
--board ${{ matrix.board }} \
|
||||||
|
--suite ${{ env.DEBIAN_SUITE }} \
|
||||||
|
--out output/ \
|
||||||
|
$SIZE_OPT \
|
||||||
|
$SLIPSTREAM_OPT
|
||||||
|
sudo chown -R $(id -u):$(id -g) output/
|
||||||
|
timeout-minutes: 90
|
||||||
|
|
||||||
|
- name: List output
|
||||||
|
run: |
|
||||||
|
ls -lh output/
|
||||||
|
|
||||||
|
- name: Generate checksums
|
||||||
|
run: |
|
||||||
|
cd output/
|
||||||
|
sha256sum *.img.gz > SHA256SUMS 2>/dev/null || echo "No images found" > SHA256SUMS
|
||||||
|
cat SHA256SUMS
|
||||||
|
|
||||||
|
- name: Upload image artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-${{ matrix.board }}-${{ env.DEBIAN_SUITE }}
|
||||||
|
path: |
|
||||||
|
output/*.img.gz
|
||||||
|
output/SHA256SUMS
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
# Collect all images and create release
|
||||||
|
release:
|
||||||
|
needs: build-image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download all artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: images/
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Generate combined checksums
|
||||||
|
run: |
|
||||||
|
cd images/
|
||||||
|
# Remove individual SHA256SUMS files
|
||||||
|
rm -f SHA256SUMS
|
||||||
|
# Generate combined checksums
|
||||||
|
sha256sum *.img.gz 2>/dev/null | sort > SHA256SUMS || echo "No images" > SHA256SUMS
|
||||||
|
cat SHA256SUMS
|
||||||
|
|
||||||
|
- name: Sign checksums
|
||||||
|
env:
|
||||||
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
run: |
|
||||||
|
if [ -n "$GPG_PRIVATE_KEY" ]; then
|
||||||
|
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||||
|
cd images/
|
||||||
|
gpg --clearsign SHA256SUMS
|
||||||
|
mv SHA256SUMS.asc SHA256SUMS.gpg
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Checkout for scripts
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
sparse-checkout: image
|
||||||
|
|
||||||
|
- name: Delete existing release assets (if any)
|
||||||
|
run: |
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
if gh release view "$VERSION" &>/dev/null; then
|
||||||
|
echo "Release $VERSION exists, deleting duplicate assets..."
|
||||||
|
for file in images/*.img.gz images/SHA256SUMS images/SHA256SUMS.gpg image/create-qemu-arm64-vm.sh image/create-vbox-vm.sh; do
|
||||||
|
[ -f "$file" ] || continue
|
||||||
|
name=$(basename "$file")
|
||||||
|
gh release delete-asset "$VERSION" "$name" --yes 2>/dev/null || true
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
images/*.img.gz
|
||||||
|
images/SHA256SUMS
|
||||||
|
images/SHA256SUMS.gpg
|
||||||
|
image/create-qemu-arm64-vm.sh
|
||||||
|
image/create-vbox-vm.sh
|
||||||
|
body: |
|
||||||
|
## SecuBox-DEB ${{ github.ref_name }} System Images
|
||||||
|
|
||||||
|
Flashable system images for SecuBox appliances.
|
||||||
|
|
||||||
|
### Available Images
|
||||||
|
|
||||||
|
| Image | Board | Architecture | Description |
|
||||||
|
|-------|-------|--------------|-------------|
|
||||||
|
| `secubox-mochabin-bookworm.img.gz` | MOCHAbin | arm64 | Marvell Armada 7040 (Pro) |
|
||||||
|
| `secubox-espressobin-v7-bookworm.img.gz` | ESPRESSObin v7 | arm64 | Marvell Armada 3720 (Lite) |
|
||||||
|
| `secubox-espressobin-ultra-bookworm.img.gz` | ESPRESSObin Ultra | arm64 | Marvell Armada 3720 (Lite+) |
|
||||||
|
| `secubox-rpi400-bookworm.img.gz` | Raspberry Pi 400 | arm64 | Pi 400 / Pi 4 |
|
||||||
|
| `secubox-vm-x64-bookworm.img.gz` | VirtualBox/QEMU | amd64 | VM for testing |
|
||||||
|
| `create-qemu-arm64-vm.sh` | QEMU ARM64 | script | Run ARM64 on x86 hosts |
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
**ARM64 boards (MOCHAbin, ESPRESSObin):**
|
||||||
|
```bash
|
||||||
|
# Flash to SD card or eMMC
|
||||||
|
gunzip -c secubox-mochabin-bookworm.img.gz | sudo dd of=/dev/sdX bs=4M status=progress
|
||||||
|
sync
|
||||||
|
```
|
||||||
|
|
||||||
|
**VirtualBox (x64):**
|
||||||
|
```bash
|
||||||
|
# Convert to VDI
|
||||||
|
gunzip secubox-vm-x64-bookworm.img.gz
|
||||||
|
VBoxManage convertfromraw secubox-vm-x64-bookworm.img secubox.vdi --format VDI
|
||||||
|
|
||||||
|
# Create VM
|
||||||
|
VBoxManage createvm --name SecuBox --ostype Debian_64 --register
|
||||||
|
VBoxManage modifyvm SecuBox --memory 2048 --cpus 2 --nic1 nat
|
||||||
|
VBoxManage storagectl SecuBox --name SATA --add sata
|
||||||
|
VBoxManage storageattach SecuBox --storagectl SATA --port 0 --device 0 --type hdd --medium secubox.vdi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sha256sum -c SHA256SUMS
|
||||||
|
gpg --verify SHA256SUMS.gpg
|
||||||
|
```
|
||||||
|
|
||||||
|
### Default Credentials
|
||||||
|
|
||||||
|
- **SSH:** root / (SSH key only, set during firstboot)
|
||||||
|
- **Web UI:** https://secubox.local:8443 (admin / generated at firstboot)
|
||||||
|
|
||||||
|
### Included Packages
|
||||||
|
|
||||||
|
**124 SecuBox packages** across all security domains:
|
||||||
|
- **Core:** hub, core, portal, system, console
|
||||||
|
- **Security:** crowdsec, waf, auth, nac, threats, ipblock, mac-guard
|
||||||
|
- **Networking:** wireguard, haproxy, dpi, qos, netmodes, vhost, cdn
|
||||||
|
- **Applications:** mail, gitea, nextcloud, ollama, jellyfin, matrix
|
||||||
|
- **SOC:** soc-agent, soc-gateway, soc-web
|
||||||
|
- **Intel:** device-intel, vortex-dns, vortex-firewall, ai-insights
|
||||||
|
draft: false
|
||||||
|
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }}
|
||||||
351
.gitea/workflows/build-installer-iso.yml
Normal file
351
.gitea/workflows/build-installer-iso.yml
Normal file
|
|
@ -0,0 +1,351 @@
|
||||||
|
# SecuBox — Installer ISO Build Workflow
|
||||||
|
# Builds hybrid live/installer ISO with wiki documentation
|
||||||
|
name: Build Installer ISO
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
preseed:
|
||||||
|
description: 'Include preseed config'
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
name:
|
||||||
|
description: 'ISO name prefix'
|
||||||
|
required: false
|
||||||
|
default: 'secubox-installer'
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["Build SecuBox .deb packages"]
|
||||||
|
types: [completed]
|
||||||
|
branches: [main, master]
|
||||||
|
|
||||||
|
env:
|
||||||
|
DEBIAN_SUITE: bookworm
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-iso:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event.workflow_run.conclusion == 'success' }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Free disk space
|
||||||
|
run: |
|
||||||
|
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/share/boost
|
||||||
|
sudo apt-get clean
|
||||||
|
df -h
|
||||||
|
|
||||||
|
- name: Download package artifacts
|
||||||
|
if: github.event_name == 'workflow_run'
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-debs-all
|
||||||
|
path: output/debs/
|
||||||
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Download packages from latest build
|
||||||
|
if: github.event_name != 'workflow_run'
|
||||||
|
uses: dawidd6/action-download-artifact@v3
|
||||||
|
with:
|
||||||
|
workflow: build-packages.yml
|
||||||
|
name: secubox-debs-all
|
||||||
|
path: output/debs/
|
||||||
|
if_no_artifact_found: warn
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: List available packages
|
||||||
|
run: |
|
||||||
|
echo "=== Available .deb packages ==="
|
||||||
|
ls -lh output/debs/*.deb 2>/dev/null || echo "No packages found"
|
||||||
|
echo "=== Package count ==="
|
||||||
|
ls output/debs/*.deb 2>/dev/null | wc -l || echo "0"
|
||||||
|
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y -qq \
|
||||||
|
debootstrap parted dosfstools e2fsprogs \
|
||||||
|
squashfs-tools grub-efi-amd64-bin grub-pc-bin \
|
||||||
|
xorriso mtools rsync curl wget \
|
||||||
|
keyboard-configuration console-setup kbd locales
|
||||||
|
|
||||||
|
- name: Build Installer ISO
|
||||||
|
run: |
|
||||||
|
SLIPSTREAM_OPT=""
|
||||||
|
if ls output/debs/*.deb >/dev/null 2>&1; then
|
||||||
|
SLIPSTREAM_OPT="--slipstream"
|
||||||
|
echo "Slipstream enabled: $(ls output/debs/*.deb | wc -l) packages"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ISO_NAME="${{ github.event.inputs.name || 'secubox-installer' }}"
|
||||||
|
|
||||||
|
sudo bash image/build-installer-iso.sh \
|
||||||
|
--name "$ISO_NAME" \
|
||||||
|
$SLIPSTREAM_OPT
|
||||||
|
sudo chown -R $(id -u):$(id -g) output/
|
||||||
|
timeout-minutes: 60
|
||||||
|
|
||||||
|
- name: Generate checksums
|
||||||
|
run: |
|
||||||
|
cd output/
|
||||||
|
sha256sum *.iso *.img 2>/dev/null > SHA256SUMS || true
|
||||||
|
cat SHA256SUMS
|
||||||
|
|
||||||
|
- name: List output
|
||||||
|
run: |
|
||||||
|
ls -lh output/
|
||||||
|
echo "=== ISO Info ==="
|
||||||
|
file output/*.iso 2>/dev/null || true
|
||||||
|
|
||||||
|
- name: Upload ISO artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-installer-iso-amd64
|
||||||
|
path: |
|
||||||
|
output/*.iso
|
||||||
|
output/*.img
|
||||||
|
output/SHA256SUMS
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
# Build wiki documentation
|
||||||
|
build-wiki:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build-iso
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Checkout wiki
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: ${{ github.repository }}.wiki
|
||||||
|
path: wiki-repo
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Generate API documentation
|
||||||
|
run: |
|
||||||
|
mkdir -p wiki-output
|
||||||
|
|
||||||
|
# Copy existing wiki docs
|
||||||
|
if [ -d "wiki" ]; then
|
||||||
|
cp -r wiki/* wiki-output/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy docs
|
||||||
|
if [ -d "docs" ]; then
|
||||||
|
cp -r docs/*.md wiki-output/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate module list
|
||||||
|
cat > wiki-output/Modules.md << 'EOF'
|
||||||
|
# SecuBox Modules
|
||||||
|
|
||||||
|
## Core
|
||||||
|
| Module | Description | API Endpoints |
|
||||||
|
|--------|-------------|---------------|
|
||||||
|
| secubox-hub | Central dashboard | /api/v1/hub/* |
|
||||||
|
| secubox-core | Shared library | - |
|
||||||
|
| secubox-portal | Login portal | /api/v1/portal/* |
|
||||||
|
| secubox-system | System management | /api/v1/system/* |
|
||||||
|
|
||||||
|
## Security
|
||||||
|
| Module | Description | API Endpoints |
|
||||||
|
|--------|-------------|---------------|
|
||||||
|
| secubox-crowdsec | Intrusion prevention | /api/v1/crowdsec/* |
|
||||||
|
| secubox-waf | Web application firewall | /api/v1/waf/* |
|
||||||
|
| secubox-auth | OAuth2/OIDC | /api/v1/auth/* |
|
||||||
|
| secubox-nac | Network access control | /api/v1/nac/* |
|
||||||
|
|
||||||
|
## Network
|
||||||
|
| Module | Description | API Endpoints |
|
||||||
|
|--------|-------------|---------------|
|
||||||
|
| secubox-wireguard | VPN management | /api/v1/wireguard/* |
|
||||||
|
| secubox-haproxy | Load balancer | /api/v1/haproxy/* |
|
||||||
|
| secubox-netmodes | Network modes | /api/v1/netmodes/* |
|
||||||
|
| secubox-qos | Traffic shaping | /api/v1/qos/* |
|
||||||
|
| secubox-dpi | Deep packet inspection | /api/v1/dpi/* |
|
||||||
|
| secubox-vhost | Virtual hosts | /api/v1/vhost/* |
|
||||||
|
| secubox-cdn | CDN cache | /api/v1/cdn/* |
|
||||||
|
|
||||||
|
## Applications
|
||||||
|
| Module | Description | API Endpoints |
|
||||||
|
|--------|-------------|---------------|
|
||||||
|
| secubox-mail | Email server | /api/v1/mail/* |
|
||||||
|
| secubox-dns | DNS management | /api/v1/dns/* |
|
||||||
|
| secubox-users | User management | /api/v1/users/* |
|
||||||
|
| secubox-gitea | Git server | /api/v1/gitea/* |
|
||||||
|
| secubox-nextcloud | File sync | /api/v1/nextcloud/* |
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
| Module | Description | API Endpoints |
|
||||||
|
|--------|-------------|---------------|
|
||||||
|
| secubox-netdata | Real-time metrics | /api/v1/netdata/* |
|
||||||
|
| secubox-metrics | Dashboard metrics | /api/v1/metrics/* |
|
||||||
|
| secubox-soc | Security operations | /api/v1/soc/* |
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Upload wiki artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-wiki
|
||||||
|
path: wiki-output/
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
# Create release on tag
|
||||||
|
release:
|
||||||
|
needs: [build-iso, build-wiki]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download ISO artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-installer-iso-amd64
|
||||||
|
path: release/
|
||||||
|
|
||||||
|
- name: Download wiki artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-wiki
|
||||||
|
path: wiki/
|
||||||
|
|
||||||
|
- name: Compress ISO
|
||||||
|
run: |
|
||||||
|
cd release/
|
||||||
|
for f in *.iso *.img; do
|
||||||
|
[ -f "$f" ] && gzip -9 "$f" && echo "Compressed: $f"
|
||||||
|
done
|
||||||
|
ls -lh
|
||||||
|
|
||||||
|
- name: Generate final checksums
|
||||||
|
run: |
|
||||||
|
cd release/
|
||||||
|
sha256sum *.gz 2>/dev/null > SHA256SUMS || true
|
||||||
|
cat SHA256SUMS
|
||||||
|
|
||||||
|
- name: Sign checksums
|
||||||
|
env:
|
||||||
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
run: |
|
||||||
|
if [ -n "$GPG_PRIVATE_KEY" ]; then
|
||||||
|
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||||
|
cd release/
|
||||||
|
gpg --clearsign SHA256SUMS
|
||||||
|
mv SHA256SUMS.asc SHA256SUMS.gpg
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "date=$(date -u +%Y-%m-%d)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Delete existing release assets (if any)
|
||||||
|
run: |
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
if gh release view "$VERSION" &>/dev/null; then
|
||||||
|
echo "Release $VERSION exists, deleting duplicate assets..."
|
||||||
|
for file in release/*.iso.gz release/*.img.gz release/SHA256SUMS release/SHA256SUMS.gpg; do
|
||||||
|
[ -f "$file" ] || continue
|
||||||
|
name=$(basename "$file")
|
||||||
|
gh release delete-asset "$VERSION" "$name" --yes 2>/dev/null || true
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
name: SecuBox Installer ${{ steps.version.outputs.version }}
|
||||||
|
body: |
|
||||||
|
## SecuBox Installer ISO ${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
|
Release date: ${{ steps.version.outputs.date }}
|
||||||
|
|
||||||
|
### Downloads
|
||||||
|
|
||||||
|
| File | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `secubox-installer-amd64-bookworm.iso.gz` | Bootable ISO (UEFI/BIOS) |
|
||||||
|
| `secubox-installer-amd64-bookworm.img.gz` | Raw USB image |
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- **Hybrid Boot**: Works as Live USB or Installer
|
||||||
|
- **UEFI + Legacy BIOS**: Compatible with all systems
|
||||||
|
- **French AZERTY keyboard**: Pre-configured
|
||||||
|
- **DHCP networking**: Auto-configured on boot
|
||||||
|
- **51 SecuBox packages**: Pre-installed
|
||||||
|
- **Preseed support**: Clone existing installations
|
||||||
|
|
||||||
|
### Boot Options
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| SecuBox Live | Boot live system with persistence |
|
||||||
|
| SecuBox Install | Headless auto-install to first disk |
|
||||||
|
| SecuBox Live (To RAM) | Load entire system to memory |
|
||||||
|
| SecuBox Debug (Rescue) | Minimal rescue shell |
|
||||||
|
|
||||||
|
### Flash to USB
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Download and extract
|
||||||
|
gunzip secubox-installer-amd64-bookworm.img.gz
|
||||||
|
|
||||||
|
# Flash (replace sdX with your USB device)
|
||||||
|
sudo dd if=secubox-installer-amd64-bookworm.img of=/dev/sdX bs=4M status=progress conv=fsync
|
||||||
|
```
|
||||||
|
|
||||||
|
### Credentials
|
||||||
|
|
||||||
|
| Service | Username | Password |
|
||||||
|
|---------|----------|----------|
|
||||||
|
| SSH | root | secubox |
|
||||||
|
| SSH | secubox | secubox |
|
||||||
|
| Web UI | admin | admin |
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sha256sum -c SHA256SUMS
|
||||||
|
gpg --verify SHA256SUMS.gpg
|
||||||
|
```
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- [Installation Guide](https://github.com/${{ github.repository }}/wiki/Installation)
|
||||||
|
- [API Reference](https://github.com/${{ github.repository }}/wiki/API-Reference)
|
||||||
|
- [Module Documentation](https://github.com/${{ github.repository }}/wiki/Modules)
|
||||||
|
|
||||||
|
files: |
|
||||||
|
release/*.iso.gz
|
||||||
|
release/*.img.gz
|
||||||
|
release/SHA256SUMS
|
||||||
|
release/SHA256SUMS.gpg
|
||||||
|
draft: false
|
||||||
|
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }}
|
||||||
|
|
||||||
|
- name: Update wiki
|
||||||
|
if: success()
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
echo "Wiki artifacts ready for manual deployment"
|
||||||
|
ls -la wiki/
|
||||||
37
.gitea/workflows/build-live-usb.yml
Normal file
37
.gitea/workflows/build-live-usb.yml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# DEPRECATED: Use build-all-live-usb.yml for all live USB builds
|
||||||
|
# This workflow is maintained for backward compatibility only.
|
||||||
|
# It calls the unified workflow with platform=x64.
|
||||||
|
|
||||||
|
name: Build SecuBox Live USB (x64 only - DEPRECATED)
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
compress:
|
||||||
|
description: 'Compress output (gzip)'
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Redirect to unified workflow
|
||||||
|
build-x64-live-usb:
|
||||||
|
uses: ./.github/workflows/build-all-live-usb.yml
|
||||||
|
with:
|
||||||
|
platform: x64
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
# Note about deprecation
|
||||||
|
deprecation-notice:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Deprecation Warning
|
||||||
|
run: |
|
||||||
|
echo "::warning::This workflow is DEPRECATED. Use build-all-live-usb.yml instead."
|
||||||
|
echo ""
|
||||||
|
echo "The unified workflow supports all platforms:"
|
||||||
|
echo " - x64 (amd64) - GRUB UEFI/BIOS"
|
||||||
|
echo " - espressobin-v7 (arm64) - U-Boot"
|
||||||
|
echo " - rpi400 (arm64) - Pi firmware"
|
||||||
|
echo ""
|
||||||
|
echo "To build all platforms:"
|
||||||
|
echo " gh workflow run build-all-live-usb.yml -f platform=all"
|
||||||
280
.gitea/workflows/build-multiboot.yml
Normal file
280
.gitea/workflows/build-multiboot.yml
Normal file
|
|
@ -0,0 +1,280 @@
|
||||||
|
name: Build Multiboot Live Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
image_size:
|
||||||
|
description: 'Image size in GB'
|
||||||
|
required: false
|
||||||
|
default: '16'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- '8'
|
||||||
|
- '16'
|
||||||
|
- '32'
|
||||||
|
include_desktop:
|
||||||
|
description: 'Include desktop environment'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- 'true'
|
||||||
|
- 'false'
|
||||||
|
create_release:
|
||||||
|
description: 'Create GitHub release'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- 'true'
|
||||||
|
- 'false'
|
||||||
|
prerelease:
|
||||||
|
description: 'Mark as prerelease'
|
||||||
|
required: false
|
||||||
|
default: 'true'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- 'true'
|
||||||
|
- 'false'
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'multiboot-v*'
|
||||||
|
paths:
|
||||||
|
- 'image/multiboot/**'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'image/multiboot/**'
|
||||||
|
|
||||||
|
env:
|
||||||
|
VERSION: '2.2.4-pre1'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Build all .deb packages first
|
||||||
|
build-packages:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Free disk space
|
||||||
|
run: |
|
||||||
|
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache
|
||||||
|
df -h
|
||||||
|
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y -qq \
|
||||||
|
build-essential devscripts debhelper \
|
||||||
|
dh-python python3-all python3-setuptools \
|
||||||
|
crossbuild-essential-arm64
|
||||||
|
|
||||||
|
- name: Build SecuBox packages
|
||||||
|
run: |
|
||||||
|
mkdir -p output/debs
|
||||||
|
|
||||||
|
# Build each package
|
||||||
|
for pkg in packages/secubox-*/; do
|
||||||
|
if [ -d "$pkg/debian" ]; then
|
||||||
|
echo "Building $(basename $pkg)..."
|
||||||
|
cd "$pkg"
|
||||||
|
dpkg-buildpackage -us -uc -b --host-arch=arm64 2>/dev/null || \
|
||||||
|
dpkg-buildpackage -us -uc -b 2>/dev/null || \
|
||||||
|
echo "WARNING: Failed to build $(basename $pkg)"
|
||||||
|
cd - >/dev/null
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Collect all .deb files
|
||||||
|
find packages/ -name "*.deb" -exec cp {} output/debs/ \;
|
||||||
|
ls -la output/debs/ || echo "No packages built"
|
||||||
|
|
||||||
|
- name: Upload packages artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-packages
|
||||||
|
path: output/debs/
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
# Build the multiboot image
|
||||||
|
build-multiboot:
|
||||||
|
needs: build-packages
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Free disk space
|
||||||
|
run: |
|
||||||
|
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache
|
||||||
|
df -h
|
||||||
|
|
||||||
|
- name: Install build tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y -qq \
|
||||||
|
parted dosfstools e2fsprogs \
|
||||||
|
debootstrap qemu-user-static binfmt-support \
|
||||||
|
squashfs-tools xz-utils rsync grub-efi-amd64-bin \
|
||||||
|
grub-pc-bin u-boot-tools
|
||||||
|
|
||||||
|
# Ensure ARM64 binfmt is registered
|
||||||
|
sudo systemctl restart binfmt-support || true
|
||||||
|
sudo update-binfmts --enable qemu-aarch64 || true
|
||||||
|
|
||||||
|
# Verify QEMU is working
|
||||||
|
if [ -f /proc/sys/fs/binfmt_misc/qemu-aarch64 ]; then
|
||||||
|
echo "✓ QEMU ARM64 binfmt registered"
|
||||||
|
else
|
||||||
|
echo "⚠ QEMU ARM64 binfmt not found, attempting manual registration"
|
||||||
|
sudo update-binfmts --import qemu-aarch64 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Download packages artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-packages
|
||||||
|
path: output/debs/
|
||||||
|
|
||||||
|
- name: List available packages
|
||||||
|
run: |
|
||||||
|
echo "SecuBox packages available for slipstream:"
|
||||||
|
ls -la output/debs/ || echo "No packages found"
|
||||||
|
|
||||||
|
- name: Build multiboot image
|
||||||
|
run: |
|
||||||
|
IMAGE_SIZE="${{ github.event.inputs.image_size || '16' }}"
|
||||||
|
DESKTOP="${{ github.event.inputs.include_desktop || 'false' }}"
|
||||||
|
|
||||||
|
mkdir -p output
|
||||||
|
|
||||||
|
BUILD_ARGS="--output output/secubox-multiboot-${{ env.VERSION }}.img"
|
||||||
|
BUILD_ARGS="$BUILD_ARGS --size ${IMAGE_SIZE}G"
|
||||||
|
|
||||||
|
if [ "$DESKTOP" = "true" ]; then
|
||||||
|
BUILD_ARGS="$BUILD_ARGS --desktop"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Building multiboot image with: $BUILD_ARGS"
|
||||||
|
sudo bash image/multiboot/build-multiboot.sh $BUILD_ARGS
|
||||||
|
|
||||||
|
sudo chown $(id -u):$(id -g) output/secubox-multiboot-${{ env.VERSION }}.img
|
||||||
|
timeout-minutes: 120
|
||||||
|
|
||||||
|
- name: Verify boot files
|
||||||
|
run: |
|
||||||
|
# Mount EFI partition and verify kernel files exist
|
||||||
|
LOOP_DEV=$(sudo losetup -f --show -P output/secubox-multiboot-${{ env.VERSION }}.img)
|
||||||
|
sudo mkdir -p /tmp/verify-efi
|
||||||
|
sudo mount "${LOOP_DEV}p1" /tmp/verify-efi
|
||||||
|
|
||||||
|
echo "=== EFI Partition Boot Files ==="
|
||||||
|
ls -la /tmp/verify-efi/
|
||||||
|
|
||||||
|
# Check for required files
|
||||||
|
MISSING=0
|
||||||
|
for f in Image vmlinuz initrd.img initrd-amd64.img boot.scr; do
|
||||||
|
if [ -f "/tmp/verify-efi/$f" ]; then
|
||||||
|
echo "✓ $f present ($(du -h /tmp/verify-efi/$f | cut -f1))"
|
||||||
|
else
|
||||||
|
echo "✗ $f MISSING!"
|
||||||
|
MISSING=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check DTBs
|
||||||
|
DTB_COUNT=$(ls /tmp/verify-efi/dtbs/marvell/*.dtb 2>/dev/null | wc -l)
|
||||||
|
if [ "$DTB_COUNT" -gt 0 ]; then
|
||||||
|
echo "✓ DTBs present ($DTB_COUNT files)"
|
||||||
|
else
|
||||||
|
echo "✗ DTBs MISSING!"
|
||||||
|
MISSING=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo umount /tmp/verify-efi
|
||||||
|
sudo losetup -d "$LOOP_DEV"
|
||||||
|
|
||||||
|
if [ "$MISSING" -eq 1 ]; then
|
||||||
|
echo "ERROR: Required boot files missing!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Compress image
|
||||||
|
run: |
|
||||||
|
cd output
|
||||||
|
echo "Compressing multiboot image..."
|
||||||
|
xz -9 -v -T0 secubox-multiboot-${{ env.VERSION }}.img
|
||||||
|
ls -lh secubox-multiboot-${{ env.VERSION }}.img.xz
|
||||||
|
|
||||||
|
- name: Generate checksums
|
||||||
|
run: |
|
||||||
|
cd output
|
||||||
|
sha256sum secubox-multiboot-${{ env.VERSION }}.img.xz > secubox-multiboot-${{ env.VERSION }}.sha256
|
||||||
|
cat secubox-multiboot-${{ env.VERSION }}.sha256
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-multiboot-${{ env.VERSION }}
|
||||||
|
path: |
|
||||||
|
output/secubox-multiboot-${{ env.VERSION }}.img.xz
|
||||||
|
output/secubox-multiboot-${{ env.VERSION }}.sha256
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Upload to release (on tag or manual)
|
||||||
|
if: startsWith(github.ref, 'refs/tags/multiboot-v') || github.event.inputs.create_release == 'true'
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
tag_name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || format('multiboot-v{0}', env.VERSION) }}
|
||||||
|
name: SecuBox Multiboot v${{ env.VERSION }}
|
||||||
|
prerelease: ${{ github.event.inputs.prerelease == 'true' || contains(env.VERSION, 'pre') }}
|
||||||
|
files: |
|
||||||
|
output/secubox-multiboot-${{ env.VERSION }}.img.xz
|
||||||
|
output/secubox-multiboot-${{ env.VERSION }}.sha256
|
||||||
|
body: |
|
||||||
|
|
||||||
|
## SecuBox Multiboot v${{ env.VERSION }} — Live RAM OS
|
||||||
|
|
||||||
|
### 🎯 What's New
|
||||||
|
- **Dual Boot Menu** — Interactive menu with 5s timeout: Live RAM Boot (default) or Flash to eMMC
|
||||||
|
- **Dual Architecture** — Boot ARM64 (MOCHAbin/ESPRESSObin) or AMD64 (x86_64 PC)
|
||||||
|
- **RAM-based Live OS** — Reduced I/O, ideal for USB gadget devices
|
||||||
|
- **Auto Kernel Install** — ARM64 kernel, DTBs, and initrd properly installed on EFI partition
|
||||||
|
- **SecuBox Pre-installed** — All modules slipstreamed, ready to use
|
||||||
|
- **Shared Data Partition** — Persistent storage accessible from both architectures
|
||||||
|
|
||||||
|
### 📦 Image Layout
|
||||||
|
|
||||||
|
| Partition | Size | Type | Description |
|
||||||
|
|-----------|------|------|-------------|
|
||||||
|
| EFI | 512MB | FAT32 | GRUB + U-Boot for multi-arch boot |
|
||||||
|
| ARM64 | ~4GB | ext4 | Debian bookworm arm64 rootfs |
|
||||||
|
| AMD64 | ~4GB | ext4 | Debian bookworm amd64 rootfs |
|
||||||
|
| Data | ~7GB | ext4 | Shared persistent storage |
|
||||||
|
|
||||||
|
### 🔧 SecuBox Modules Included
|
||||||
|
- secubox-core, secubox-hub
|
||||||
|
- secubox-crowdsec, secubox-haproxy
|
||||||
|
- secubox-system, secubox-hardening
|
||||||
|
- secubox-ipblock, and more...
|
||||||
|
|
||||||
|
### 💾 Flash to SD/USB
|
||||||
|
```bash
|
||||||
|
xzcat secubox-multiboot-${{ env.VERSION }}.img.xz | sudo dd of=/dev/sdX bs=4M status=progress conv=fsync
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🔐 Default credentials
|
||||||
|
- User: `secubox`
|
||||||
|
- Password: `secubox`
|
||||||
|
- Root: `secubox`
|
||||||
|
|
||||||
|
### 🎯 Use Cases
|
||||||
|
- **Demo/Recovery** — Boot from USB to demo or repair SecuBox installations
|
||||||
|
- **Factory Reset** — Clone to eMMC/SD for fresh installations
|
||||||
|
- **Pi Zero Eye Remote** — USB gadget boot media with reduced I/O
|
||||||
|
|
||||||
|
### ⏱️ First boot
|
||||||
|
~90s (all packages pre-installed, no internet required)
|
||||||
|
|
||||||
|
See [Multiboot Wiki](https://github.com/CyberMind-FR/secubox-deb/wiki/Multiboot) for full documentation.
|
||||||
341
.gitea/workflows/build-packages.yml
Normal file
341
.gitea/workflows/build-packages.yml
Normal file
|
|
@ -0,0 +1,341 @@
|
||||||
|
name: Build SecuBox .deb packages
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, master]
|
||||||
|
tags: ['v*']
|
||||||
|
pull_request:
|
||||||
|
branches: [main, master]
|
||||||
|
workflow_call:
|
||||||
|
secrets:
|
||||||
|
GPG_PRIVATE_KEY:
|
||||||
|
required: false
|
||||||
|
DEPLOY_SSH_KEY:
|
||||||
|
required: false
|
||||||
|
DEPLOY_KNOWN_HOSTS:
|
||||||
|
required: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
package:
|
||||||
|
description: 'Package to build (empty = all)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
arch:
|
||||||
|
description: 'Architecture'
|
||||||
|
required: true
|
||||||
|
default: 'amd64'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- amd64
|
||||||
|
- arm64
|
||||||
|
- both
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Discover all packages dynamically.
|
||||||
|
#
|
||||||
|
# Emits a single flat matrix of {package, arch} objects pre-filtered to skip
|
||||||
|
# arch-all+arm64 combos. This avoids a cross-product expansion that would
|
||||||
|
# exceed GitHub Actions' hard 256-jobs-per-matrix cap once the package
|
||||||
|
# catalog grows past ~128 entries (we hit 134 in v2.9.0).
|
||||||
|
discover:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
packages: ${{ steps.find.outputs.packages }}
|
||||||
|
matrix: ${{ steps.find.outputs.matrix }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Find packages + build flat matrix
|
||||||
|
id: find
|
||||||
|
env:
|
||||||
|
REQUESTED_ARCH: ${{ github.event.inputs.arch }}
|
||||||
|
REQUESTED_PKG: ${{ github.event.inputs.package }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
# Still emit the legacy `packages` output for backwards compat.
|
||||||
|
packages=$(find packages/secubox-* -path "*/debian/control" -not -path "*/debian/*/DEBIAN/control" \
|
||||||
|
| xargs dirname | xargs dirname | xargs -n1 basename \
|
||||||
|
| sort | jq -R -s -c 'split("\n") | map(select(length > 0))')
|
||||||
|
echo "packages=$packages" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Found packages: $packages"
|
||||||
|
|
||||||
|
# Build the flat {package, arch} matrix. Honour the workflow_dispatch
|
||||||
|
# `arch` and `package` filters if set (empty on `push: tags` events).
|
||||||
|
requested_arch="${REQUESTED_ARCH:-}"
|
||||||
|
requested_pkg="${REQUESTED_PKG:-}"
|
||||||
|
|
||||||
|
combos=$(find packages/secubox-* -path "*/debian/control" -not -path "*/debian/*/DEBIAN/control" \
|
||||||
|
| sort | while read -r ctrl; do
|
||||||
|
pkg=$(basename "$(dirname "$(dirname "$ctrl")")")
|
||||||
|
if [ -n "$requested_pkg" ] && [ "$requested_pkg" != "$pkg" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
is_all=0
|
||||||
|
if grep -q "^Architecture:[[:space:]]*all" "$ctrl"; then
|
||||||
|
is_all=1
|
||||||
|
fi
|
||||||
|
# amd64 always, unless arch=arm64 was requested explicitly.
|
||||||
|
if [ -z "$requested_arch" ] || [ "$requested_arch" = "amd64" ]; then
|
||||||
|
echo "{\"package\":\"$pkg\",\"arch\":\"amd64\"}"
|
||||||
|
fi
|
||||||
|
# arm64 only for arch-specific packages, unless arch=amd64 requested.
|
||||||
|
if [ "$is_all" = "0" ] && { [ -z "$requested_arch" ] || [ "$requested_arch" = "arm64" ]; }; then
|
||||||
|
echo "{\"package\":\"$pkg\",\"arch\":\"arm64\"}"
|
||||||
|
fi
|
||||||
|
done | jq -s -c .)
|
||||||
|
echo "matrix=$combos" >> "$GITHUB_OUTPUT"
|
||||||
|
# Print the size so over-cap regressions are visible in the log.
|
||||||
|
echo "Matrix size: $(echo "$combos" | jq 'length') combos"
|
||||||
|
|
||||||
|
# Build packages — one job per {package, arch} entry from discover.
|
||||||
|
build:
|
||||||
|
needs: discover
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include: ${{ fromJson(needs.discover.outputs.matrix) }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Skip if specific package requested
|
||||||
|
if: ${{ github.event.inputs.package != '' && github.event.inputs.package != matrix.package }}
|
||||||
|
run: echo "Skipping ${{ matrix.package }}" && exit 0
|
||||||
|
|
||||||
|
- name: Check if arch-all package
|
||||||
|
id: check-arch
|
||||||
|
run: |
|
||||||
|
if [ -f "packages/${{ matrix.package }}/debian/control" ]; then
|
||||||
|
if grep -q "Architecture: all" "packages/${{ matrix.package }}/debian/control"; then
|
||||||
|
echo "is_arch_all=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "is_arch_all=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "is_arch_all=true" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Skip arm64 for arch-all packages
|
||||||
|
if: matrix.arch == 'arm64' && steps.check-arch.outputs.is_arch_all == 'true'
|
||||||
|
run: |
|
||||||
|
echo "⏭ Skipping arm64 build for Architecture: all package"
|
||||||
|
echo " arch-all packages are architecture-independent"
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
- name: Install build dependencies
|
||||||
|
if: "!(matrix.arch == 'arm64' && steps.check-arch.outputs.is_arch_all == 'true')"
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y -qq \
|
||||||
|
build-essential dpkg-dev debhelper devscripts fakeroot \
|
||||||
|
dh-python python3-all python3-setuptools
|
||||||
|
|
||||||
|
- name: Setup Node.js (for soc-web)
|
||||||
|
if: matrix.package == 'secubox-soc-web'
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Build React app (for soc-web)
|
||||||
|
if: matrix.package == 'secubox-soc-web'
|
||||||
|
run: |
|
||||||
|
cd packages/secubox-soc-web
|
||||||
|
npm ci --ignore-scripts || npm install --ignore-scripts
|
||||||
|
npm run build
|
||||||
|
ls -la dist/
|
||||||
|
|
||||||
|
- name: Setup Go (for daemon)
|
||||||
|
if: matrix.package == 'secubox-daemon'
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.22'
|
||||||
|
|
||||||
|
- name: Build Go binaries (for daemon)
|
||||||
|
if: matrix.package == 'secubox-daemon'
|
||||||
|
env:
|
||||||
|
GOOS: linux
|
||||||
|
GOARCH: ${{ matrix.arch == 'arm64' && 'arm64' || 'amd64' }}
|
||||||
|
run: |
|
||||||
|
cd daemon
|
||||||
|
mkdir -p build
|
||||||
|
go build -ldflags "-s -w" -o build/secuboxd ./cmd/secuboxd
|
||||||
|
go build -ldflags "-s -w" -o build/secuboxctl ./cmd/secuboxctl
|
||||||
|
go build -ldflags "-s -w" -o build/c3box ./c3box/backend
|
||||||
|
ls -la build/
|
||||||
|
|
||||||
|
- name: Build ${{ matrix.package }} (${{ matrix.arch }})
|
||||||
|
if: "!(matrix.arch == 'arm64' && steps.check-arch.outputs.is_arch_all == 'true')"
|
||||||
|
run: |
|
||||||
|
cd packages/${{ matrix.package }}
|
||||||
|
|
||||||
|
if [ ! -f debian/control ]; then
|
||||||
|
echo "⚠ debian/control missing — skip"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
dpkg-buildpackage -us -uc -b
|
||||||
|
|
||||||
|
echo "✅ Build OK: ${{ matrix.package }} (${{ matrix.arch }})"
|
||||||
|
|
||||||
|
- name: Collect artifacts
|
||||||
|
if: "!(matrix.arch == 'arm64' && steps.check-arch.outputs.is_arch_all == 'true')"
|
||||||
|
run: |
|
||||||
|
mkdir -p artifacts
|
||||||
|
find packages/ -maxdepth 1 -name "*.deb" -exec cp {} artifacts/ \;
|
||||||
|
ls -lh artifacts/ || echo "No .deb files"
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
if: "!(matrix.arch == 'arm64' && steps.check-arch.outputs.is_arch_all == 'true')"
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.package }}-${{ matrix.arch }}
|
||||||
|
path: artifacts/*.deb
|
||||||
|
if-no-files-found: warn
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
# Combine all artifacts. `if: always() && != cancelled` so we still
|
||||||
|
# publish whatever .deb files the matrix succeeded on, even when a few
|
||||||
|
# entries failed (partial-release resilience).
|
||||||
|
collect:
|
||||||
|
needs: build
|
||||||
|
if: ${{ always() && needs.build.result != 'cancelled' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Download all artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: all-debs/
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Generate SHA256SUMS
|
||||||
|
run: |
|
||||||
|
cd all-debs/
|
||||||
|
find . -name "*.deb" -exec mv {} . \; 2>/dev/null || true
|
||||||
|
sha256sum *.deb > SHA256SUMS 2>/dev/null || echo "No debs found"
|
||||||
|
cat SHA256SUMS
|
||||||
|
|
||||||
|
- name: Upload combined artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-debs-all
|
||||||
|
path: all-debs/
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
# Publish to APT repo (on tag) — partial-release resilient.
|
||||||
|
publish:
|
||||||
|
needs: collect
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ always() && needs.collect.result == 'success' && startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
environment: production
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download all packages
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-debs-all
|
||||||
|
path: debs/
|
||||||
|
|
||||||
|
- name: Install reprepro
|
||||||
|
run: sudo apt-get install -y reprepro gnupg
|
||||||
|
|
||||||
|
- name: Import GPG key
|
||||||
|
env:
|
||||||
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
run: |
|
||||||
|
if [ -z "$GPG_PRIVATE_KEY" ]; then
|
||||||
|
echo "⚠ GPG_PRIVATE_KEY secret not set - skipping signing"
|
||||||
|
echo "SKIP_SIGN=true" >> $GITHUB_ENV
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||||
|
gpg --list-keys
|
||||||
|
|
||||||
|
- name: Setup repository
|
||||||
|
if: env.SKIP_SIGN != 'true'
|
||||||
|
run: |
|
||||||
|
mkdir -p repo/conf
|
||||||
|
cp repo/conf/distributions repo/conf/ 2>/dev/null || true
|
||||||
|
cat > repo/conf/options << EOF
|
||||||
|
verbose
|
||||||
|
basedir $(pwd)/repo
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Add packages to repository
|
||||||
|
if: env.SKIP_SIGN != 'true'
|
||||||
|
run: |
|
||||||
|
for deb in debs/*.deb; do
|
||||||
|
[ -f "$deb" ] || continue
|
||||||
|
echo "Adding: $(basename $deb)"
|
||||||
|
reprepro -b repo includedeb bookworm "$deb" || true
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Export public key
|
||||||
|
if: env.SKIP_SIGN != 'true'
|
||||||
|
run: |
|
||||||
|
gpg --armor --export packages@secubox.in > repo/secubox-keyring.gpg
|
||||||
|
|
||||||
|
- name: Deploy to server
|
||||||
|
if: env.SKIP_SIGN != 'true'
|
||||||
|
env:
|
||||||
|
SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||||
|
KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
|
||||||
|
run: |
|
||||||
|
if [ -z "$SSH_PRIVATE_KEY" ]; then
|
||||||
|
echo "⚠ DEPLOY_SSH_KEY not set - skipping deploy"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
||||||
|
chmod 600 ~/.ssh/id_ed25519
|
||||||
|
echo "$KNOWN_HOSTS" > ~/.ssh/known_hosts
|
||||||
|
|
||||||
|
rsync -avz --delete \
|
||||||
|
repo/dists/ repo/pool/ repo/secubox-keyring.gpg \
|
||||||
|
deploy@apt.secubox.in:/var/www/apt.secubox.in/
|
||||||
|
|
||||||
|
- name: Delete existing release assets (if any)
|
||||||
|
run: |
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
if gh release view "$VERSION" &>/dev/null; then
|
||||||
|
echo "Release $VERSION exists, checking for duplicate assets..."
|
||||||
|
for file in debs/*.deb debs/SHA256SUMS; do
|
||||||
|
[ -f "$file" ] || continue
|
||||||
|
name=$(basename "$file")
|
||||||
|
gh release delete-asset "$VERSION" "$name" --yes 2>/dev/null || true
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
debs/*.deb
|
||||||
|
debs/SHA256SUMS
|
||||||
|
body: |
|
||||||
|
## SecuBox-DEB ${{ github.ref_name }}
|
||||||
|
|
||||||
|
Debian bookworm packages for GlobalScale MOCHAbin / ESPRESSObin (arm64) and x86_64.
|
||||||
|
|
||||||
|
**33 packages** including:
|
||||||
|
- Core infrastructure (hub, core, portal)
|
||||||
|
- Security (crowdsec, waf, auth, nac)
|
||||||
|
- Networking (wireguard, netmodes, dpi, qos)
|
||||||
|
- Applications (mail, gitea, nextcloud, webmail)
|
||||||
|
- Publishing (droplet, streamlit, metablogizer)
|
||||||
|
|
||||||
|
**Installation:**
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://apt.secubox.in/secubox-keyring.gpg | \
|
||||||
|
sudo tee /usr/share/keyrings/secubox.gpg > /dev/null
|
||||||
|
echo "deb [signed-by=/usr/share/keyrings/secubox.gpg] https://apt.secubox.in bookworm main" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/secubox.list
|
||||||
|
sudo apt update && sudo apt install secubox-full
|
||||||
|
```
|
||||||
201
.gitea/workflows/build-secubox-cli.yml
Normal file
201
.gitea/workflows/build-secubox-cli.yml
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
# SecuBox CLI — Build Go Binary
|
||||||
|
# Builds the secubox meta-script generator for linux-amd64 and linux-arm64
|
||||||
|
name: Build SecuBox CLI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master, main]
|
||||||
|
paths:
|
||||||
|
- 'cmd/secubox/**'
|
||||||
|
- '.github/workflows/build-secubox-cli.yml'
|
||||||
|
tags: ['v*']
|
||||||
|
pull_request:
|
||||||
|
branches: [master, main]
|
||||||
|
paths:
|
||||||
|
- 'cmd/secubox/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Version override (e.g., v2.8.0)'
|
||||||
|
required: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
GO_VERSION: '1.22'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ${{ env.GO_VERSION }}
|
||||||
|
cache-dependency-path: cmd/secubox/go.sum
|
||||||
|
|
||||||
|
- name: Download dependencies
|
||||||
|
working-directory: cmd/secubox
|
||||||
|
run: go mod download
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
working-directory: cmd/secubox
|
||||||
|
run: go test -v -race -coverprofile=coverage.out ./...
|
||||||
|
|
||||||
|
- name: Upload coverage
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: coverage
|
||||||
|
path: cmd/secubox/coverage.out
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
needs: test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- goos: linux
|
||||||
|
goarch: amd64
|
||||||
|
suffix: linux-amd64
|
||||||
|
- goos: linux
|
||||||
|
goarch: arm64
|
||||||
|
suffix: linux-arm64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # For git describe
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ${{ env.GO_VERSION }}
|
||||||
|
cache-dependency-path: cmd/secubox/go.sum
|
||||||
|
|
||||||
|
- name: Determine version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
if [ -n "${{ github.event.inputs.version }}" ]; then
|
||||||
|
VERSION="${{ github.event.inputs.version }}"
|
||||||
|
elif [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
else
|
||||||
|
VERSION="$(git describe --tags --always --dirty 2>/dev/null || echo 'dev')"
|
||||||
|
fi
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "Building version: $VERSION"
|
||||||
|
|
||||||
|
- name: Build binary
|
||||||
|
working-directory: cmd/secubox
|
||||||
|
env:
|
||||||
|
GOOS: ${{ matrix.goos }}
|
||||||
|
GOARCH: ${{ matrix.goarch }}
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
BUILD_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
COMMIT=$(git rev-parse --short HEAD)
|
||||||
|
|
||||||
|
go build -ldflags="-s -w \
|
||||||
|
-X main.Version=$VERSION \
|
||||||
|
-X main.BuildTime=$BUILD_TIME \
|
||||||
|
-X main.Commit=$COMMIT" \
|
||||||
|
-o secubox-${{ matrix.suffix }} .
|
||||||
|
|
||||||
|
- name: Compress binary
|
||||||
|
working-directory: cmd/secubox
|
||||||
|
run: |
|
||||||
|
tar -czvf secubox-${{ matrix.suffix }}.tar.gz secubox-${{ matrix.suffix }}
|
||||||
|
sha256sum secubox-${{ matrix.suffix }}.tar.gz > secubox-${{ matrix.suffix }}.tar.gz.sha256
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-${{ matrix.suffix }}
|
||||||
|
path: |
|
||||||
|
cmd/secubox/secubox-${{ matrix.suffix }}.tar.gz
|
||||||
|
cmd/secubox/secubox-${{ matrix.suffix }}.tar.gz.sha256
|
||||||
|
|
||||||
|
release:
|
||||||
|
name: Release
|
||||||
|
needs: build
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download all artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: artifacts/
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Generate release notes
|
||||||
|
run: |
|
||||||
|
cat > RELEASE_NOTES.md << 'EOF'
|
||||||
|
# SecuBox CLI ${{ github.ref_name }}
|
||||||
|
|
||||||
|
Meta-script generator for SecuBox-DEB image building.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Profile-based image generation
|
||||||
|
- Board detection and auto-configuration
|
||||||
|
- A/B partition OTA updates
|
||||||
|
- GitHub releases integration
|
||||||
|
|
||||||
|
## Downloads
|
||||||
|
|
||||||
|
| Platform | File | SHA256 |
|
||||||
|
|----------|------|--------|
|
||||||
|
| Linux x64 | `secubox-linux-amd64.tar.gz` | [checksum] |
|
||||||
|
| Linux ARM64 | `secubox-linux-arm64.tar.gz` | [checksum] |
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux x64
|
||||||
|
curl -LO https://github.com/CyberMind-FR/secubox-deb/releases/download/${{ github.ref_name }}/secubox-linux-amd64.tar.gz
|
||||||
|
tar xzf secubox-linux-amd64.tar.gz
|
||||||
|
sudo mv secubox-linux-amd64 /usr/local/bin/secubox
|
||||||
|
|
||||||
|
# Linux ARM64
|
||||||
|
curl -LO https://github.com/CyberMind-FR/secubox-deb/releases/download/${{ github.ref_name }}/secubox-linux-arm64.tar.gz
|
||||||
|
tar xzf secubox-linux-arm64.tar.gz
|
||||||
|
sudo mv secubox-linux-arm64 /usr/local/bin/secubox
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Interactive wizard
|
||||||
|
secubox gen
|
||||||
|
|
||||||
|
# Generate for specific board
|
||||||
|
secubox gen --board mochabin --tier pro
|
||||||
|
|
||||||
|
# Build image
|
||||||
|
secubox build --board mochabin --output secubox.img
|
||||||
|
|
||||||
|
# Check hardware
|
||||||
|
secubox info
|
||||||
|
|
||||||
|
# OTA update
|
||||||
|
secubox ota update --url https://releases.secubox.in/v2.8.0/secubox-mochabin.img.gz
|
||||||
|
```
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
name: SecuBox CLI ${{ github.ref_name }}
|
||||||
|
body_path: RELEASE_NOTES.md
|
||||||
|
files: |
|
||||||
|
artifacts/*.tar.gz
|
||||||
|
artifacts/*.sha256
|
||||||
|
draft: false
|
||||||
|
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }}
|
||||||
22
.gitea/workflows/license-check.yml
Normal file
22
.gitea/workflows/license-check.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||||
|
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
||||||
|
# Source-Disclosed License — All rights reserved except as expressly granted.
|
||||||
|
# See LICENCE-CMSD-1.0.md for terms.
|
||||||
|
|
||||||
|
name: License Headers
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
- name: Verify CMSD-1.0 headers
|
||||||
|
run: python3 scripts/license-headers.py --check
|
||||||
158
.gitea/workflows/publish-packages.yml
Normal file
158
.gitea/workflows/publish-packages.yml
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
# SecuBox — Publish packages to apt.secubox.in
|
||||||
|
# This workflow is triggered after build-packages.yml completes on a tag
|
||||||
|
name: Publish Packages
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
distribution:
|
||||||
|
description: 'Target distribution'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: 'bookworm'
|
||||||
|
secrets:
|
||||||
|
GPG_PRIVATE_KEY:
|
||||||
|
required: true
|
||||||
|
DEPLOY_SSH_KEY:
|
||||||
|
required: true
|
||||||
|
DEPLOY_KNOWN_HOSTS:
|
||||||
|
required: true
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["Build SecuBox .deb packages"]
|
||||||
|
types: [completed]
|
||||||
|
branches: [main, master]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
distribution:
|
||||||
|
description: 'Target distribution'
|
||||||
|
required: true
|
||||||
|
default: 'bookworm'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- bookworm
|
||||||
|
- trixie
|
||||||
|
run_id:
|
||||||
|
description: 'Build workflow run ID to publish (leave empty for latest)'
|
||||||
|
required: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
REPO_HOST: apt.secubox.in
|
||||||
|
REPO_USER: deploy
|
||||||
|
REPO_PATH: /var/www/apt.secubox.in
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Only run if triggered workflow succeeded and was a tag
|
||||||
|
if: |
|
||||||
|
(github.event.workflow_run.conclusion == 'success' &&
|
||||||
|
startsWith(github.event.workflow_run.head_branch, 'v')) ||
|
||||||
|
github.event_name == 'workflow_dispatch'
|
||||||
|
environment: production
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download artifacts from build workflow
|
||||||
|
uses: dawidd6/action-download-artifact@v3
|
||||||
|
with:
|
||||||
|
workflow: build-packages.yml
|
||||||
|
run_id: ${{ github.event.inputs.run_id || github.event.workflow_run.id }}
|
||||||
|
name: secubox-debs-all
|
||||||
|
path: debs/
|
||||||
|
|
||||||
|
- name: List downloaded packages
|
||||||
|
run: |
|
||||||
|
echo "Downloaded packages:"
|
||||||
|
ls -lh debs/*.deb 2>/dev/null | head -40 || echo "No .deb files found"
|
||||||
|
echo "Total: $(ls -1 debs/*.deb 2>/dev/null | wc -l) packages"
|
||||||
|
|
||||||
|
- name: Install reprepro
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y reprepro gnupg
|
||||||
|
|
||||||
|
- name: Import GPG key
|
||||||
|
env:
|
||||||
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
run: |
|
||||||
|
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||||
|
gpg --list-keys
|
||||||
|
|
||||||
|
- name: Setup repository structure
|
||||||
|
run: |
|
||||||
|
mkdir -p repo/conf
|
||||||
|
cp repo/conf/distributions repo/conf/
|
||||||
|
cat > repo/conf/options << EOF
|
||||||
|
verbose
|
||||||
|
basedir $(pwd)/repo
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Add packages to repository
|
||||||
|
env:
|
||||||
|
DIST: ${{ github.event.inputs.distribution || 'bookworm' }}
|
||||||
|
run: |
|
||||||
|
echo "Adding packages to $DIST..."
|
||||||
|
for deb in debs/*.deb; do
|
||||||
|
[ -f "$deb" ] || continue
|
||||||
|
echo " Adding: $(basename $deb)"
|
||||||
|
reprepro -b repo includedeb "$DIST" "$deb" || echo " Warning: Failed to add $(basename $deb)"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Export public GPG key
|
||||||
|
run: |
|
||||||
|
gpg --armor --export packages@secubox.in > repo/secubox-keyring.gpg
|
||||||
|
gpg --export packages@secubox.in > repo/secubox-keyring.gpg.bin
|
||||||
|
cp repo/secubox-keyring.gpg repo/dists/
|
||||||
|
|
||||||
|
- name: Generate install script
|
||||||
|
run: |
|
||||||
|
cat > repo/install.sh << 'INSTALL'
|
||||||
|
#!/bin/bash
|
||||||
|
# SecuBox APT Repository Installation Script
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Installing SecuBox APT repository..."
|
||||||
|
|
||||||
|
# Download and install GPG key
|
||||||
|
curl -fsSL https://apt.secubox.in/secubox-keyring.gpg | \
|
||||||
|
sudo tee /usr/share/keyrings/secubox.gpg > /dev/null
|
||||||
|
|
||||||
|
# Add repository
|
||||||
|
echo "deb [signed-by=/usr/share/keyrings/secubox.gpg] https://apt.secubox.in bookworm main" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/secubox.list
|
||||||
|
|
||||||
|
# Update package lists
|
||||||
|
sudo apt-get update
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "SecuBox repository installed successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "Install packages with:"
|
||||||
|
echo " sudo apt install secubox-full # All modules"
|
||||||
|
echo " sudo apt install secubox-lite # Essential modules only"
|
||||||
|
echo ""
|
||||||
|
INSTALL
|
||||||
|
chmod +x repo/install.sh
|
||||||
|
|
||||||
|
- name: Deploy to server
|
||||||
|
env:
|
||||||
|
SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||||
|
KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
||||||
|
chmod 600 ~/.ssh/id_ed25519
|
||||||
|
echo "$KNOWN_HOSTS" > ~/.ssh/known_hosts
|
||||||
|
|
||||||
|
echo "Deploying to ${REPO_HOST}..."
|
||||||
|
rsync -avz --delete \
|
||||||
|
repo/dists/ repo/pool/ repo/secubox-keyring.gpg repo/secubox-keyring.gpg.bin repo/install.sh \
|
||||||
|
${REPO_USER}@${REPO_HOST}:${REPO_PATH}/
|
||||||
|
|
||||||
|
- name: Verify deployment
|
||||||
|
run: |
|
||||||
|
echo "Verifying deployment..."
|
||||||
|
curl -sf "https://${REPO_HOST}/dists/bookworm/Release" | head -10
|
||||||
|
echo ""
|
||||||
|
echo "Repository published successfully!"
|
||||||
|
echo "Install with: curl -fsSL https://${REPO_HOST}/install.sh | sudo bash"
|
||||||
226
.gitea/workflows/release.yml
Normal file
226
.gitea/workflows/release.yml
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
# SecuBox — Complete Release Workflow
|
||||||
|
# Orchestrates package builds, image builds, and publishing
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: ['v*']
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Version tag (e.g., v1.0.0)'
|
||||||
|
required: true
|
||||||
|
build_images:
|
||||||
|
description: 'Build system images'
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
publish:
|
||||||
|
description: 'Publish to APT repo'
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Build all packages
|
||||||
|
build-packages:
|
||||||
|
uses: ./.github/workflows/build-packages.yml
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
# Build system images (optional).
|
||||||
|
# `if: always() && != cancelled` lets us still ship images even if a few
|
||||||
|
# matrix entries in build-packages failed (partial-release resilience).
|
||||||
|
build-images:
|
||||||
|
if: ${{ always() && needs.build-packages.result != 'cancelled' && github.event.inputs.build_images != 'false' }}
|
||||||
|
needs: build-packages
|
||||||
|
uses: ./.github/workflows/build-image.yml
|
||||||
|
with:
|
||||||
|
board: all
|
||||||
|
size: '8G'
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
# Build Live USB images (all platforms) — partial-release resilient.
|
||||||
|
build-live-usb:
|
||||||
|
if: ${{ always() && needs.build-packages.result != 'cancelled' }}
|
||||||
|
needs: build-packages
|
||||||
|
uses: ./.github/workflows/build-all-live-usb.yml
|
||||||
|
with:
|
||||||
|
platform: all
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
# Publish to APT repository — partial-release resilient.
|
||||||
|
publish:
|
||||||
|
if: ${{ always() && needs.build-packages.result != 'cancelled' && github.event.inputs.publish != 'false' }}
|
||||||
|
needs: build-packages
|
||||||
|
uses: ./.github/workflows/publish-packages.yml
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
# Create unified release. Ships whatever artifacts the upstream jobs
|
||||||
|
# managed to produce — handles partial successes gracefully (any
|
||||||
|
# `failure` here means "best-effort", `cancelled` we hard-skip).
|
||||||
|
create-release:
|
||||||
|
needs: [build-packages, build-images, build-live-usb]
|
||||||
|
if: ${{ always() && needs.build-packages.result != 'cancelled' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download package artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: secubox-debs-all
|
||||||
|
path: release/packages/
|
||||||
|
|
||||||
|
- name: Download image artifacts
|
||||||
|
if: needs.build-images.result == 'success'
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
pattern: secubox-*-bookworm
|
||||||
|
path: release/images/
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Download Live USB artifacts
|
||||||
|
if: needs.build-live-usb.result == 'success'
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
pattern: secubox-*-bookworm
|
||||||
|
path: release/live-usb/
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Generate release notes
|
||||||
|
run: |
|
||||||
|
VERSION="${{ github.ref_name || github.event.inputs.version }}"
|
||||||
|
|
||||||
|
cat > release/RELEASE_NOTES.md << EOF
|
||||||
|
# SecuBox-DEB ${VERSION}
|
||||||
|
|
||||||
|
## ⚡ CyberMind Security Platform
|
||||||
|
|
||||||
|
### What's Included
|
||||||
|
|
||||||
|
**124 Packages** across all security domains:
|
||||||
|
- **Core:** hub, core, portal, system, console
|
||||||
|
- **Security:** crowdsec, waf, auth, nac, threats, ipblock, mac-guard
|
||||||
|
- **Networking:** wireguard, haproxy, dpi, qos, netmodes, vhost, cdn
|
||||||
|
- **Applications:** mail, gitea, nextcloud, ollama, jellyfin, matrix
|
||||||
|
- **SOC:** soc-agent, soc-gateway, soc-web (hierarchical security operations)
|
||||||
|
- **Intel:** device-intel, vortex-dns, vortex-firewall, ai-insights
|
||||||
|
|
||||||
|
### System Images (Disk Install)
|
||||||
|
- MOCHAbin (Marvell Armada 7040) - arm64
|
||||||
|
- ESPRESSObin v7 (Marvell Armada 3720) - arm64
|
||||||
|
- ESPRESSObin Ultra (Marvell Armada 3720) - arm64
|
||||||
|
- Raspberry Pi 400 - arm64
|
||||||
|
- VM x64 (VirtualBox/QEMU) - amd64
|
||||||
|
- QEMU ARM64 emulation script (x86 hosts)
|
||||||
|
|
||||||
|
### Live USB Images (Bootable + Flasher)
|
||||||
|
- x64 Live USB (GRUB UEFI/BIOS) - amd64
|
||||||
|
- EspressoBin V7 Live USB (U-Boot) - arm64
|
||||||
|
- Raspberry Pi 400 Live USB - arm64
|
||||||
|
|
||||||
|
### Boot Options
|
||||||
|
- ⚡ **SecuBox Live** - Normal boot with persistence
|
||||||
|
- 🖼️ **Kiosk GUI** - Fullscreen browser (default)
|
||||||
|
- 📟 **Console TUI** - Text-based dashboard
|
||||||
|
- 🌉 **Bridge Mode** - Inline transparent bridge
|
||||||
|
- 💾 **Install to Disk** - Permanent installation
|
||||||
|
- 🚀 **To RAM** - Load entire system to memory
|
||||||
|
|
||||||
|
## Quick Install
|
||||||
|
|
||||||
|
### From APT Repository
|
||||||
|
\`\`\`bash
|
||||||
|
curl -fsSL https://apt.secubox.in/install.sh | sudo bash
|
||||||
|
sudo apt install secubox-full
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### From Live USB Image
|
||||||
|
\`\`\`bash
|
||||||
|
# Flash to USB
|
||||||
|
gunzip -c secubox-live-amd64-bookworm.img.gz | sudo dd of=/dev/sdX bs=4M status=progress
|
||||||
|
|
||||||
|
# VirtualBox VM
|
||||||
|
bash image/create-vbox-vm.sh --name SecuBox --memory 4096
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Default Credentials
|
||||||
|
- **Web UI:** https://<IP>:9443 (admin / secubox)
|
||||||
|
- **SSH:** root / secubox
|
||||||
|
|
||||||
|
## Checksums
|
||||||
|
|
||||||
|
All files are signed with GPG key \`packages@secubox.in\`.
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
gpg --verify SHA256SUMS.gpg
|
||||||
|
sha256sum -c SHA256SUMS
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [Wiki Home](https://github.com/CyberMind-FR/secubox-deb/wiki)
|
||||||
|
- [VirtualBox Quick Start](https://github.com/CyberMind-FR/secubox-deb/wiki/VirtualBox)
|
||||||
|
- [API Reference](https://github.com/CyberMind-FR/secubox-deb/wiki/API-Reference)
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Generate combined checksums
|
||||||
|
run: |
|
||||||
|
cd release/
|
||||||
|
find . -name "*.deb" -o -name "*.img.gz" -o -name "*.img.xz" -o -name "*.img" | \
|
||||||
|
xargs sha256sum 2>/dev/null | sort > SHA256SUMS || true
|
||||||
|
|
||||||
|
- name: Sign checksums
|
||||||
|
env:
|
||||||
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
run: |
|
||||||
|
if [ -n "$GPG_PRIVATE_KEY" ]; then
|
||||||
|
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||||
|
cd release/
|
||||||
|
gpg --clearsign SHA256SUMS
|
||||||
|
mv SHA256SUMS.asc SHA256SUMS.gpg
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Copy VM scripts to release
|
||||||
|
run: |
|
||||||
|
cp image/create-qemu-arm64-vm.sh release/
|
||||||
|
cp image/create-vbox-vm.sh release/
|
||||||
|
|
||||||
|
- name: Delete existing release assets (for retry)
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
VERSION="${{ github.ref_name || github.event.inputs.version }}"
|
||||||
|
# Check if release exists and delete conflicting assets
|
||||||
|
if gh release view "$VERSION" &>/dev/null; then
|
||||||
|
echo "Release exists, cleaning up for retry..."
|
||||||
|
# Get existing asset names
|
||||||
|
existing=$(gh release view "$VERSION" --json assets --jq '.assets[].name')
|
||||||
|
for asset in release/packages/*.deb release/images/*.img.gz release/images/*.img.xz release/live-usb/*.img*; do
|
||||||
|
[ -f "$asset" ] || continue
|
||||||
|
name=$(basename "$asset")
|
||||||
|
if echo "$existing" | grep -qF "$name"; then
|
||||||
|
echo "Deleting existing asset: $name"
|
||||||
|
gh release delete-asset "$VERSION" "$name" --yes 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
name: SecuBox-DEB ${{ github.ref_name || github.event.inputs.version }}
|
||||||
|
body_path: release/RELEASE_NOTES.md
|
||||||
|
files: |
|
||||||
|
release/packages/*.deb
|
||||||
|
release/images/*.img.gz
|
||||||
|
release/images/*.img.xz
|
||||||
|
release/live-usb/*.img*
|
||||||
|
release/create-qemu-arm64-vm.sh
|
||||||
|
release/create-vbox-vm.sh
|
||||||
|
release/SHA256SUMS
|
||||||
|
release/SHA256SUMS.gpg
|
||||||
|
draft: false
|
||||||
|
fail_on_unmatched_files: false
|
||||||
|
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }}
|
||||||
167
.gitea/workflows/sync-all.yml
Normal file
167
.gitea/workflows/sync-all.yml
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
name: Sync All — Build, Release, Metrics
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master, main]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
force_rebuild:
|
||||||
|
description: 'Force rebuild all packages'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
env:
|
||||||
|
SECUBOX_VERSION: "1.9.0"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# 1. Discover changes and compute metrics
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
discover:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
packages_changed: ${{ steps.changes.outputs.packages }}
|
||||||
|
total_packages: ${{ steps.metrics.outputs.total }}
|
||||||
|
total_endpoints: ${{ steps.metrics.outputs.endpoints }}
|
||||||
|
migration_percent: ${{ steps.metrics.outputs.migration }}
|
||||||
|
commit_count: ${{ steps.metrics.outputs.commits }}
|
||||||
|
issue_count: ${{ steps.metrics.outputs.issues }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Detect changed packages
|
||||||
|
id: changes
|
||||||
|
run: |
|
||||||
|
# Find packages changed in this push
|
||||||
|
if [ "${{ github.event_name }}" = "push" ]; then
|
||||||
|
CHANGED=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} \
|
||||||
|
| grep '^packages/secubox-' \
|
||||||
|
| cut -d'/' -f2 \
|
||||||
|
| sort -u \
|
||||||
|
| jq -R -s -c 'split("\n") | map(select(length > 0))')
|
||||||
|
else
|
||||||
|
CHANGED='[]'
|
||||||
|
fi
|
||||||
|
echo "packages=$CHANGED" >> $GITHUB_OUTPUT
|
||||||
|
echo "Changed packages: $CHANGED"
|
||||||
|
|
||||||
|
- name: Compute project metrics
|
||||||
|
id: metrics
|
||||||
|
run: |
|
||||||
|
# Count packages
|
||||||
|
TOTAL=$(find packages/secubox-* -maxdepth 0 -type d 2>/dev/null | wc -l)
|
||||||
|
echo "total=$TOTAL" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# Count API endpoints (rough estimate from main.py files)
|
||||||
|
ENDPOINTS=$(grep -r '@app\.\|@router\.' packages/*/api/*.py 2>/dev/null | wc -l || echo "2000")
|
||||||
|
echo "endpoints=$ENDPOINTS" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# Migration percent (packages with api/main.py)
|
||||||
|
WITH_API=$(find packages/secubox-*/api/main.py 2>/dev/null | wc -l)
|
||||||
|
PERCENT=$((WITH_API * 100 / TOTAL))
|
||||||
|
echo "migration=$PERCENT" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# Commit count
|
||||||
|
COMMITS=$(git rev-list --count HEAD)
|
||||||
|
echo "commits=$COMMITS" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# Open issues (via gh)
|
||||||
|
ISSUES=$(gh issue list --state open --limit 1000 --json number | jq length 2>/dev/null || echo "0")
|
||||||
|
echo "issues=$ISSUES" >> $GITHUB_OUTPUT
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# 2. Build changed packages (or all if forced)
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
build-packages:
|
||||||
|
needs: discover
|
||||||
|
if: needs.discover.outputs.packages_changed != '[]' || github.event.inputs.force_rebuild == 'true'
|
||||||
|
uses: ./.github/workflows/build-packages.yml
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# 3. Update WIP and metrics badges
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
update-metrics:
|
||||||
|
needs: [discover, build-packages]
|
||||||
|
if: always()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Update metrics in README
|
||||||
|
run: |
|
||||||
|
# Update package count badge
|
||||||
|
sed -i "s/Debian_Packages-[0-9]*-/Debian_Packages-${{ needs.discover.outputs.total_packages }}-/" README.md
|
||||||
|
|
||||||
|
# Update migration percent
|
||||||
|
sed -i "s/Migration-[0-9]*%25-/Migration-${{ needs.discover.outputs.migration_percent }}%25-/" README.md
|
||||||
|
|
||||||
|
# Update endpoints count
|
||||||
|
sed -i "s/API_Endpoints-[0-9]*+-/API_Endpoints-${{ needs.discover.outputs.total_endpoints }}+-/" README.md
|
||||||
|
|
||||||
|
- name: Update WIP with session info
|
||||||
|
run: |
|
||||||
|
DATE=$(date '+%Y-%m-%d')
|
||||||
|
SESSION=$(grep -oP 'Session \K[0-9]+' .claude/WIP.md | head -1 || echo "0")
|
||||||
|
NEW_SESSION=$((SESSION))
|
||||||
|
|
||||||
|
# Add CI sync entry if not already present today
|
||||||
|
if ! grep -q "CI Sync $DATE" .claude/WIP.md; then
|
||||||
|
echo "" >> .claude/WIP.md
|
||||||
|
echo "## CI Sync $DATE" >> .claude/WIP.md
|
||||||
|
echo "- Packages: ${{ needs.discover.outputs.total_packages }}" >> .claude/WIP.md
|
||||||
|
echo "- Endpoints: ${{ needs.discover.outputs.total_endpoints }}" >> .claude/WIP.md
|
||||||
|
echo "- Migration: ${{ needs.discover.outputs.migration_percent }}%" >> .claude/WIP.md
|
||||||
|
echo "- Commits: ${{ needs.discover.outputs.commit_count }}" >> .claude/WIP.md
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Commit metrics update
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add README.md .claude/WIP.md || true
|
||||||
|
git diff --staged --quiet || git commit -m "ci: Update metrics [skip ci]"
|
||||||
|
git push || true
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# 4. Create release on tags
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
release:
|
||||||
|
needs: [discover, build-packages]
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
uses: ./.github/workflows/release.yml
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# 5. Summary
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
summary:
|
||||||
|
needs: [discover, build-packages, update-metrics]
|
||||||
|
if: always()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Generate summary
|
||||||
|
run: |
|
||||||
|
echo "## SecuBox Sync Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Metric | Value |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Packages | ${{ needs.discover.outputs.total_packages }} |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| API Endpoints | ${{ needs.discover.outputs.total_endpoints }}+ |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Migration | ${{ needs.discover.outputs.migration_percent }}% |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Commits | ${{ needs.discover.outputs.commit_count }} |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Open Issues | ${{ needs.discover.outputs.issue_count }} |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "### Changed Packages" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '```json' >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '${{ needs.discover.outputs.packages_changed }}' >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
@ -3,13 +3,16 @@
|
||||||
# Git server in Alpine LXC for Debian
|
# Git server in Alpine LXC for Debian
|
||||||
# Three-fold architecture: Components, Status, Access
|
# Three-fold architecture: Components, Status, Access
|
||||||
|
|
||||||
VERSION="1.4.0"
|
VERSION="1.5.0"
|
||||||
CONFIG_FILE="/etc/secubox/gitea.toml"
|
CONFIG_FILE="/etc/secubox/gitea.toml"
|
||||||
LXC_PATH="/srv/lxc"
|
LXC_PATH="" # auto-detected below if blank, see resolve_lxc_path
|
||||||
DATA_PATH="/srv/gitea"
|
DATA_PATH="/srv/gitea"
|
||||||
CONTAINER="gitea"
|
CONTAINER="gitea"
|
||||||
GITEA_VERSION="1.22.6"
|
GITEA_VERSION="1.22.6"
|
||||||
|
|
||||||
|
# Gitea API hard floors
|
||||||
|
MIRROR_MIN_INTERVAL="10m0s"
|
||||||
|
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
YELLOW='\033[1;33m'
|
YELLOW='\033[1;33m'
|
||||||
|
|
@ -32,7 +35,22 @@ HTTP_PORT=$(config_get "http_port" "3000")
|
||||||
SSH_PORT=$(config_get "ssh_port" "2222")
|
SSH_PORT=$(config_get "ssh_port" "2222")
|
||||||
DOMAIN=$(config_get "domain" "git.local")
|
DOMAIN=$(config_get "domain" "git.local")
|
||||||
APP_NAME=$(config_get "app_name" "SecuBox Git")
|
APP_NAME=$(config_get "app_name" "SecuBox Git")
|
||||||
LXC_IP=$(config_get "lxc_ip" "192.168.255.40")
|
LXC_IP=$(config_get "lxc_ip" "10.100.0.40")
|
||||||
|
GITEA_APP_INI=$(config_get "app_ini" "/var/lib/gitea/custom/conf/app.ini")
|
||||||
|
ADMIN_USER=$(config_get "admin_user" "gandalf")
|
||||||
|
|
||||||
|
# Resolve LXC_PATH: explicit config wins, then fall back to the first
|
||||||
|
# directory containing a `<CONTAINER>/rootfs/` from the board conventions.
|
||||||
|
resolve_lxc_path() {
|
||||||
|
local cfg
|
||||||
|
cfg=$(config_get "lxc_path" "")
|
||||||
|
if [ -n "$cfg" ]; then echo "$cfg"; return; fi
|
||||||
|
for p in /data/lxc /srv/lxc /var/lib/lxc; do
|
||||||
|
[ -d "$p/$CONTAINER/rootfs" ] && { echo "$p"; return; }
|
||||||
|
done
|
||||||
|
echo "/data/lxc" # match modern board reality; install will create it
|
||||||
|
}
|
||||||
|
LXC_PATH=${LXC_PATH:-$(resolve_lxc_path)}
|
||||||
|
|
||||||
require_root() {
|
require_root() {
|
||||||
[ "$(id -u)" -eq 0 ] || { error "Root required"; exit 1; }
|
[ "$(id -u)" -eq 0 ] || { error "Root required"; exit 1; }
|
||||||
|
|
@ -332,48 +350,326 @@ user_list() {
|
||||||
# Mirror Management
|
# Mirror Management
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
mirror_add() {
|
# ============================================================================
|
||||||
local repo_url="$1"
|
# Repo / Mirror — the third routing verb (issue #176)
|
||||||
local local_name="$2"
|
# Forges `repo mirror add/remove/list/sync` parallel to mitmproxyctl route
|
||||||
local owner="${3:-admin}"
|
# (issue #173) and haproxyctl vhost. Wraps the 4 quirks of the Gitea
|
||||||
|
# pull-mirror API into one atomic operation:
|
||||||
|
# 1. No "convert existing repo to mirror" -> delete + migrate
|
||||||
|
# 2. Minimum interval = 10m (enforced server-side, 500 below)
|
||||||
|
# 3. Filesystem fetches don't trigger Gitea hooks -> DB stays "empty"
|
||||||
|
# 4. Token generation needs to run inside the LXC as the gitea user
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
if [ -z "$repo_url" ] || [ -z "$local_name" ]; then
|
# Generate a short-lived admin token via gitea CLI inside the LXC and echo
|
||||||
echo "Usage: giteactl mirror add <repo_url> <local_name> [owner]"
|
# it. Token name embeds the PID so concurrent calls don't collide.
|
||||||
return 1
|
gitea_token_gen() {
|
||||||
fi
|
require_root
|
||||||
|
if ! lxc_running; then error "Container not running"; return 1; fi
|
||||||
if ! lxc_running; then
|
local tname="giteactl-$$-$(date +%s)"
|
||||||
error "Container not running"
|
local out
|
||||||
return 1
|
out=$(lxc-attach -n "$CONTAINER" -P "$LXC_PATH" -- \
|
||||||
fi
|
su -s /bin/bash gitea -c \
|
||||||
|
"gitea --config $GITEA_APP_INI admin user generate-access-token \
|
||||||
log "Creating mirror: $local_name from $repo_url"
|
--username $ADMIN_USER --token-name $tname --scopes all 2>&1" \
|
||||||
# Use Gitea API for mirror creation
|
| tail -1)
|
||||||
local api_token
|
# output line ends with the actual token after the last ':'
|
||||||
api_token=$(get_admin_token)
|
local token
|
||||||
|
token=$(echo "$out" | awk -F: '{print $NF}' | tr -d ' ')
|
||||||
if [ -n "$api_token" ]; then
|
[ -z "$token" ] && { error "token gen failed: $out"; return 1; }
|
||||||
lxc_attach "curl -s -X POST \
|
echo "$token" "$tname"
|
||||||
-H 'Authorization: token $api_token' \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d '{
|
|
||||||
\"clone_addr\": \"$repo_url\",
|
|
||||||
\"repo_name\": \"$local_name\",
|
|
||||||
\"mirror\": true,
|
|
||||||
\"private\": false
|
|
||||||
}' \
|
|
||||||
http://localhost:${HTTP_PORT}/api/v1/repos/migrate"
|
|
||||||
else
|
|
||||||
warn "No admin token available. Create mirror manually via web interface."
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get_admin_token() {
|
gitea_token_revoke() {
|
||||||
# Get admin token from database
|
local tname="$1"
|
||||||
local db_file="$DATA_PATH/git/gitea.db"
|
[ -z "$tname" ] && return 0
|
||||||
if [ -f "$db_file" ]; then
|
lxc-attach -n "$CONTAINER" -P "$LXC_PATH" -- \
|
||||||
sqlite3 "$db_file" "SELECT token_hash FROM access_token WHERE name='secubox-api' LIMIT 1" 2>/dev/null
|
su -s /bin/bash gitea -c \
|
||||||
|
"gitea --config $GITEA_APP_INI admin user delete-access-token \
|
||||||
|
--username $ADMIN_USER --token-name $tname 2>&1" \
|
||||||
|
>/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run a curl against the in-LXC Gitea API. Caller passes method + path
|
||||||
|
# (without /api/v1 prefix) + body (or "" if none). Echo HTTP code on stderr,
|
||||||
|
# body on stdout.
|
||||||
|
gitea_api() {
|
||||||
|
local token="$1" method="$2" path="$3" body="$4"
|
||||||
|
local url="http://localhost:${HTTP_PORT}/api/v1${path}"
|
||||||
|
local args=("-s" "-X" "$method" "-H" "Authorization: token $token" "-H" "Content-Type: application/json")
|
||||||
|
[ -n "$body" ] && args+=("-d" "$body")
|
||||||
|
lxc-attach -n "$CONTAINER" -P "$LXC_PATH" -- \
|
||||||
|
curl "${args[@]}" -w "\nHTTP_CODE:%{http_code}\n" "$url"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Normalize an interval string. Gitea requires ≥10m; coerce silently to the
|
||||||
|
# floor so operators can ask for "5m" and get the closest legal value.
|
||||||
|
_normalize_interval() {
|
||||||
|
local i="$1"
|
||||||
|
[ -z "$i" ] && { echo "$MIRROR_MIN_INTERVAL"; return; }
|
||||||
|
# Already in the canonical "Nm0s" form
|
||||||
|
case "$i" in
|
||||||
|
*h*|*d*) echo "$i"; return ;;
|
||||||
|
*m*)
|
||||||
|
local n
|
||||||
|
n=$(echo "$i" | sed 's/[^0-9].*//')
|
||||||
|
if [ -n "$n" ] && [ "$n" -lt 10 ]; then
|
||||||
|
warn "interval ${i} below Gitea floor ${MIRROR_MIN_INTERVAL}; coerced"
|
||||||
|
echo "$MIRROR_MIN_INTERVAL"
|
||||||
|
else
|
||||||
|
# ensure "NmXs" canonical
|
||||||
|
case "$i" in *s) echo "$i" ;; *) echo "${i}0s" ;; esac
|
||||||
fi
|
fi
|
||||||
|
;;
|
||||||
|
*) echo "$i" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse "owner/name" or "name" (default owner = ADMIN_USER)
|
||||||
|
_parse_repo_ref() {
|
||||||
|
local ref="$1"
|
||||||
|
case "$ref" in
|
||||||
|
*/*) echo "${ref%/*}" "${ref#*/}" ;;
|
||||||
|
*) echo "$ADMIN_USER" "$ref" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_repo() {
|
||||||
|
local action="${1:-}"; shift || true
|
||||||
|
case "$action" in
|
||||||
|
create) cmd_repo_create "$@" ;;
|
||||||
|
delete|del) cmd_repo_delete "$@" ;;
|
||||||
|
mirror) cmd_repo_mirror "$@" ;;
|
||||||
|
list|ls) cmd_repo_list "$@" ;;
|
||||||
|
*)
|
||||||
|
cat <<EOF
|
||||||
|
Repo commands:
|
||||||
|
repo create OWNER/NAME [--private] [--default-branch BR]
|
||||||
|
repo delete OWNER/NAME [--force]
|
||||||
|
repo list [OWNER]
|
||||||
|
repo mirror add OWNER/NAME GITHUB_URL [--interval 10m] [--force]
|
||||||
|
(--force allows recreating an
|
||||||
|
existing non-mirror repo)
|
||||||
|
repo mirror remove OWNER/NAME (turns off mirror, repo stays)
|
||||||
|
repo mirror sync OWNER/NAME (trigger an immediate pull)
|
||||||
|
repo mirror list (all mirror repos, JSON)
|
||||||
|
EOF
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_repo_create() {
|
||||||
|
local ref="$1"; shift || true
|
||||||
|
[ -z "$ref" ] && { error "repo create: OWNER/NAME required"; return 1; }
|
||||||
|
local owner name; read -r owner name <<<"$(_parse_repo_ref "$ref")"
|
||||||
|
local private=false default_branch="master"
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--private) private=true ;;
|
||||||
|
--default-branch) default_branch="$2"; shift ;;
|
||||||
|
*) error "unknown flag: $1"; return 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
local tok tname; read -r tok tname <<<"$(gitea_token_gen)" || return 1
|
||||||
|
local body
|
||||||
|
body=$(printf '{"name":"%s","description":"Created by giteactl","private":%s,"default_branch":"%s"}' \
|
||||||
|
"$name" "$private" "$default_branch")
|
||||||
|
local out
|
||||||
|
out=$(gitea_api "$tok" POST "/admin/users/${owner}/repos" "$body")
|
||||||
|
gitea_token_revoke "$tname"
|
||||||
|
local code
|
||||||
|
code=$(echo "$out" | grep HTTP_CODE | cut -d: -f2)
|
||||||
|
case "$code" in
|
||||||
|
201) log "created ${owner}/${name}"; return 0 ;;
|
||||||
|
409) warn "${owner}/${name} already exists"; return 0 ;;
|
||||||
|
*) error "create failed (HTTP $code): $(echo "$out" | head -1)"; return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_repo_delete() {
|
||||||
|
local ref="$1"; shift || true
|
||||||
|
[ -z "$ref" ] && { error "repo delete: OWNER/NAME required"; return 1; }
|
||||||
|
local force=false
|
||||||
|
[ "${1:-}" = "--force" ] && force=true
|
||||||
|
local owner name; read -r owner name <<<"$(_parse_repo_ref "$ref")"
|
||||||
|
if ! $force; then
|
||||||
|
warn "repo delete is destructive — pass --force to proceed (will delete ${owner}/${name})"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local tok tname; read -r tok tname <<<"$(gitea_token_gen)" || return 1
|
||||||
|
local out
|
||||||
|
out=$(gitea_api "$tok" DELETE "/repos/${owner}/${name}" "")
|
||||||
|
gitea_token_revoke "$tname"
|
||||||
|
local code
|
||||||
|
code=$(echo "$out" | grep HTTP_CODE | cut -d: -f2)
|
||||||
|
case "$code" in
|
||||||
|
204) log "deleted ${owner}/${name}"; return 0 ;;
|
||||||
|
404) warn "${owner}/${name} did not exist"; return 0 ;;
|
||||||
|
*) error "delete failed (HTTP $code): $(echo "$out" | head -1)"; return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_repo_list() {
|
||||||
|
local owner="${1:-}"
|
||||||
|
local tok tname; read -r tok tname <<<"$(gitea_token_gen)" || return 1
|
||||||
|
local path
|
||||||
|
if [ -n "$owner" ]; then path="/users/${owner}/repos?limit=100"
|
||||||
|
else path="/repos/search?limit=100"; fi
|
||||||
|
local out
|
||||||
|
out=$(gitea_api "$tok" GET "$path" "")
|
||||||
|
gitea_token_revoke "$tname"
|
||||||
|
echo "$out" | sed '/^HTTP_CODE/d'
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_repo_mirror() {
|
||||||
|
local action="${1:-}"; shift || true
|
||||||
|
case "$action" in
|
||||||
|
add) cmd_repo_mirror_add "$@" ;;
|
||||||
|
remove|rm) cmd_repo_mirror_remove "$@" ;;
|
||||||
|
sync) cmd_repo_mirror_sync "$@" ;;
|
||||||
|
list|ls) cmd_repo_mirror_list "$@" ;;
|
||||||
|
*)
|
||||||
|
cat <<EOF
|
||||||
|
Mirror commands:
|
||||||
|
repo mirror add OWNER/NAME GITHUB_URL [--interval 10m] [--force]
|
||||||
|
repo mirror remove OWNER/NAME
|
||||||
|
repo mirror sync OWNER/NAME
|
||||||
|
repo mirror list
|
||||||
|
EOF
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_repo_mirror_add() {
|
||||||
|
local ref="$1" url="$2"; shift 2 || { error "repo mirror add: OWNER/NAME URL required"; return 1; }
|
||||||
|
local interval="$MIRROR_MIN_INTERVAL" force=false
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--interval) interval="$2"; shift ;;
|
||||||
|
--force) force=true ;;
|
||||||
|
*) error "unknown flag: $1"; return 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
interval=$(_normalize_interval "$interval")
|
||||||
|
local owner name; read -r owner name <<<"$(_parse_repo_ref "$ref")"
|
||||||
|
local tok tname; read -r tok tname <<<"$(gitea_token_gen)" || return 1
|
||||||
|
|
||||||
|
# 1. Probe existing state
|
||||||
|
local probe
|
||||||
|
probe=$(gitea_api "$tok" GET "/repos/${owner}/${name}" "")
|
||||||
|
local probe_code
|
||||||
|
probe_code=$(echo "$probe" | grep HTTP_CODE | cut -d: -f2)
|
||||||
|
local is_mirror=false
|
||||||
|
if [ "$probe_code" = "200" ]; then
|
||||||
|
echo "$probe" | grep -q '"mirror":true' && is_mirror=true
|
||||||
|
if $is_mirror; then
|
||||||
|
log "${owner}/${name} is already a mirror; updating interval to ${interval}"
|
||||||
|
local body
|
||||||
|
body=$(printf '{"mirror_interval":"%s"}' "$interval")
|
||||||
|
gitea_api "$tok" PATCH "/repos/${owner}/${name}" "$body" >/dev/null
|
||||||
|
cmd_repo_mirror_sync_inner "$tok" "$owner" "$name"
|
||||||
|
gitea_token_revoke "$tname"; return 0
|
||||||
|
else
|
||||||
|
if ! $force; then
|
||||||
|
error "${owner}/${name} exists and is NOT a mirror; pass --force to delete+recreate"
|
||||||
|
gitea_token_revoke "$tname"; return 1
|
||||||
|
fi
|
||||||
|
log "deleting existing non-mirror ${owner}/${name} (force)"
|
||||||
|
gitea_api "$tok" DELETE "/repos/${owner}/${name}" "" >/dev/null
|
||||||
|
sleep 2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Migrate as mirror
|
||||||
|
log "creating pull mirror ${owner}/${name} <- ${url} (interval ${interval})"
|
||||||
|
local body
|
||||||
|
body=$(printf '{"clone_addr":"%s","repo_name":"%s","repo_owner":"%s","mirror":true,"mirror_interval":"%s","service":"github","private":false,"description":"Mirror of %s"}' \
|
||||||
|
"$url" "$name" "$owner" "$interval" "$url")
|
||||||
|
local out
|
||||||
|
out=$(gitea_api "$tok" POST "/repos/migrate" "$body")
|
||||||
|
local code
|
||||||
|
code=$(echo "$out" | grep HTTP_CODE | cut -d: -f2)
|
||||||
|
case "$code" in
|
||||||
|
201) log "mirror created" ;;
|
||||||
|
500)
|
||||||
|
# Gitea returns 500 for "interval below minimum" — surface message
|
||||||
|
local msg
|
||||||
|
msg=$(echo "$out" | grep -o '"message":"[^"]*"' | head -1)
|
||||||
|
error "migrate failed (500): $msg"
|
||||||
|
gitea_token_revoke "$tname"; return 1
|
||||||
|
;;
|
||||||
|
*) error "migrate failed (HTTP $code): $(echo "$out" | head -3)"
|
||||||
|
gitea_token_revoke "$tname"; return 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# 3. Wait for initial clone to settle (avoid race with DB rescan)
|
||||||
|
sleep 5
|
||||||
|
cmd_repo_mirror_sync_inner "$tok" "$owner" "$name"
|
||||||
|
gitea_token_revoke "$tname"
|
||||||
|
log "OK — ${owner}/${name} mirrors ${url} every ${interval}"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_repo_mirror_remove() {
|
||||||
|
local ref="$1"
|
||||||
|
[ -z "$ref" ] && { error "repo mirror remove: OWNER/NAME required"; return 1; }
|
||||||
|
local owner name; read -r owner name <<<"$(_parse_repo_ref "$ref")"
|
||||||
|
local tok tname; read -r tok tname <<<"$(gitea_token_gen)" || return 1
|
||||||
|
# Setting mirror_interval="" disables auto-sync; repo stays as non-mirror.
|
||||||
|
# Per Gitea docs there is no true "demote-to-non-mirror" — the flag stays.
|
||||||
|
# Cleanest: just set interval to 0 which disables sync.
|
||||||
|
local out
|
||||||
|
out=$(gitea_api "$tok" PATCH "/repos/${owner}/${name}" '{"mirror_interval":"0s"}')
|
||||||
|
gitea_token_revoke "$tname"
|
||||||
|
local code
|
||||||
|
code=$(echo "$out" | grep HTTP_CODE | cut -d: -f2)
|
||||||
|
case "$code" in
|
||||||
|
200) log "${owner}/${name} mirror sync disabled (interval=0); repo content preserved" ;;
|
||||||
|
*) error "remove failed (HTTP $code): $(echo "$out" | head -1)"; return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_repo_mirror_sync_inner() {
|
||||||
|
local tok="$1" owner="$2" name="$3"
|
||||||
|
log "triggering mirror-sync on ${owner}/${name}"
|
||||||
|
local out
|
||||||
|
out=$(gitea_api "$tok" POST "/repos/${owner}/${name}/mirror-sync" "")
|
||||||
|
local code
|
||||||
|
code=$(echo "$out" | grep HTTP_CODE | cut -d: -f2)
|
||||||
|
case "$code" in
|
||||||
|
200) log "sync triggered" ;;
|
||||||
|
400) warn "sync rejected (HTTP 400) — repo may still be in initial clone state" ;;
|
||||||
|
*) warn "sync returned HTTP $code" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_repo_mirror_sync() {
|
||||||
|
local ref="$1"
|
||||||
|
[ -z "$ref" ] && { error "repo mirror sync: OWNER/NAME required"; return 1; }
|
||||||
|
local owner name; read -r owner name <<<"$(_parse_repo_ref "$ref")"
|
||||||
|
local tok tname; read -r tok tname <<<"$(gitea_token_gen)" || return 1
|
||||||
|
cmd_repo_mirror_sync_inner "$tok" "$owner" "$name"
|
||||||
|
gitea_token_revoke "$tname"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_repo_mirror_list() {
|
||||||
|
local tok tname; read -r tok tname <<<"$(gitea_token_gen)" || return 1
|
||||||
|
local out
|
||||||
|
out=$(gitea_api "$tok" GET "/repos/search?mirror=true&limit=100" "")
|
||||||
|
gitea_token_revoke "$tname"
|
||||||
|
echo "$out" | sed '/^HTTP_CODE/d' | python3 -c "
|
||||||
|
import sys, json
|
||||||
|
try:
|
||||||
|
d = json.load(sys.stdin)
|
||||||
|
for r in d.get('data', []):
|
||||||
|
print(f\" {r['full_name']:40s} <- {r.get('original_url','?')} (every {r.get('mirror_interval','?')})\")
|
||||||
|
if not d.get('data'):
|
||||||
|
print('(no mirror repos)')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'parse error: {e}', file=sys.stderr)
|
||||||
|
" 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
@ -788,6 +1084,15 @@ Users:
|
||||||
user passwd <n> <p> Change password
|
user passwd <n> <p> Change password
|
||||||
user list List users
|
user list List users
|
||||||
|
|
||||||
|
Repos & Mirrors (issue #176, parallel to mitmproxyctl route):
|
||||||
|
repo create OWNER/NAME [--private] [--default-branch BR]
|
||||||
|
repo delete OWNER/NAME --force
|
||||||
|
repo list [OWNER]
|
||||||
|
repo mirror add OWNER/NAME GITHUB_URL [--interval 10m] [--force]
|
||||||
|
repo mirror remove OWNER/NAME
|
||||||
|
repo mirror sync OWNER/NAME
|
||||||
|
repo mirror list
|
||||||
|
|
||||||
Backup:
|
Backup:
|
||||||
backup [name] Create backup
|
backup [name] Create backup
|
||||||
restore <file> Restore from backup
|
restore <file> Restore from backup
|
||||||
|
|
@ -823,6 +1128,7 @@ case "${1:-}" in
|
||||||
stop) shift; cmd_stop "$@" ;;
|
stop) shift; cmd_stop "$@" ;;
|
||||||
restart) shift; cmd_restart "$@" ;;
|
restart) shift; cmd_restart "$@" ;;
|
||||||
user) shift; cmd_user "$@" ;;
|
user) shift; cmd_user "$@" ;;
|
||||||
|
repo) shift; cmd_repo "$@" ;;
|
||||||
backup) shift; cmd_backup "$@" ;;
|
backup) shift; cmd_backup "$@" ;;
|
||||||
restore) shift; cmd_restore "$@" ;;
|
restore) shift; cmd_restore "$@" ;;
|
||||||
migrate) shift; cmd_migrate "$@" ;;
|
migrate) shift; cmd_migrate "$@" ;;
|
||||||
|
|
|
||||||
|
|
@ -178,16 +178,30 @@ check_prerequisites() {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Charger les modules nécessaires
|
# Charger les modules nécessaires. dwc2 must be loaded FIRST — it creates
|
||||||
|
# the UDC node that the gadget functions bind to. Historically dwc2 was
|
||||||
|
# loaded implicitly by secubox-eye-gadget.service's ExecStartPre; with
|
||||||
|
# that service disabled at boot (storage-only mode is opt-in), the
|
||||||
|
# gadget chain now owns its own dwc2 modprobe explicitly.
|
||||||
|
modprobe dwc2 2>/dev/null || true
|
||||||
modprobe libcomposite 2>/dev/null || true
|
modprobe libcomposite 2>/dev/null || true
|
||||||
modprobe usb_f_ecm 2>/dev/null || true
|
modprobe usb_f_ecm 2>/dev/null || true
|
||||||
modprobe usb_f_rndis 2>/dev/null || true
|
modprobe usb_f_rndis 2>/dev/null || true
|
||||||
modprobe usb_f_acm 2>/dev/null || true
|
modprobe usb_f_acm 2>/dev/null || true
|
||||||
modprobe usb_f_mass_storage 2>/dev/null || true
|
modprobe usb_f_mass_storage 2>/dev/null || true
|
||||||
|
|
||||||
|
# dwc2 binds asynchronously to the BCM USB controller — wait up to 5s
|
||||||
|
# for the UDC node to appear (typically <500ms on a Pi Zero W).
|
||||||
|
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||||
|
if [[ -d /sys/class/udc ]] && [[ -n "$(ls /sys/class/udc 2>/dev/null)" ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
|
||||||
# Vérifier la présence d'un UDC (USB Device Controller)
|
# Vérifier la présence d'un UDC (USB Device Controller)
|
||||||
if [[ ! -d /sys/class/udc ]] || [[ -z "$(ls /sys/class/udc 2>/dev/null)" ]]; then
|
if [[ ! -d /sys/class/udc ]] || [[ -z "$(ls /sys/class/udc 2>/dev/null)" ]]; then
|
||||||
err "Aucun UDC trouvé — ce script doit être exécuté sur un RPi Zero W"
|
err "Aucun UDC trouvé — vérifier dtoverlay=dwc2 dans /boot/config.txt et module dwc2 dans /etc/modules"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -650,9 +650,14 @@ mkdir -p "$ROOT_MNT/etc/systemd/system/dnsmasq.service.d"
|
||||||
cp "$SCRIPT_DIR/files/etc/systemd/system/dnsmasq.service.d/secubox-eye.conf" \
|
cp "$SCRIPT_DIR/files/etc/systemd/system/dnsmasq.service.d/secubox-eye.conf" \
|
||||||
"$ROOT_MNT/etc/systemd/system/dnsmasq.service.d/"
|
"$ROOT_MNT/etc/systemd/system/dnsmasq.service.d/"
|
||||||
|
|
||||||
# Enable new gadget service
|
# secubox-eye-gadget is STORAGE-only mode for U-Boot rescue. Enabling it
|
||||||
ln -sf /etc/systemd/system/secubox-eye-gadget.service \
|
# at boot alongside secubox-otg-gadget.service (composite ECM+ACM) caused
|
||||||
"$ROOT_MNT/etc/systemd/system/multi-user.target.wants/"
|
# UDC contention. Install the unit but DO NOT enable at boot.
|
||||||
|
# Manual U-Boot rescue mode:
|
||||||
|
# systemctl disable secubox-otg-gadget.service
|
||||||
|
# systemctl enable --now secubox-eye-gadget.service
|
||||||
|
# ln -sf /etc/systemd/system/secubox-eye-gadget.service \
|
||||||
|
# "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/"
|
||||||
|
|
||||||
# Copy framebuffer dashboard (Pi Zero W has no NEON, can't run Chromium)
|
# Copy framebuffer dashboard (Pi Zero W has no NEON, can't run Chromium)
|
||||||
log "Installing framebuffer dashboard..."
|
log "Installing framebuffer dashboard..."
|
||||||
|
|
@ -684,10 +689,15 @@ if [[ -f "$SCRIPT_DIR/secubox-eye-agent.service" && -f "$SCRIPT_DIR/config.toml.
|
||||||
# Install agent service
|
# Install agent service
|
||||||
cp "$SCRIPT_DIR/secubox-eye-agent.service" "$ROOT_MNT/etc/systemd/system/"
|
cp "$SCRIPT_DIR/secubox-eye-agent.service" "$ROOT_MNT/etc/systemd/system/"
|
||||||
|
|
||||||
# Enable agent service via symlink (atomic, no chroot needed)
|
# The agent depends on Pydantic v2 (pydantic_core, Rust) which has no
|
||||||
|
# ARMv6 wheel — pip ships an ARMv7 wheel that crashes with SIGILL on
|
||||||
|
# the Pi Zero W BCM2835 (status=4/ILL). v2.2.1 design moved metrics
|
||||||
|
# rendering to secubox-fallback-display.service (pure-Python Pillow),
|
||||||
|
# so we install the unit but DO NOT enable at boot. ARMv7+ boards:
|
||||||
|
# systemctl enable --now secubox-eye-agent.service
|
||||||
mkdir -p "$ROOT_MNT/etc/systemd/system/multi-user.target.wants"
|
mkdir -p "$ROOT_MNT/etc/systemd/system/multi-user.target.wants"
|
||||||
ln -sf /etc/systemd/system/secubox-eye-agent.service \
|
# ln -sf /etc/systemd/system/secubox-eye-agent.service \
|
||||||
"$ROOT_MNT/etc/systemd/system/multi-user.target.wants/"
|
# "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/"
|
||||||
|
|
||||||
# v2.2.0: Install menu system icons for radial menu
|
# v2.2.0: Install menu system icons for radial menu
|
||||||
if [[ -d "$SCRIPT_DIR/assets/icons" ]]; then
|
if [[ -d "$SCRIPT_DIR/assets/icons" ]]; then
|
||||||
|
|
@ -766,7 +776,10 @@ ln -sf /etc/systemd/system/eye-firstboot-hostname.service "$ROOT_MNT/etc/systemd
|
||||||
ln -sf /etc/systemd/system/hyperpixel2r-init.service "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true
|
ln -sf /etc/systemd/system/hyperpixel2r-init.service "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true
|
||||||
|
|
||||||
# Eye Remote services
|
# Eye Remote services
|
||||||
ln -sf /etc/systemd/system/secubox-eye-gadget.service "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true
|
# secubox-eye-gadget (storage-only, U-Boot rescue) is NOT enabled at boot
|
||||||
|
# to avoid UDC contention with secubox-otg-gadget.service. See comment at
|
||||||
|
# line 653 for the manual rescue-mode recipe.
|
||||||
|
# ln -sf /etc/systemd/system/secubox-eye-gadget.service "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true
|
||||||
# v2.2.1: Use fallback-display instead of eye-agent (3D cube + rainbow rings, stable)
|
# v2.2.1: Use fallback-display instead of eye-agent (3D cube + rainbow rings, stable)
|
||||||
ln -sf /etc/systemd/system/secubox-fallback-display.service "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true
|
ln -sf /etc/systemd/system/secubox-fallback-display.service "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true
|
||||||
# NOTE: secubox-eye-agent is broken (import errors) - disabled pending fix
|
# NOTE: secubox-eye-agent is broken (import errors) - disabled pending fix
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,19 @@ After=systemd-modules-load.service
|
||||||
Before=network-pre.target
|
Before=network-pre.target
|
||||||
Wants=network-pre.target
|
Wants=network-pre.target
|
||||||
|
|
||||||
# Conditions : seulement sur un périphérique avec UDC (RPi Zero W)
|
# Conditions : configfs disponible (UDC est crééable dynamiquement via dwc2,
|
||||||
ConditionPathIsDirectory=/sys/class/udc
|
# voir ExecStartPre — pas de pré-condition stricte sur /sys/class/udc).
|
||||||
ConditionPathExists=/sys/kernel/config
|
ConditionPathExists=/sys/kernel/config
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
RemainAfterExit=yes
|
RemainAfterExit=yes
|
||||||
|
|
||||||
# Chargement des modules nécessaires
|
# Chargement des modules nécessaires. dwc2 est chargé en premier pour créer
|
||||||
|
# le UDC (sinon le gadget ne peut s'attacher). secubox-eye-gadget.service
|
||||||
|
# le chargeait historiquement avant; depuis qu'il est désactivé au boot
|
||||||
|
# (storage-only mode opt-in), on doit le charger ici.
|
||||||
|
ExecStartPre=/sbin/modprobe dwc2
|
||||||
ExecStartPre=/sbin/modprobe libcomposite
|
ExecStartPre=/sbin/modprobe libcomposite
|
||||||
ExecStartPre=/sbin/modprobe usb_f_ecm
|
ExecStartPre=/sbin/modprobe usb_f_ecm
|
||||||
ExecStartPre=/sbin/modprobe usb_f_acm
|
ExecStartPre=/sbin/modprobe usb_f_acm
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,11 @@ WEBUI_PORT=9080
|
||||||
METABLOG_PORT=8900
|
METABLOG_PORT=8900
|
||||||
BACKEND_IP="10.100.0.1"
|
BACKEND_IP="10.100.0.1"
|
||||||
|
|
||||||
# Dead container IPs to fix (not running LXC containers)
|
# Dead container IPs to fix (not running LXC containers).
|
||||||
DEAD_CONTAINER_IPS="10.100.0.10 10.100.0.20 10.100.0.30 10.100.0.40"
|
# 10.100.0.10 was removed after the mail Phase 1 LXC went live there
|
||||||
|
# (mail.gk2 / webmail.gk2 / rspamd.gk2 / autoconfig.gk2 must keep routing
|
||||||
|
# to the live container, not be rewritten to 10.100.0.1:9080).
|
||||||
|
DEAD_CONTAINER_IPS="10.100.0.20 10.100.0.30 10.100.0.40"
|
||||||
|
|
||||||
log() {
|
log() {
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >&2
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >&2
|
||||||
|
|
@ -39,8 +42,14 @@ get_current_routes() {
|
||||||
|
|
||||||
# Get all domains from HAProxy that use mitmproxy_inspector
|
# Get all domains from HAProxy that use mitmproxy_inspector
|
||||||
get_haproxy_domains() {
|
get_haproxy_domains() {
|
||||||
|
# `use_backend mitmproxy_inspector if host_<dotted_domain>` lines end at
|
||||||
|
# the hostname (no trailing space, regex anchors on $). The previous
|
||||||
|
# `(?= )` lookahead required a space and silently matched zero domains —
|
||||||
|
# which is why sync never auto-populated routes for newly-added vhosts
|
||||||
|
# (caught 2026-05-17 when ckwa.gk2.secubox.in returned 502 because its
|
||||||
|
# route had never been written by sync).
|
||||||
grep "use_backend mitmproxy_inspector" "$HAPROXY_CFG" | \
|
grep "use_backend mitmproxy_inspector" "$HAPROXY_CFG" | \
|
||||||
grep -oP 'host_\K[a-z0-9_]+(?= )' | \
|
grep -oP 'host_\K[a-z0-9_]+(?=\s|$)' | \
|
||||||
sed 's/_/./g' | sort -u
|
sed 's/_/./g' | sort -u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user