Private
Public Access
1
0

Compare commits

..

2 Commits

Author SHA1 Message Date
b4921d51a0 fix(docker): use ubuntu:24.04 runtime instead of debian:bookworm-slim
Some checks failed
CI Pipeline / Rust Format Check (pull_request) Successful in 6s
CI Pipeline / Clippy Lints (pull_request) Successful in 54s
CI Pipeline / Rust Unit Tests (pull_request) Failing after 1m20s
CI Pipeline / Security Audit (pull_request) Successful in 5s
CI Pipeline / Frontend Lint & Type Check (pull_request) Successful in 15s
CI Pipeline / Build .deb & Release (pull_request) Has been skipped
The project targets Ubuntu 24.04, not Debian Bookworm. Ubuntu 24.04
includes PostgreSQL 16 in default repos, eliminating the need for the
PGDG APT repo workaround. Also fixes libssl3 → libssl3t64 package name
for the time64 transition in Ubuntu 24.04.
2026-06-07 17:34:05 -05:00
455013db8e fix(docker): add PostgreSQL APT repo for postgresql-client-16
Some checks failed
CI Pipeline / Rust Format Check (pull_request) Successful in 4s
CI Pipeline / Clippy Lints (pull_request) Successful in 52s
CI Pipeline / Rust Unit Tests (pull_request) Failing after 1m21s
CI Pipeline / Security Audit (pull_request) Successful in 5s
CI Pipeline / Frontend Lint & Type Check (pull_request) Successful in 14s
CI Pipeline / Build .deb & Release (pull_request) Has been skipped
Debian Bookworm default repos only ship PostgreSQL 15. The Docker
runtime stage needs postgresql-client-16 for the entrypoint script,
so add the official PGDG APT repository.

- Add PGDG GPG key and sources.list entry for bookworm-pgdg
- Install ca-certificates and curl first (needed for repo setup)
- Purge gnupg2 after use to keep image lean
- Verify argon2 package name is correct for Bookworm (it is)
2026-06-07 17:21:37 -05:00
9 changed files with 48 additions and 165 deletions

View File

@ -9,7 +9,6 @@ on:
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions: permissions:
contents: write contents: write
@ -20,7 +19,7 @@ jobs:
name: Rust Format name: Rust Format
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
components: rustfmt components: rustfmt
@ -31,7 +30,7 @@ jobs:
name: Clippy name: Clippy
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
components: clippy components: clippy
@ -44,7 +43,7 @@ jobs:
name: Rust Tests name: Rust Tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- name: Install system dependencies - name: Install system dependencies
@ -55,7 +54,7 @@ jobs:
name: Security Audit name: Security Audit
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- run: cargo install cargo-audit && cargo audit - run: cargo install cargo-audit && cargo audit
@ -63,11 +62,11 @@ jobs:
name: Secret scanning name: Secret scanning
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Gitleaks - name: Gitleaks
uses: gitleaks/gitleaks-action@v3 uses: gitleaks/gitleaks-action@v2
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -75,9 +74,9 @@ jobs:
name: Frontend Lint name: Frontend Lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v5 uses: actions/setup-node@v4
with: with:
node-version: '20' node-version: '20'
- name: Install & Lint - name: Install & Lint
@ -88,7 +87,7 @@ jobs:
needs: [rust-format, clippy, rust-test, security-audit, frontend-lint] needs: [rust-format, clippy, rust-test, security-audit, frontend-lint]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Free disk space - name: Free disk space
@ -104,7 +103,7 @@ jobs:
- name: Strip binaries - name: Strip binaries
run: strip target/release/pm-web target/release/pm-worker run: strip target/release/pm-web target/release/pm-worker
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v5 uses: actions/setup-node@v4
with: with:
node-version: '20' node-version: '20'
- name: Build frontend - name: Build frontend
@ -126,7 +125,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT
- name: Upload to GitHub Release - name: Upload to GitHub Release
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v3 uses: softprops/action-gh-release@v2
with: with:
body: ${{ steps.release_notes.outputs.notes }} body: ${{ steps.release_notes.outputs.notes }}
files: linux-patch-manager_*.deb files: linux-patch-manager_*.deb
@ -136,18 +135,17 @@ jobs:
needs: [rust-format, clippy, rust-test, security-audit, frontend-lint] needs: [rust-format, clippy, rust-test, security-audit, frontend-lint]
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 60
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4 uses: docker/setup-buildx-action@v3
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v4 uses: docker/setup-qemu-action@v3
- name: Log in to GitHub Container Registry - name: Log in to GitHub Container Registry
uses: docker/login-action@v4 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@ -155,7 +153,7 @@ jobs:
- name: Extract Docker metadata - name: Extract Docker metadata
id: meta id: meta
uses: docker/metadata-action@v6 uses: docker/metadata-action@v5
with: with:
images: ghcr.io/draco-lunaris/linux-patch-manager images: ghcr.io/draco-lunaris/linux-patch-manager
tags: | tags: |
@ -165,10 +163,10 @@ jobs:
type=sha type=sha
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v7 uses: docker/build-push-action@v6
with: with:
context: . context: .
platforms: linux/amd64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}

View File

@ -12,7 +12,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "1.1.8" version = "1.1.0"
edition = "2021" edition = "2021"
authors = ["Echo <echo@moon-dragon.us>"] authors = ["Echo <echo@moon-dragon.us>"]
license = "MIT" license = "MIT"

View File

@ -6,36 +6,20 @@
# ============================================================================= # =============================================================================
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Stage 1: Rust build (Ubuntu 24.04 + rustup) # Stage 1: Rust build
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
FROM ubuntu:24.04 AS rust-builder FROM rust:1.82-bookworm AS rust-builder
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
build-essential \
curl \
pkg-config \ pkg-config \
libssl-dev \ libssl-dev \
libfontconfig1-dev \ libfontconfig1-dev \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Install Rust via rustup (stable channel, provides 1.85+)
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
ENV PATH="/root/.cargo/bin:${PATH}"
WORKDIR /usr/src/app WORKDIR /usr/src/app
# Cache dependencies by building a dummy project first # Cache dependencies by building a dummy project first
COPY Cargo.toml Cargo.lock ./ COPY Cargo.toml Cargo.lock ./
COPY crates/pm-web/Cargo.toml crates/pm-web/Cargo.toml
COPY crates/pm-worker/Cargo.toml crates/pm-worker/Cargo.toml
COPY crates/pm-core/Cargo.toml crates/pm-core/Cargo.toml
COPY crates/pm-agent-client/Cargo.toml crates/pm-agent-client/Cargo.toml
COPY crates/pm-auth/Cargo.toml crates/pm-auth/Cargo.toml
COPY crates/pm-ca/Cargo.toml crates/pm-ca/Cargo.toml
COPY crates/pm-reports/Cargo.toml crates/pm-reports/Cargo.toml
COPY crates/migrate-secrets/Cargo.toml crates/migrate-secrets/Cargo.toml
RUN mkdir -p crates/pm-web/src crates/pm-worker/src crates/pm-core/src \ RUN mkdir -p crates/pm-web/src crates/pm-worker/src crates/pm-core/src \
crates/pm-agent-client/src crates/pm-auth/src crates/pm-ca/src \ crates/pm-agent-client/src crates/pm-auth/src crates/pm-ca/src \
crates/pm-reports/src crates/migrate-secrets/src crates/pm-reports/src crates/migrate-secrets/src
@ -51,7 +35,6 @@ RUN cargo build --release 2>/dev/null || true
# Now build the real project # Now build the real project
COPY crates/ crates/ COPY crates/ crates/
COPY migrations/ migrations/
RUN cargo build --release RUN cargo build --release
# Verify binaries exist # Verify binaries exist
@ -61,21 +44,9 @@ RUN ls -la target/release/pm-web target/release/pm-worker
RUN strip target/release/pm-web target/release/pm-worker RUN strip target/release/pm-web target/release/pm-worker
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Stage 2: Frontend build (Ubuntu 24.04 + Node.js 20) # Stage 2: Frontend build
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
FROM ubuntu:24.04 AS frontend-builder FROM node:20-bookworm-slim AS frontend-builder
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Install Node.js 20 via NodeSource
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /usr/src/app/frontend WORKDIR /usr/src/app/frontend
COPY frontend/package.json frontend/package-lock.json ./ COPY frontend/package.json frontend/package-lock.json ./
@ -93,7 +64,6 @@ RUN apt-get update && apt-get install -y \
ca-certificates \ ca-certificates \
libssl3t64 \ libssl3t64 \
libfontconfig1 \ libfontconfig1 \
openssl \
postgresql-client-16 \ postgresql-client-16 \
argon2 \ argon2 \
curl \ curl \

42
debian/changelog vendored
View File

@ -1,45 +1,3 @@
linux-patch-manager (1.1.8-1) unstable; urgency=low
* Release v1.1.8
-- git-echo <git-echo@moon-dragon.us> Tue, 09 Jun 2026 11:47:58 -0500
linux-patch-manager (1.1.7-1) unstable; urgency=low
* Release v1.1.7
-- git-echo <git-echo@moon-dragon.us> Tue, 09 Jun 2026 09:11:11 -0500
linux-patch-manager (1.1.6-1) unstable; urgency=low
* Release v1.1.6
-- git-echo <git-echo@moon-dragon.us> Tue, 09 Jun 2026 08:10:52 -0500
linux-patch-manager (1.1.5-1) unstable; urgency=low
* Release v1.1.5
-- git-echo <git-echo@moon-dragon.us> Mon, 08 Jun 2026 20:15:50 -0500
linux-patch-manager (1.1.4-1) unstable; urgency=low
* Release v1.1.4
-- git-echo <git-echo@moon-dragon.us> Mon, 08 Jun 2026 17:30:35 -0500
linux-patch-manager (1.1.2-1) unstable; urgency=low
* Release v1.1.2
-- git-echo <git-echo@moon-dragon.us> Sun, 07 Jun 2026 21:19:18 -0500
linux-patch-manager (1.1.1-1) unstable; urgency=low
* Release v1.1.1
-- git-echo <git-echo@moon-dragon.us> Sun, 07 Jun 2026 18:55:59 -0500
linux-patch-manager (1.1.0-1) unstable; urgency=low linux-patch-manager (1.1.0-1) unstable; urgency=low
* Release v1.1.0 * Release v1.1.0

2
debian/control vendored
View File

@ -1,5 +1,5 @@
Package: linux-patch-manager Package: linux-patch-manager
Version: 1.1.8-1 Version: 1.1.0-1
Architecture: amd64 Architecture: amd64
Maintainer: Moon Dragon <echo@moon-dragon.us> Maintainer: Moon Dragon <echo@moon-dragon.us>
Installed-Size: 45000 Installed-Size: 45000

75
debian/postinst vendored Normal file → Executable file
View File

@ -107,27 +107,7 @@ setup_database() {
# Store password for config generation # Store password for config generation
echo "${db_password}" > /tmp/.pm-db-password-new echo "${db_password}" > /tmp/.pm-db-password-new
else else
info "PostgreSQL user '${DB_USER}' already exists." info "PostgreSQL user '${DB_USER}' already exists, skipping creation."
# Recover the DB password: try from existing config, or generate new.
local config_file="${CONFIG_DIR}/config.toml"
local existing_pw=""
if [[ -f "${config_file}" ]]; then
# Extract password from URL: postgres://user:PASSWORD@host/db
# Use @localhost anchor so passwords containing @ are extracted correctly.
existing_pw=$(sed -n 's|^url = "postgres://[^:]*:\(.*\)@localhost.*"|\1|p' "${config_file}" | head -1)
fi
if [[ -n "${existing_pw}" && "${existing_pw}" != "CHANGEME" ]]; then
# Config has a real password — sync it to PostgreSQL so the app can connect.
psql_run -c "ALTER ROLE ${DB_USER} WITH PASSWORD '${existing_pw}';" 2>/dev/null || true
echo "${existing_pw}" > /tmp/.pm-db-password-new
info "Synced DB password from existing config to PostgreSQL."
else
# No config or CHANGEME — generate a fresh password and update PostgreSQL.
db_password=$(openssl rand -base64 32 | tr -dc 'A-Za-z0-9' | head -c 32)
psql_run -c "ALTER ROLE ${DB_USER} WITH PASSWORD '${db_password}';" 2>/dev/null || true
echo "${db_password}" > /tmp/.pm-db-password-new
info "Generated new DB password for existing user."
fi
fi fi
# Create database if not exists # Create database if not exists
@ -227,11 +207,8 @@ generate_admin_password() {
admin_password=$(openssl rand -base64 32 | tr -dc 'A-Za-z0-9!@#%^&*' | head -c 24) admin_password=$(openssl rand -base64 32 | tr -dc 'A-Za-z0-9!@#%^&*' | head -c 24)
# Hash with argon2 (PHC format, compatible with the application) # Hash with argon2 (PHC format, compatible with the application)
# Generate a random 16-character salt (argon2 requires minimum 8 characters)
local admin_salt
admin_salt=$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 16)
local password_hash local password_hash
password_hash=$(echo -n "${admin_password}" | argon2 "${admin_salt}" -id -t 3 -m 16 -p 1 -l 32 -e) password_hash=$(echo -n "${admin_password}" | argon2 salt -id -t 3 -m 65536 -p 1 -l 32 -e)
# Update admin user password in database # Update admin user password in database
# Only update if the placeholder hash is still present # Only update if the placeholder hash is still present
@ -269,56 +246,42 @@ generate_admin_password() {
} }
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# 7. Write config.toml with DB URL # 7. Write config.toml with DB URL (only if file doesn't exist)
# ---------------------------------------------------------------------------
# Handles three scenarios:
# 1. No config file → create from example with real DB password
# 2. Config exists with CHANGEME → replace CHANGEME with real DB password
# 3. Config exists with real password → leave it alone (upgrade)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
write_config() { write_config() {
local config_file="${CONFIG_DIR}/config.toml" local config_file="${CONFIG_DIR}/config.toml"
# Resolve the DB password to use: from setup_database() or generate fresh. if [[ -f "${config_file}" ]]; then
info "Config file ${config_file} already exists, not overwriting."
return 0
fi
info "Writing configuration file..."
# Get the DB password — use the one we just generated if we created the user
local db_password="" local db_password=""
if [[ -f /tmp/.pm-db-password-new ]]; then if [[ -f /tmp/.pm-db-password-new ]]; then
db_password=$(cat /tmp/.pm-db-password-new) db_password=$(cat /tmp/.pm-db-password-new)
fi fi
if [[ -f "${config_file}" ]]; then # If we don't have a password (user already existed), generate a new one
# Check if the config still has the CHANGEME placeholder # and update the PostgreSQL user so we can connect
if grep -q 'CHANGEME' "${config_file}"; then
if [[ -z "${db_password}" ]]; then
# No password from setup_database() — generate a fresh one
db_password=$(openssl rand -base64 32 | tr -dc 'A-Za-z0-9' | head -c 32)
psql_run -c "ALTER ROLE ${DB_USER} WITH PASSWORD '${db_password}';" 2>/dev/null || true
fi
info "Replacing CHANGEME placeholder in existing config with real DB password."
sed -i "s|postgres://patch_manager:CHANGEME@localhost/patch_manager|postgres://${DB_USER}:${db_password}@localhost/${DB_NAME}|" "${config_file}"
else
info "Config file ${config_file} already exists with a real password, leaving it unchanged."
return 0
fi
else
# No config file — create from example
if [[ -z "${db_password}" ]]; then if [[ -z "${db_password}" ]]; then
db_password=$(openssl rand -base64 32 | tr -dc 'A-Za-z0-9' | head -c 32) db_password=$(openssl rand -base64 32 | tr -dc 'A-Za-z0-9' | head -c 32)
psql_run -c "ALTER ROLE ${DB_USER} WITH PASSWORD '${db_password}';" 2>/dev/null || true psql_run -c "ALTER ROLE ${DB_USER} WITH PASSWORD '${db_password}';" 2>/dev/null || true
fi fi
info "Writing configuration file..." # Copy example config and set the DB URL
cp /usr/share/patch-manager/config.example.toml "${config_file}" cp /usr/share/patch-manager/config.example.toml "${config_file}"
sed -i "s|postgres://patch_manager:CHANGEME@localhost/patch_manager|postgres://${DB_USER}:${db_password}@localhost/${DB_NAME}|" "${config_file}" sed -i "s|postgres://patch_manager:CHANGEME@localhost/patch_manager|postgres://${DB_USER}:${db_password}@localhost/${DB_NAME}|" "${config_file}"
fi
chown patch-manager:patch-manager "${config_file}" chown patch-manager:patch-manager "${config_file}"
chmod 640 "${config_file}" chmod 640 "${config_file}"
info "Configuration written to ${config_file}" info "Configuration written to ${config_file}"
} }
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# 8. Generate JWT keys (idempotent) # 8. Generate JWT keys (idempotent)
# Only generates if missing; regenerates verify.pem from signing.pem if lost.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
generate_jwt_keys() { generate_jwt_keys() {
if [[ ! -f "${CONFIG_DIR}/jwt/signing.pem" ]]; then if [[ ! -f "${CONFIG_DIR}/jwt/signing.pem" ]]; then
@ -329,14 +292,8 @@ generate_jwt_keys() {
chmod 600 "${CONFIG_DIR}/jwt/signing.pem" chmod 600 "${CONFIG_DIR}/jwt/signing.pem"
chmod 644 "${CONFIG_DIR}/jwt/verify.pem" chmod 644 "${CONFIG_DIR}/jwt/verify.pem"
info "JWT keys generated." info "JWT keys generated."
elif [[ ! -f "${CONFIG_DIR}/jwt/verify.pem" ]]; then
info "Regenerating missing JWT verification key from existing signing key..."
openssl pkey -in "${CONFIG_DIR}/jwt/signing.pem" -pubout -out "${CONFIG_DIR}/jwt/verify.pem" 2>/dev/null
chown patch-manager:patch-manager "${CONFIG_DIR}/jwt/verify.pem"
chmod 644 "${CONFIG_DIR}/jwt/verify.pem"
info "JWT verification key regenerated."
else else
info "JWT keys already exist, skipping." info "JWT signing key already exists, skipping."
fi fi
} }

View File

@ -8,7 +8,7 @@
services: services:
db: db:
image: postgres:16 image: postgres:16-bookworm
restart: unless-stopped restart: unless-stopped
environment: environment:
POSTGRES_USER: patch_manager POSTGRES_USER: patch_manager

View File

@ -1,7 +1,7 @@
{ {
"name": "patch-manager-ui", "name": "patch-manager-ui",
"private": true, "private": true,
"version": "1.1.8", "version": "1.1.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@ -22,7 +22,7 @@ warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; } error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
VERSION="1.1.8" VERSION="1.1.0"
RELEASE="1" RELEASE="1"
PKG_NAME="linux-patch-manager" PKG_NAME="linux-patch-manager"
DEB_NAME="${PKG_NAME}_${VERSION}-${RELEASE}_amd64.deb" DEB_NAME="${PKG_NAME}_${VERSION}-${RELEASE}_amd64.deb"