Compare commits
2 Commits
release/v1
...
release/v1
| Author | SHA1 | Date | |
|---|---|---|---|
| d6f0c23dd8 | |||
| ae15404496 |
@ -1,40 +0,0 @@
|
||||
# Build artifacts
|
||||
target/
|
||||
*.deb
|
||||
package-build/
|
||||
|
||||
# Frontend build output (rebuilt in Docker)
|
||||
frontend/dist/
|
||||
frontend/node_modules/
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Docker
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
.dockerignore
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.*
|
||||
|
||||
# Documentation
|
||||
docs/
|
||||
|
||||
# Agent Zero project data
|
||||
.a0proj/
|
||||
|
||||
# Python
|
||||
venv/
|
||||
__pycache__/
|
||||
|
||||
# Misc
|
||||
*.md
|
||||
!README.md
|
||||
LICENSE
|
||||
@ -1,8 +0,0 @@
|
||||
# Linux Patch Manager — Docker Environment Variables
|
||||
# Copy this file to .env and edit the values before running docker compose up.
|
||||
|
||||
# Required: PostgreSQL password for the patch_manager user
|
||||
DB_PASSWORD=changeme-to-a-strong-password
|
||||
|
||||
# Optional: Docker image tag (defaults to 'latest' if not set)
|
||||
TAG=latest
|
||||
45
.github/workflows/ci.yml
vendored
45
.github/workflows/ci.yml
vendored
@ -12,7 +12,6 @@ env:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
rust-format:
|
||||
@ -129,47 +128,3 @@ jobs:
|
||||
with:
|
||||
body: ${{ steps.release_notes.outputs.notes }}
|
||||
files: linux-patch-manager_*.deb
|
||||
|
||||
docker:
|
||||
name: Docker Build & Push
|
||||
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@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/draco-lunaris/linux-patch-manager
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -28,9 +28,6 @@ frontend/dist
|
||||
*.deb
|
||||
package-build/
|
||||
|
||||
# Docker environment
|
||||
.env
|
||||
|
||||
# Private key material - NEVER commit
|
||||
*.key
|
||||
*.key.pem
|
||||
|
||||
@ -12,7 +12,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.1.5"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
authors = ["Echo <echo@moon-dragon.us>"]
|
||||
license = "MIT"
|
||||
|
||||
141
Dockerfile
141
Dockerfile
@ -1,141 +0,0 @@
|
||||
# =============================================================================
|
||||
# Linux Patch Manager — Multi-stage Docker Build
|
||||
# =============================================================================
|
||||
# Build: docker build -t linux-patch-manager .
|
||||
# Run: docker compose up
|
||||
# =============================================================================
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 1: Rust build (Ubuntu 24.04 + rustup)
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM ubuntu:24.04 AS rust-builder
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
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
|
||||
RUN echo 'fn main(){}' > crates/pm-web/src/main.rs \
|
||||
&& echo 'fn main(){}' > crates/pm-worker/src/main.rs \
|
||||
&& echo '' > crates/pm-core/src/lib.rs \
|
||||
&& echo '' > crates/pm-agent-client/src/lib.rs \
|
||||
&& echo '' > crates/pm-auth/src/lib.rs \
|
||||
&& echo '' > crates/pm-ca/src/lib.rs \
|
||||
&& echo '' > crates/pm-reports/src/lib.rs \
|
||||
&& echo 'fn main(){}' > crates/migrate-secrets/src/main.rs
|
||||
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
|
||||
RUN ls -la target/release/pm-web target/release/pm-worker
|
||||
|
||||
# Strip debug symbols
|
||||
RUN strip target/release/pm-web target/release/pm-worker
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 2: Frontend build (Ubuntu 24.04 + Node.js 20)
|
||||
# ---------------------------------------------------------------------------
|
||||
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/*
|
||||
|
||||
WORKDIR /usr/src/app/frontend
|
||||
COPY frontend/package.json frontend/package-lock.json ./
|
||||
RUN npm ci --production=false
|
||||
|
||||
COPY frontend/ ./
|
||||
RUN npm run build
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 3: Runtime
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM ubuntu:24.04 AS runtime
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ca-certificates \
|
||||
libssl3t64 \
|
||||
libfontconfig1 \
|
||||
openssl \
|
||||
postgresql-client-16 \
|
||||
argon2 \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create service user
|
||||
RUN useradd --system --no-create-home --shell /usr/sbin/nologin \
|
||||
--comment "Linux Patch Manager service account" patch-manager
|
||||
|
||||
# Create directories
|
||||
RUN mkdir -p /etc/patch-manager/ca /etc/patch-manager/certs \
|
||||
/etc/patch-manager/jwt /etc/patch-manager/tls \
|
||||
/var/log/patch-manager /opt/patch-manager \
|
||||
/usr/share/patch-manager/frontend \
|
||||
/usr/share/patch-manager/migrations
|
||||
|
||||
# Copy binaries
|
||||
COPY --from=rust-builder /usr/src/app/target/release/pm-web /usr/local/bin/pm-web
|
||||
COPY --from=rust-builder /usr/src/app/target/release/pm-worker /usr/local/bin/pm-worker
|
||||
|
||||
# Copy frontend
|
||||
COPY --from=frontend-builder /usr/src/app/frontend/dist/ /usr/share/patch-manager/frontend/
|
||||
|
||||
# Copy migrations
|
||||
COPY migrations/ /usr/share/patch-manager/migrations/
|
||||
|
||||
# Copy entrypoint
|
||||
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
RUN chmod 755 /usr/local/bin/entrypoint.sh
|
||||
|
||||
# Copy config template
|
||||
COPY config/config.example.toml /usr/share/patch-manager/config.example.toml
|
||||
|
||||
# Set ownership
|
||||
RUN chown -R patch-manager:patch-manager \
|
||||
/etc/patch-manager /var/log/patch-manager \
|
||||
/opt/patch-manager /usr/share/patch-manager
|
||||
|
||||
# Expose HTTPS port
|
||||
EXPOSE 443
|
||||
|
||||
# Volume for persistent data
|
||||
VOLUME ["/etc/patch-manager", "/var/log/patch-manager", "/opt/patch-manager"]
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
30
debian/changelog
vendored
30
debian/changelog
vendored
@ -1,33 +1,3 @@
|
||||
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
|
||||
|
||||
-- git-echo <git-echo@moon-dragon.us> Sun, 07 Jun 2026 16:47:03 -0500
|
||||
|
||||
linux-patch-manager (1.0.0-1) unstable; urgency=low
|
||||
|
||||
* Release v1.0.0
|
||||
|
||||
5
debian/control
vendored
5
debian/control
vendored
@ -1,10 +1,9 @@
|
||||
Package: linux-patch-manager
|
||||
Version: 1.1.5-1
|
||||
Version: 1.0.0-1
|
||||
Architecture: amd64
|
||||
Maintainer: Moon Dragon <echo@moon-dragon.us>
|
||||
Installed-Size: 45000
|
||||
Pre-Depends: postgresql-16
|
||||
Depends: postgresql-16, argon2, libssl3, libc6 (>= 2.39), libfontconfig1
|
||||
Depends: postgresql-16, libssl3, libc6 (>= 2.39), libfontconfig1
|
||||
Recommends: postgresql-client-16, fonts-dejavu-core
|
||||
Suggests: gpg
|
||||
Section: admin
|
||||
|
||||
417
debian/postinst
vendored
Executable file → Normal file
417
debian/postinst
vendored
Executable file → Normal file
@ -4,348 +4,91 @@ set -e
|
||||
# =============================================================================
|
||||
# Linux Patch Manager — Post-install script
|
||||
# =============================================================================
|
||||
# Fully automated: apt install ./linux-patch-manager_X.X.X-1_amd64.deb
|
||||
# results in a running service with a printed admin password.
|
||||
# All steps are idempotent (safe to re-run on upgrade).
|
||||
# =============================================================================
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||
|
||||
DB_NAME="patch_manager"
|
||||
DB_USER="patch_manager"
|
||||
CONFIG_DIR="/etc/patch-manager"
|
||||
MIGRATION_DIR="/usr/share/patch-manager/migrations"
|
||||
ADMIN_PASSWORD_FILE="/etc/patch-manager/admin-password.txt"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# PostgreSQL helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
psql_run() {
|
||||
# Run SQL as the postgres superuser
|
||||
sudo -u postgres psql -v ON_ERROR_STOP=1 "$@" 2>/dev/null
|
||||
}
|
||||
|
||||
psql_run_db() {
|
||||
# Run SQL against the patch_manager database
|
||||
sudo -u postgres psql -v ON_ERROR_STOP=1 -d "${DB_NAME}" "$@" 2>/dev/null
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. Create service user (idempotent)
|
||||
# ---------------------------------------------------------------------------
|
||||
create_service_user() {
|
||||
if ! id patch-manager &>/dev/null; then
|
||||
useradd --system --no-create-home --shell /usr/sbin/nologin \
|
||||
--comment "Linux Patch Manager service account" patch-manager
|
||||
info "Service user 'patch-manager' created."
|
||||
else
|
||||
info "Service user 'patch-manager' already exists."
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. Create required directories (idempotent)
|
||||
# ---------------------------------------------------------------------------
|
||||
create_directories() {
|
||||
mkdir -p "${CONFIG_DIR}/ca" "${CONFIG_DIR}/certs" \
|
||||
"${CONFIG_DIR}/jwt" "${CONFIG_DIR}/tls" \
|
||||
/var/log/patch-manager /opt/patch-manager \
|
||||
/var/backups/patch-manager
|
||||
|
||||
chown -R patch-manager:patch-manager \
|
||||
"${CONFIG_DIR}" /var/log/patch-manager \
|
||||
/opt/patch-manager /usr/share/patch-manager/frontend
|
||||
|
||||
chmod 750 "${CONFIG_DIR}/ca" "${CONFIG_DIR}/jwt"
|
||||
chmod 700 /var/backups/patch-manager
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. Wait for PostgreSQL to be ready
|
||||
# ---------------------------------------------------------------------------
|
||||
wait_for_postgresql() {
|
||||
info "Waiting for PostgreSQL to be ready..."
|
||||
local retries=30
|
||||
local delay=2
|
||||
local i
|
||||
for ((i = 1; i <= retries; i++)); do
|
||||
if pg_isready -q 2>/dev/null; then
|
||||
info "PostgreSQL is ready."
|
||||
return 0
|
||||
fi
|
||||
warn "PostgreSQL not ready yet (attempt ${i}/${retries}), waiting ${delay}s..."
|
||||
sleep "${delay}"
|
||||
done
|
||||
error "PostgreSQL did not become ready after $((retries * delay)) seconds."
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 4. Create PostgreSQL user and database (idempotent)
|
||||
# ---------------------------------------------------------------------------
|
||||
setup_database() {
|
||||
info "Setting up PostgreSQL database and user..."
|
||||
|
||||
# Generate a random password for the DB user
|
||||
local db_password
|
||||
db_password=$(openssl rand -base64 32 | tr -dc 'A-Za-z0-9' | head -c 32)
|
||||
|
||||
# Create role if not exists
|
||||
local role_exists
|
||||
role_exists=$(psql_run -t -A -c "SELECT 1 FROM pg_roles WHERE rolname='${DB_USER}'" 2>/dev/null || echo "")
|
||||
if [[ "${role_exists}" != "1" ]]; then
|
||||
psql_run -c "CREATE ROLE ${DB_USER} LOGIN PASSWORD '${db_password}';"
|
||||
info "PostgreSQL user '${DB_USER}' created."
|
||||
# Store password for config generation
|
||||
echo "${db_password}" > /tmp/.pm-db-password-new
|
||||
else
|
||||
info "PostgreSQL user '${DB_USER}' already exists, skipping creation."
|
||||
fi
|
||||
|
||||
# Create database if not exists
|
||||
local db_exists
|
||||
db_exists=$(psql_run -t -A -c "SELECT 1 FROM pg_database WHERE datname='${DB_NAME}'" 2>/dev/null || echo "")
|
||||
if [[ "${db_exists}" != "1" ]]; then
|
||||
psql_run -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};"
|
||||
info "Database '${DB_NAME}' created."
|
||||
else
|
||||
info "Database '${DB_NAME}' already exists, skipping creation."
|
||||
fi
|
||||
|
||||
# Grant permissions (idempotent)
|
||||
psql_run_db -c "GRANT USAGE ON SCHEMA public TO ${DB_USER};" 2>/dev/null || true
|
||||
psql_run_db -c "GRANT CREATE ON SCHEMA public TO ${DB_USER};" 2>/dev/null || true
|
||||
psql_run_db -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 5. Apply database migrations (idempotent)
|
||||
# ---------------------------------------------------------------------------
|
||||
apply_migrations() {
|
||||
info "Applying database migrations..."
|
||||
|
||||
# Ensure pgcrypto extension is available
|
||||
psql_run_db -c "CREATE EXTENSION IF NOT EXISTS pgcrypto;" 2>/dev/null || true
|
||||
|
||||
# Create migration tracking table if not exists
|
||||
psql_run_db <<'MIGSQL'
|
||||
CREATE TABLE IF NOT EXISTS _migrations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
filename TEXT NOT NULL UNIQUE,
|
||||
applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
MIGSQL
|
||||
|
||||
# Handle upgrade from pre-migration-tracking versions:
|
||||
# If tables exist but _migrations is empty, mark all existing migrations as applied.
|
||||
local migration_count
|
||||
migration_count=$(psql_run_db -t -A -c "SELECT COUNT(*) FROM _migrations;" 2>/dev/null || echo "0")
|
||||
migration_count="${migration_count// /}"
|
||||
|
||||
local tables_exist
|
||||
tables_exist=$(psql_run_db -t -A -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public' AND table_name='users';" 2>/dev/null || echo "0")
|
||||
tables_exist="${tables_exist// /}"
|
||||
|
||||
if [[ "${migration_count}" == "0" && "${tables_exist}" -gt 0 ]]; then
|
||||
info "Existing database detected — marking all shipped migrations as already applied."
|
||||
for sql_file in $(ls "${MIGRATION_DIR}"/*.sql 2>/dev/null | sort); do
|
||||
local fname
|
||||
fname=$(basename "${sql_file}")
|
||||
psql_run_db -c "INSERT INTO _migrations (filename) VALUES ('${fname}') ON CONFLICT (filename) DO NOTHING;" 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
|
||||
# Apply each migration in sorted order, skipping already-applied ones
|
||||
local applied=0
|
||||
local skipped=0
|
||||
for sql_file in $(ls "${MIGRATION_DIR}"/*.sql 2>/dev/null | sort); do
|
||||
local fname
|
||||
fname=$(basename "${sql_file}")
|
||||
|
||||
local already_applied
|
||||
already_applied=$(psql_run_db -t -A -c "SELECT COUNT(*) FROM _migrations WHERE filename='${fname}';" 2>/dev/null || echo "0")
|
||||
already_applied="${already_applied// /}"
|
||||
|
||||
if [[ "${already_applied}" -gt 0 ]]; then
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
info " Applying migration: ${fname}"
|
||||
if psql_run_db -f "${sql_file}"; then
|
||||
psql_run_db -c "INSERT INTO _migrations (filename) VALUES ('${fname}');" 2>/dev/null || true
|
||||
applied=$((applied + 1))
|
||||
else
|
||||
error " Failed to apply migration: ${fname}"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "${applied}" -gt 0 ]]; then
|
||||
info "Applied ${applied} new migration(s), skipped ${skipped} already applied."
|
||||
else
|
||||
info "All migrations up to date (${skipped} already applied)."
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6. Generate admin password and update database
|
||||
# ---------------------------------------------------------------------------
|
||||
generate_admin_password() {
|
||||
info "Generating admin password..."
|
||||
|
||||
# Generate a random 24-character password
|
||||
local 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)
|
||||
local password_hash
|
||||
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
|
||||
# The placeholder starts with: $argon2id$v=19$m=65536,t=3,p=1$AAAAAAAAAAAAAAAA
|
||||
# Using single-quoted variable to preserve $ signs in SQL LIKE pattern
|
||||
local placeholder_pattern
|
||||
placeholder_pattern='$argon2id$v=19$m=65536,t=3,p=1$AAAAAAAAAAAAAAAA%'
|
||||
|
||||
local updated
|
||||
updated=$(psql_run_db -t -A -c \
|
||||
"UPDATE users SET password_hash = '${password_hash}', force_password_reset = TRUE \
|
||||
WHERE username = 'admin' AND password_hash LIKE '${placeholder_pattern}' \
|
||||
RETURNING id;" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "${updated}" ]]; then
|
||||
# Write admin password to file (mode 600, owned by root)
|
||||
echo "${admin_password}" > "${ADMIN_PASSWORD_FILE}"
|
||||
chmod 600 "${ADMIN_PASSWORD_FILE}"
|
||||
chown root:root "${ADMIN_PASSWORD_FILE}"
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}=============================================${NC}"
|
||||
echo -e "${CYAN} Linux Patch Manager — Admin Credentials${NC}"
|
||||
echo -e "${CYAN}=============================================${NC}"
|
||||
echo -e " Username: ${GREEN}admin${NC}"
|
||||
echo -e " Password: ${GREEN}${admin_password}${NC}"
|
||||
echo ""
|
||||
echo -e " ${YELLOW}IMPORTANT: Save this password! It will not be shown again.${NC}"
|
||||
echo -e " Password also saved to: ${ADMIN_PASSWORD_FILE}"
|
||||
echo -e "${CYAN}=============================================${NC}"
|
||||
echo ""
|
||||
else
|
||||
info "Admin password already set (not a fresh install). Password file not regenerated."
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 7. Write config.toml with DB URL (only if file doesn't exist)
|
||||
# ---------------------------------------------------------------------------
|
||||
write_config() {
|
||||
local config_file="${CONFIG_DIR}/config.toml"
|
||||
|
||||
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 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)
|
||||
# ---------------------------------------------------------------------------
|
||||
generate_jwt_keys() {
|
||||
if [[ ! -f "${CONFIG_DIR}/jwt/signing.pem" ]]; then
|
||||
info "Generating Ed25519 JWT signing key..."
|
||||
openssl genpkey -algorithm ed25519 -out "${CONFIG_DIR}/jwt/signing.pem" 2>/dev/null
|
||||
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/signing.pem" "${CONFIG_DIR}/jwt/verify.pem"
|
||||
chmod 600 "${CONFIG_DIR}/jwt/signing.pem"
|
||||
chmod 644 "${CONFIG_DIR}/jwt/verify.pem"
|
||||
info "JWT keys generated."
|
||||
else
|
||||
info "JWT signing key already exists, skipping."
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 9. Enable and start services
|
||||
# ---------------------------------------------------------------------------
|
||||
enable_and_start_services() {
|
||||
systemctl daemon-reload
|
||||
|
||||
# Enable the target (which pulls in web + worker)
|
||||
systemctl enable patch-manager.target 2>/dev/null || true
|
||||
|
||||
# Start or restart services
|
||||
if systemctl is-active --quiet patch-manager.target 2>/dev/null; then
|
||||
info "Restarting patch-manager services (upgrade)..."
|
||||
systemctl restart patch-manager.target 2>/dev/null || true
|
||||
else
|
||||
info "Starting patch-manager services..."
|
||||
systemctl start patch-manager.target 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 10. Install backup cron (idempotent)
|
||||
# ---------------------------------------------------------------------------
|
||||
install_backup_cron() {
|
||||
if ! crontab -l 2>/dev/null | grep -qF "backup.sh"; then
|
||||
(crontab -l 2>/dev/null; echo "0 2 * * * /usr/local/bin/backup.sh >> /var/log/patch-manager/backup.log 2>&1") | crontab -
|
||||
info "Nightly backup cron installed."
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Main
|
||||
# =============================================================================
|
||||
case "$1" in
|
||||
configure)
|
||||
create_service_user
|
||||
create_directories
|
||||
wait_for_postgresql
|
||||
setup_database
|
||||
apply_migrations
|
||||
generate_admin_password
|
||||
write_config
|
||||
generate_jwt_keys
|
||||
enable_and_start_services
|
||||
install_backup_cron
|
||||
# Create service user if not exists
|
||||
if ! id patch-manager &>/dev/null; then
|
||||
useradd --system --no-create-home --shell /usr/sbin/nologin \
|
||||
--comment "Linux Patch Manager service account" patch-manager
|
||||
fi
|
||||
|
||||
# Clean up temp file
|
||||
rm -f /tmp/.pm-db-password-new
|
||||
# Create required directories
|
||||
mkdir -p /etc/patch-manager/ca /etc/patch-manager/certs \
|
||||
/etc/patch-manager/jwt /etc/patch-manager/tls \
|
||||
/var/log/patch-manager /opt/patch-manager \
|
||||
/var/backups/patch-manager
|
||||
|
||||
info "Linux Patch Manager installation complete."
|
||||
chown -R patch-manager:patch-manager \
|
||||
/etc/patch-manager /var/log/patch-manager \
|
||||
/opt/patch-manager /usr/share/patch-manager/frontend
|
||||
|
||||
chmod 750 /etc/patch-manager/ca /etc/patch-manager/jwt
|
||||
chmod 700 /var/backups/patch-manager
|
||||
|
||||
# Generate JWT signing key if not present
|
||||
if [[ ! -f /etc/patch-manager/jwt/signing.pem ]]; then
|
||||
openssl genpkey -algorithm ed25519 -out /etc/patch-manager/jwt/signing.pem 2>/dev/null
|
||||
openssl pkey -in /etc/patch-manager/jwt/signing.pem -pubout -out /etc/patch-manager/jwt/verify.pem 2>/dev/null
|
||||
chown patch-manager:patch-manager /etc/patch-manager/jwt/signing.pem /etc/patch-manager/jwt/verify.pem
|
||||
chmod 600 /etc/patch-manager/jwt/signing.pem
|
||||
chmod 644 /etc/patch-manager/jwt/verify.pem
|
||||
fi
|
||||
|
||||
# Write default config if not present
|
||||
if [[ ! -f /etc/patch-manager/config.toml ]]; then
|
||||
cp /usr/share/patch-manager/config.example.toml /etc/patch-manager/config.toml
|
||||
chown patch-manager:patch-manager /etc/patch-manager/config.toml
|
||||
chmod 640 /etc/patch-manager/config.toml
|
||||
fi
|
||||
|
||||
# Install backup cron if not present
|
||||
if ! crontab -l 2>/dev/null | grep -qF "backup.sh"; then
|
||||
(crontab -l 2>/dev/null; echo "0 2 * * * /usr/local/bin/backup.sh >> /var/log/patch-manager/backup.log 2>&1") | crontab -
|
||||
fi
|
||||
|
||||
# Reload systemd
|
||||
systemctl daemon-reload
|
||||
|
||||
# Restart services if this is an upgrade (not a fresh install)
|
||||
if systemctl is-active --quiet patch-manager-web 2>/dev/null; then
|
||||
systemctl restart patch-manager-web || true
|
||||
fi
|
||||
if systemctl is-active --quiet patch-manager-worker 2>/dev/null; then
|
||||
systemctl restart patch-manager-worker || true
|
||||
fi
|
||||
|
||||
# Run pending database migrations
|
||||
MIGRATION_DIR="/usr/share/patch-manager/migrations"
|
||||
if [[ -d "$MIGRATION_DIR" ]]; then
|
||||
echo "Applying database migrations..."
|
||||
for sql_file in $(ls "$MIGRATION_DIR"/*.sql 2>/dev/null | sort); do
|
||||
echo " Applying: $(basename "$sql_file")"
|
||||
done
|
||||
echo "Note: Migrations must be applied manually: sudo -u patch_manager psql -d patch_manager -f <migration_file>"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Linux Patch Manager installed successfully!"
|
||||
echo "==========================================="
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Install and configure PostgreSQL:"
|
||||
echo " apt install postgresql-16"
|
||||
echo " 2. Create the database:"
|
||||
echo " sudo -u postgres createdb -O patch_manager patch_manager"
|
||||
echo " 3. Edit /etc/patch-manager/config.toml with your database URL"
|
||||
echo " 4. Enable and start services:"
|
||||
echo " systemctl enable --now patch-manager.target"
|
||||
echo " 5. Access the web UI at https://localhost"
|
||||
echo " Default admin credentials are set via the seed migration."
|
||||
echo ""
|
||||
echo "IMPORTANT: Change the default admin password immediately after first login!"
|
||||
echo ""
|
||||
echo "If this is an upgrade, services have been restarted automatically."
|
||||
echo "Apply any new database migrations:"
|
||||
echo " sudo -u patch_manager psql -d patch_manager -f /usr/share/patch-manager/migrations/<NNN_migration>.sql"
|
||||
echo ""
|
||||
;;
|
||||
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
# =============================================================================
|
||||
# Linux Patch Manager — Docker Compose Deployment
|
||||
# =============================================================================
|
||||
# Usage:
|
||||
# cp .env.example .env # Edit DB_PASSWORD
|
||||
# docker compose up -d
|
||||
# =============================================================================
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: patch_manager
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_DB: patch_manager
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U patch_manager -d patch_manager"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 10s
|
||||
networks:
|
||||
- patch-manager-net
|
||||
|
||||
app:
|
||||
image: ghcr.io/draco-lunaris/linux-patch-manager:${TAG:-latest}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "443:443"
|
||||
environment:
|
||||
DATABASE_URL: postgres://patch_manager:${DB_PASSWORD}@db:5432/patch_manager
|
||||
PATCH_MANAGER_CONFIG: /etc/patch-manager/config.toml
|
||||
volumes:
|
||||
- pm-config:/etc/patch-manager
|
||||
- pm-logs:/var/log/patch-manager
|
||||
- pm-data:/opt/patch-manager
|
||||
networks:
|
||||
- patch-manager-net
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
driver: local
|
||||
pm-config:
|
||||
driver: local
|
||||
pm-logs:
|
||||
driver: local
|
||||
pm-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
patch-manager-net:
|
||||
driver: bridge
|
||||
@ -1,232 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# =============================================================================
|
||||
# Linux Patch Manager — Docker Entrypoint
|
||||
# =============================================================================
|
||||
# Handles first-run: wait for DB, run migrations, generate admin password,
|
||||
# start pm-web and pm-worker services.
|
||||
# =============================================================================
|
||||
|
||||
MIGRATION_DIR="/usr/share/patch-manager/migrations"
|
||||
CONFIG_DIR="/etc/patch-manager"
|
||||
ADMIN_PASSWORD_FILE="/etc/patch-manager/admin-password.txt"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Parse DATABASE_URL into PG* env vars for psql compatibility
|
||||
# ---------------------------------------------------------------------------
|
||||
parse_database_url() {
|
||||
# DATABASE_URL format: postgres://user:password@host:port/dbname
|
||||
local url="${DATABASE_URL}"
|
||||
|
||||
# Extract components
|
||||
DB_PASS=$(echo "$url" | sed -n 's|postgres://[^:]*:\([^@]*\)@.*|\1|p')
|
||||
DB_HOST=$(echo "$url" | sed -n 's|.*@\([^:/]*\).*|\1|p')
|
||||
DB_PORT=$(echo "$url" | sed -n 's|.*:\([0-9]*\)/.*|\1|p')
|
||||
DB_USER=$(echo "$url" | sed -n 's|postgres://\([^:]*\):.*|\1|p')
|
||||
DB_NAME=$(echo "$url" | sed -n 's|.*/\([^?]*\).*|\1|p')
|
||||
|
||||
# Default port
|
||||
DB_PORT="${DB_PORT:-5432}"
|
||||
|
||||
export PGHOST="${DB_HOST}"
|
||||
export PGPORT="${DB_PORT}"
|
||||
export PGUSER="${DB_USER}"
|
||||
export PGPASSWORD="${DB_PASS}"
|
||||
export PGDATABASE="${DB_NAME}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Wait for PostgreSQL to be ready
|
||||
# ---------------------------------------------------------------------------
|
||||
wait_for_db() {
|
||||
echo "[entrypoint] Waiting for PostgreSQL at ${PGHOST}:${DB_PORT}..."
|
||||
local retries=60
|
||||
local delay=2
|
||||
local i
|
||||
for ((i = 1; i <= retries; i++)); do
|
||||
if pg_isready -q -h "${PGHOST}" -p "${DB_PORT}" -U "${DB_USER}" 2>/dev/null; then
|
||||
echo "[entrypoint] PostgreSQL is ready."
|
||||
return 0
|
||||
fi
|
||||
echo "[entrypoint] PostgreSQL not ready (attempt ${i}/${retries}), waiting ${delay}s..."
|
||||
sleep "${delay}"
|
||||
done
|
||||
echo "[entrypoint] ERROR: PostgreSQL did not become ready after $((retries * delay)) seconds." >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Run database migrations (idempotent)
|
||||
# ---------------------------------------------------------------------------
|
||||
run_migrations() {
|
||||
echo "[entrypoint] Applying database migrations..."
|
||||
|
||||
# Ensure pgcrypto extension
|
||||
psql -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS pgcrypto;" 2>/dev/null || true
|
||||
|
||||
# Create migration tracking table
|
||||
psql -v ON_ERROR_STOP=1 <<'EOSQL'
|
||||
CREATE TABLE IF NOT EXISTS _migrations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
filename TEXT NOT NULL UNIQUE,
|
||||
applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
EOSQL
|
||||
|
||||
# Handle upgrade from pre-migration-tracking versions
|
||||
local migration_count
|
||||
migration_count=$(psql -t -A -c "SELECT COUNT(*) FROM _migrations;" 2>/dev/null || echo "0")
|
||||
migration_count="${migration_count// /}"
|
||||
|
||||
local tables_exist
|
||||
tables_exist=$(psql -t -A -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public' AND table_name='users';" 2>/dev/null || echo "0")
|
||||
tables_exist="${tables_exist// /}"
|
||||
|
||||
if [[ "${migration_count}" == "0" && "${tables_exist}" -gt 0 ]]; then
|
||||
echo "[entrypoint] Existing database detected — marking all shipped migrations as already applied."
|
||||
for sql_file in $(ls "${MIGRATION_DIR}"/*.sql 2>/dev/null | sort); do
|
||||
local fname
|
||||
fname=$(basename "${sql_file}")
|
||||
psql -v ON_ERROR_STOP=1 -c "INSERT INTO _migrations (filename) VALUES ('${fname}') ON CONFLICT (filename) DO NOTHING;" 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
|
||||
# Apply each migration in sorted order
|
||||
local applied=0
|
||||
local skipped=0
|
||||
for sql_file in $(ls "${MIGRATION_DIR}"/*.sql 2>/dev/null | sort); do
|
||||
local fname
|
||||
fname=$(basename "${sql_file}")
|
||||
|
||||
local already_applied
|
||||
already_applied=$(psql -t -A -c "SELECT COUNT(*) FROM _migrations WHERE filename='${fname}';" 2>/dev/null || echo "0")
|
||||
already_applied="${already_applied// /}"
|
||||
|
||||
if [[ "${already_applied}" -gt 0 ]]; then
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "[entrypoint] Applying migration: ${fname}"
|
||||
if psql -v ON_ERROR_STOP=1 -f "${sql_file}"; then
|
||||
psql -v ON_ERROR_STOP=1 -c "INSERT INTO _migrations (filename) VALUES ('${fname}');" 2>/dev/null || true
|
||||
applied=$((applied + 1))
|
||||
else
|
||||
echo "[entrypoint] ERROR: Failed to apply migration: ${fname}" >&2
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "${applied}" -gt 0 ]]; then
|
||||
echo "[entrypoint] Applied ${applied} new migration(s), skipped ${skipped}."
|
||||
else
|
||||
echo "[entrypoint] All migrations up to date (${skipped} already applied)."
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Generate admin password on first run
|
||||
# ---------------------------------------------------------------------------
|
||||
generate_admin_password() {
|
||||
echo "[entrypoint] Checking admin password status..."
|
||||
|
||||
# Generate a random 24-character password
|
||||
local admin_password
|
||||
admin_password=$(openssl rand -base64 32 | tr -dc 'A-Za-z0-9!@#%^&*' | head -c 24)
|
||||
|
||||
# Hash with argon2 (PHC format)
|
||||
local password_hash
|
||||
password_hash=$(echo -n "${admin_password}" | argon2 salt -id -t 3 -m 65536 -p 1 -l 32 -e)
|
||||
|
||||
# Update admin user — only if placeholder hash is still present
|
||||
# The placeholder starts with: $argon2id$v=19$m=65536,t=3,p=1$AAAAAAAAAAAAAAAA
|
||||
# Using single-quoted variable to preserve $ signs in the SQL LIKE pattern
|
||||
local placeholder_pattern
|
||||
placeholder_pattern='$argon2id$v=19$m=65536,t=3,p=1$AAAAAAAAAAAAAAAA%'
|
||||
|
||||
local updated
|
||||
updated=$(psql -t -A -c \
|
||||
"UPDATE users SET password_hash = '${password_hash}', force_password_reset = TRUE \
|
||||
WHERE username = 'admin' AND password_hash LIKE '${placeholder_pattern}' \
|
||||
RETURNING id;" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "${updated}" ]]; then
|
||||
echo "${admin_password}" > "${ADMIN_PASSWORD_FILE}"
|
||||
chmod 600 "${ADMIN_PASSWORD_FILE}"
|
||||
chown root:root "${ADMIN_PASSWORD_FILE}"
|
||||
|
||||
echo ""
|
||||
echo "============================================="
|
||||
echo " Linux Patch Manager — Admin Credentials"
|
||||
echo "============================================="
|
||||
echo " Username: admin"
|
||||
echo " Password: ${admin_password}"
|
||||
echo ""
|
||||
echo " IMPORTANT: Save this password! It will not be shown again."
|
||||
echo " Password also saved to: ${ADMIN_PASSWORD_FILE}"
|
||||
echo "============================================="
|
||||
echo ""
|
||||
else
|
||||
echo "[entrypoint] Admin password already set (not a fresh install)."
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Generate JWT keys if not present
|
||||
# ---------------------------------------------------------------------------
|
||||
generate_jwt_keys() {
|
||||
if [[ ! -f "${CONFIG_DIR}/jwt/signing.pem" ]]; then
|
||||
echo "[entrypoint] Generating Ed25519 JWT signing key..."
|
||||
openssl genpkey -algorithm ed25519 -out "${CONFIG_DIR}/jwt/signing.pem" 2>/dev/null
|
||||
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/signing.pem" "${CONFIG_DIR}/jwt/verify.pem"
|
||||
chmod 600 "${CONFIG_DIR}/jwt/signing.pem"
|
||||
chmod 644 "${CONFIG_DIR}/jwt/verify.pem"
|
||||
echo "[entrypoint] JWT keys generated."
|
||||
else
|
||||
echo "[entrypoint] JWT signing key already exists."
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Write config.toml if not present
|
||||
# ---------------------------------------------------------------------------
|
||||
write_config() {
|
||||
local config_file="${CONFIG_DIR}/config.toml"
|
||||
|
||||
if [[ -f "${config_file}" ]]; then
|
||||
echo "[entrypoint] Config file already exists, not overwriting."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "[entrypoint] Writing configuration file..."
|
||||
cp /usr/share/patch-manager/config.example.toml "${config_file}"
|
||||
sed -i "s|postgres://patch_manager:CHANGEME@localhost/patch_manager|${DATABASE_URL}|" "${config_file}"
|
||||
chown patch-manager:patch-manager "${config_file}"
|
||||
chmod 640 "${config_file}"
|
||||
echo "[entrypoint] Configuration written."
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
echo "[entrypoint] Linux Patch Manager Docker entrypoint starting..."
|
||||
|
||||
parse_database_url
|
||||
wait_for_db
|
||||
run_migrations
|
||||
generate_admin_password
|
||||
generate_jwt_keys
|
||||
write_config
|
||||
|
||||
echo "[entrypoint] Starting pm-web and pm-worker..."
|
||||
|
||||
# Start pm-worker in background
|
||||
pm-worker &
|
||||
WORKER_PID=$!
|
||||
|
||||
# Start pm-web in foreground (main process)
|
||||
export PATCH_MANAGER_CONFIG="${CONFIG_DIR}/config.toml"
|
||||
|
||||
exec pm-web
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "patch-manager-ui",
|
||||
"private": true,
|
||||
"version": "1.1.5",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@ -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.5"
|
||||
VERSION="1.0.0"
|
||||
RELEASE="1"
|
||||
PKG_NAME="linux-patch-manager"
|
||||
DEB_NAME="${PKG_NAME}_${VERSION}-${RELEASE}_amd64.deb"
|
||||
|
||||
Reference in New Issue
Block a user