Merge pull request #344 from rajnandan1/fix-ping-non-root

refactor: clean up Dockerfile and improve security practices
This commit is contained in:
Raj Nandan Sharma
2025-03-12 22:49:47 +05:30
committed by GitHub
6 changed files with 54 additions and 29 deletions

View File

@@ -58,10 +58,10 @@ COPY . .
# TODO: Reevaluate permissions (possibly reduce?)...
# Remove docs directory and ensure required directories exist
RUN rm -rf src/routes/\(docs\) \
static/documentation \
static/fonts/lato/full && \
mkdir -p uploads database && \
# TODO: Consider changing below to `chmod -R u-rwX,g=rX,o= uploads database`
static/documentation \
static/fonts/lato/full && \
mkdir -p uploads database && \
# TODO: Consider changing below to `chmod -R u-rwX,g=rX,o= uploads database`
chmod -R 750 uploads database
# Build the application and remove `devDependencies`
@@ -78,15 +78,15 @@ RUN apt-get update && apt-get install -y \
iputils-ping=3:20221126-1+deb12u1 \
sqlite3=3.40.1-2+deb12u1 \
tzdata=2024b-0+deb12u1 \
# TODO: Is it ok to change to `curl` here so that we don't have to maintain `wget` version mismatch between Debian architectures? (`curl` is only used for the container healthcheck and because there is an Alpine variant (best!) we probably don't care if the Debian image ends up building bigger due to `curl`.)
curl=7.88.1-10+deb12u8 && \
# TODO: Is it ok to change to `curl` here so that we don't have to maintain `wget` version mismatch between Debian architectures? (`curl` is only used for the container healthcheck and because there is an Alpine variant (best!) we probably don't care if the Debian image ends up building bigger due to `curl`.)
curl=7.88.1-10+deb12u8 && \
rm -rf /var/lib/apt/lists/*
FROM node:${ALPINE_VERSION} AS final-alpine
RUN apk add --no-cache --update \
iputils=20240905-r0 \
sqlite=3.48.0-r0 \
tzdata
iputils=20240905-r0 \
sqlite=3.48.0-r0 \
tzdata
FROM final-${VARIANT} AS final
@@ -120,25 +120,34 @@ COPY --chown=node:node --from=builder /app/main.js ./main.js
COPY --chown=node:node --from=builder /app/openapi.json ./openapi.json
COPY --chown=node:node --from=builder /app/openapi.yaml ./openapi.yaml
# Set capabilities for ping (before changing to non-root user)
RUN if [ -f "/etc/alpine-release" ]; then \
# Alpine path
setcap cap_net_raw+ep /bin/ping; \
else \
# Debian path
setcap cap_net_raw+ep /usr/bin/ping; \
fi
# Ensure necessary directories are writable
VOLUME ["/uploads", "/database"]
# Set container timezone and make entrypoint script executable
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone && \
chmod +x ./entrypoint.sh
# TODO: To improve security, consider dropping unnecessary capabilities instead of granting image all network capabilities of host. (Maybe `setcap cap_net_raw+p /usr/bin/ping`, etc.) Could also drop all and then grant only the capabilities that are explicitly needed. Some examples are commented out below...
# setcap cap_net_bind_service=+ep /usr/local/bin/node
# setcap cap_net_bind_service=+ep /usr/bin/ping
# setcap cap_net_bind_service=+ep /usr/bin/ping6
# setcap cap_net_bind_service=+ep /usr/bin/tracepath
# setcap cap_net_bind_service=+ep /usr/bin/clockdiff
# TODO: To improve security, consider dropping unnecessary capabilities instead of granting image all network capabilities of host. (Maybe `setcap cap_net_raw+p /usr/bin/ping`, etc.) Could also drop all and then grant only the capabilities that are explicitly needed. Some examples are commented out below...
# setcap cap_net_bind_service=+ep /usr/local/bin/node
# setcap cap_net_bind_service=+ep /usr/bin/ping
# setcap cap_net_bind_service=+ep /usr/bin/ping6
# setcap cap_net_bind_service=+ep /usr/bin/tracepath
# setcap cap_net_bind_service=+ep /usr/bin/clockdiff
# Expose the application port
EXPOSE $PORT
# Add a healthcheck to the container; `wget` vs. `curl` depending on base image. Using this approach because `wget` does not actually maintain versioning across architectures, so we cannot pin a `wget` version (in above `final-debian` base, `apt-get install`) between differing architectures (e.g. arm64, amd64)
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD sh -c 'if [ -f "/etc/alpine-release" ]; then wget --quiet --spider http://localhost:$HEALTHCHECK_PORT$HEALTHCHECK_PATH || exit 1; else curl --silent --head --fail http://localhost:$HEALTHCHECK_PORT$HEALTHCHECK_PATH || exit 1; fi'
CMD sh -c 'if [ -f "/etc/alpine-release" ]; then wget --quiet --spider http://localhost:$HEALTHCHECK_PORT$HEALTHCHECK_PATH || exit 1; else curl --silent --head --fail http://localhost:$HEALTHCHECK_PORT$HEALTHCHECK_PATH || exit 1; fi'
# TODO: Revisit letting user define $PUID & $PGID overrides (e.g. `addgroup -g $PGID newgroup && adduser -D -G newgroup -u $PUID node`) as well as potentially ensure no root user exists. (Make sure no processes are running as root, first!)
# Use a non-root user (recommended for security)

View File

@@ -1,6 +1,6 @@
{
"name": "kener",
"version": "3.2.2",
"version": "3.2.3",
"private": false,
"license": "MIT",
"description": "Kener: An open-source Node.js status page application for real-time service monitoring, incident management, and customizable reporting. Simplify service outage tracking, enhance incident communication, and ensure a seamless user experience.",

View File

@@ -13,7 +13,7 @@ class Webhook {
constructor(trigger_meta, method, siteData, monitorData) {
const kenerHeader = {
"Content-Type": "application/json",
"User-Agent": "Kener/3.2.2",
"User-Agent": "Kener/3.2.3",
};
let headers = trigger_meta.headers;
this.trigger_meta = trigger_meta;

View File

@@ -1,19 +1,32 @@
// @ts-nocheck
import figlet from "figlet";
import { Cron } from "croner";
import { Minuter } from "./cron-minute.js";
import db from "./db/db.js";
import { GetAllSiteData, GetMonitorsParsed, HashString } from "./controllers/controller.js";
import { fileURLToPath } from "url";
import { dirname } from "path";
import { dirname, resolve } from "path";
import fs from "fs";
const jobs = [];
process.env.TZ = "UTC";
let isStartUP = true;
// Get the version from package.json
const getVersion = () => {
try {
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packagePath = resolve(__dirname, "../../../package.json");
const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8"));
return packageJson.version;
} catch (error) {
console.error("Error reading version:", error);
return "unknown";
}
};
const scheduleCronJobs = async () => {
// Fetch and map all active monitors, creating a unique hash for each
const activeMonitors = (await GetMonitorsParsed({ status: "ACTIVE" })).map((monitor) => ({
@@ -72,12 +85,15 @@ async function Startup() {
mainJob.trigger();
figlet("Kener is UP!", function (err, data) {
const version = getVersion();
figlet("Kener v" + version, function (err, data) {
if (err) {
console.log("Something went wrong...");
return;
}
console.log(data);
console.log(`Kener version ${version} is running!`);
});
}

View File

@@ -110,7 +110,7 @@
<!-- Document Icon - Replace with your own logo -->
<img src="https://kener.ing/logo.png" class="h-8 w-8" alt="" />
<span class="text-xl font-medium">Kener Documentation</span>
<span class="me-2 rounded border px-2.5 py-0.5 text-xs font-medium"> 3.2.2 </span>
<span class="me-2 rounded border px-2.5 py-0.5 text-xs font-medium"> 3.2.3 </span>
</a>
</div>
@@ -202,7 +202,7 @@
</div>
<div class="flex items-center space-x-3">
<a
href="https://github.com/sponsors/rajnandan1"
href="https://buymeacoffee.com/rajnandan1"
class="rounded-md bg-amber-500 px-3 py-1.5 text-sm font-medium text-white transition-colors hover:bg-amber-600"
>
Sponsor

View File

@@ -158,18 +158,18 @@
<!-- List -->
<ul class="flex flex-wrap items-center">
<li
class="before:size-[3px] relative inline-block pe-4 text-xs before:absolute before:end-1.5 before:top-1/2 before:-translate-y-1/2 before:rounded-full before:bg-gray-400 last:pe-0 last-of-type:before:hidden dark:text-neutral-500 dark:before:bg-neutral-600"
class="relative inline-block pe-4 text-xs before:absolute before:end-1.5 before:top-1/2 before:size-[3px] before:-translate-y-1/2 before:rounded-full before:bg-gray-400 last:pe-0 last-of-type:before:hidden dark:text-neutral-500 dark:before:bg-neutral-600"
>
<a
target="_blank"
class="text-xs font-medium text-muted-foreground hover:underline hover:decoration-1 focus:decoration-1 focus:outline-none"
href="https://github.com/sponsors/rajnandan1"
href="https://buymeacoffee.com/rajnandan1"
>
Donate
</a>
</li>
<li
class="before:size-[3px] relative inline-block pe-4 text-xs before:absolute before:end-1.5 before:top-1/2 before:-translate-y-1/2 before:rounded-full before:bg-gray-400 last:pe-0 last-of-type:before:hidden dark:text-neutral-500 dark:before:bg-neutral-600"
class="relative inline-block pe-4 text-xs before:absolute before:end-1.5 before:top-1/2 before:size-[3px] before:-translate-y-1/2 before:rounded-full before:bg-gray-400 last:pe-0 last-of-type:before:hidden dark:text-neutral-500 dark:before:bg-neutral-600"
>
<a
target="_blank"
@@ -180,7 +180,7 @@
</a>
</li>
<li
class="before:size-[3px] relative inline-block pe-4 text-xs before:absolute before:end-1.5 before:top-1/2 before:-translate-y-1/2 before:rounded-full before:bg-gray-400 last:pe-0 last-of-type:before:hidden dark:text-neutral-500 dark:before:bg-neutral-600"
class="relative inline-block pe-4 text-xs before:absolute before:end-1.5 before:top-1/2 before:size-[3px] before:-translate-y-1/2 before:rounded-full before:bg-gray-400 last:pe-0 last-of-type:before:hidden dark:text-neutral-500 dark:before:bg-neutral-600"
>
<a
target="_blank"
@@ -191,7 +191,7 @@
</a>
</li>
<li
class="before:size-[3px] relative inline-block pe-4 text-xs before:absolute before:end-1.5 before:top-1/2 before:-translate-y-1/2 before:rounded-full before:bg-gray-400 last:pe-0 last-of-type:before:hidden dark:text-neutral-500 dark:before:bg-neutral-600"
class="relative inline-block pe-4 text-xs before:absolute before:end-1.5 before:top-1/2 before:size-[3px] before:-translate-y-1/2 before:rounded-full before:bg-gray-400 last:pe-0 last-of-type:before:hidden dark:text-neutral-500 dark:before:bg-neutral-600"
>
<a
target="_blank"