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

View File

@ -12,7 +12,7 @@ members = [
]
[workspace.package]
version = "1.1.8"
version = "1.1.0"
edition = "2021"
authors = ["Echo <echo@moon-dragon.us>"]
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
ENV DEBIAN_FRONTEND=noninteractive
FROM rust:1.82-bookworm AS rust-builder
RUN apt-get update && apt-get install -y \
build-essential \
curl \
pkg-config \
libssl-dev \
libfontconfig1-dev \
&& 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
# Cache dependencies by building a dummy project first
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 \
crates/pm-agent-client/src crates/pm-auth/src crates/pm-ca/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
COPY crates/ crates/
COPY migrations/ migrations/
RUN cargo build --release
# 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
# ---------------------------------------------------------------------------
# Stage 2: Frontend build (Ubuntu 24.04 + Node.js 20)
# Stage 2: Frontend build
# ---------------------------------------------------------------------------
FROM ubuntu:24.04 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/*
FROM node:20-bookworm-slim AS frontend-builder
WORKDIR /usr/src/app/frontend
COPY frontend/package.json frontend/package-lock.json ./
@ -93,7 +64,6 @@ RUN apt-get update && apt-get install -y \
ca-certificates \
libssl3t64 \
libfontconfig1 \
openssl \
postgresql-client-16 \
argon2 \
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
* Release v1.1.0

2
debian/control vendored
View File

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

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

@ -107,27 +107,7 @@ setup_database() {
# Store password for config generation
echo "${db_password}" > /tmp/.pm-db-password-new
else
info "PostgreSQL user '${DB_USER}' already exists."
# 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
info "PostgreSQL user '${DB_USER}' already exists, skipping creation."
fi
# 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)
# 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
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
# Only update if the placeholder hash is still present
@ -269,56 +246,42 @@ generate_admin_password() {
}
# ---------------------------------------------------------------------------
# 7. Write config.toml with DB URL
# ---------------------------------------------------------------------------
# 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)
# 7. Write config.toml with DB URL (only if file doesn't exist)
# ---------------------------------------------------------------------------
write_config() {
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=""
if [[ -f /tmp/.pm-db-password-new ]]; then
db_password=$(cat /tmp/.pm-db-password-new)
fi
if [[ -f "${config_file}" ]]; then
# Check if the config still has the CHANGEME placeholder
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
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 "Writing configuration 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}"
# If we don't have a password (user already existed), generate a new one
# and update the PostgreSQL user so we can connect
if [[ -z "${db_password}" ]]; then
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
# Copy example config and set the DB URL
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}"
chown patch-manager:patch-manager "${config_file}"
chmod 640 "${config_file}"
info "Configuration written to ${config_file}"
}
# ---------------------------------------------------------------------------
# 8. Generate JWT keys (idempotent)
# Only generates if missing; regenerates verify.pem from signing.pem if lost.
# ---------------------------------------------------------------------------
generate_jwt_keys() {
if [[ ! -f "${CONFIG_DIR}/jwt/signing.pem" ]]; then
@ -329,14 +292,8 @@ generate_jwt_keys() {
chmod 600 "${CONFIG_DIR}/jwt/signing.pem"
chmod 644 "${CONFIG_DIR}/jwt/verify.pem"
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
info "JWT keys already exist, skipping."
info "JWT signing key already exists, skipping."
fi
}

View File

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

View File

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

View File

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