From 6e344749938b1b545816e5e14f45114291fcd679 Mon Sep 17 00:00:00 2001 From: Nathan JAUNET Date: Thu, 2 Oct 2025 17:23:44 +0200 Subject: [PATCH 1/6] Preserve user permission for command execution --- .gitignore | 1 + app/_server/actions/cronjobs/index.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 439226a..94c7e7e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,6 @@ node_modules .vscode .DS_Store .cursorignore +.idea tsconfig.tsbuildinfo docker-compose.test.yml \ No newline at end of file diff --git a/app/_server/actions/cronjobs/index.ts b/app/_server/actions/cronjobs/index.ts index e18e77e..f81ad2f 100644 --- a/app/_server/actions/cronjobs/index.ts +++ b/app/_server/actions/cronjobs/index.ts @@ -267,7 +267,7 @@ export const runCronJob = async ( const userInfo = await getUserInfo(job.user); if (userInfo && userInfo.username !== "root") { - command = `nsenter -t 1 -m -u -i -n -p --setuid=${userInfo.uid} --setgid=${userInfo.gid} sh -c "${job.command}"`; + command = `nsenter -t 1 -m -u -i -n -p sh -c "setpriv --reuid=${userInfo.uid} --regid=${userInfo.gid} --init-groups -- sh -c \\"${job.command}\\""` } else { command = `nsenter -t 1 -m -u -i -n -p sh -c "${job.command}"`; } From 8329c0d0309ab0b46cfe7a08a750415fc4126290 Mon Sep 17 00:00:00 2001 From: fccview Date: Wed, 8 Oct 2025 14:34:34 +0100 Subject: [PATCH 2/6] make it so root works via a user instead, also update permissions for scripts on creation so they are executable right off the bat --- README.md | 7 ++++++ app/_server/actions/cronjobs/index.ts | 15 ++++++++--- app/_server/actions/scripts/index.ts | 36 ++++++++++++++++----------- docker-compose.yml | 6 +++++ 4 files changed, 46 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index b6339c8..30de75a 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,12 @@ services: # replace root with your user - find it with: ls -asl /var/spool/cron/crontabs/ # For multiple users, use comma-separated values: HOST_CRONTAB_USER=root,user1,user2 - HOST_CRONTAB_USER=root + + # --- !! IMPORTANT !!DOCKER EXEC USER + # If you do not specify this user to be a valid user on your system, + # any cronjob containing a docker command will fail. IDEALLY you should not be running + # docker commands as root, so this is only a fallback. ONLY ONE USER IS ALLOWED. + - DOCKER_EXEC_USER=fccview volumes: # --- MOUNT DOCKER SOCKET # Mount Docker socket to execute commands on host @@ -147,6 +153,7 @@ The following environment variables can be configured: | `DOCKER` | `false` | ONLY set this to true if you are runnign the app via docker, in the docker-compose.yml file | | `HOST_CRONTAB_USER` | `root` | Comma separated list of users that run cronjobs on your host machine | | `AUTH_PASSWORD` | `N/A` | If you set a password the application will be password protected with basic next-auth | +| `DOCKER_EXEC_USER` | `N/A` | If you don't set this user you won't be able to run docker commands as root. | **Example**: To change the clock update interval to 60 seconds: diff --git a/app/_server/actions/cronjobs/index.ts b/app/_server/actions/cronjobs/index.ts index f81ad2f..534e1c3 100644 --- a/app/_server/actions/cronjobs/index.ts +++ b/app/_server/actions/cronjobs/index.ts @@ -265,12 +265,19 @@ export const runCronJob = async ( if (isDocker) { const userInfo = await getUserInfo(job.user); + const dockerExecUser = process.env.DOCKER_EXEC_USER; - if (userInfo && userInfo.username !== "root") { - command = `nsenter -t 1 -m -u -i -n -p sh -c "setpriv --reuid=${userInfo.uid} --regid=${userInfo.gid} --init-groups -- sh -c \\"${job.command}\\""` - } else { - command = `nsenter -t 1 -m -u -i -n -p sh -c "${job.command}"`; + let executionUser = userInfo ? userInfo.username : "root"; + + if (dockerExecUser && executionUser === "root") { + console.log( + `Overriding root execution. Running command as user: ${dockerExecUser}` + ); + executionUser = dockerExecUser; } + + const escapedCommand = job.command.replace(/'/g, "'\\''"); + command = `nsenter -t 1 -m -u -i -n -p su - ${executionUser} -c '${escapedCommand}'`; } const { stdout, stderr } = await execAsync(command, { diff --git a/app/_server/actions/scripts/index.ts b/app/_server/actions/scripts/index.ts index 6567ea0..0377d88 100644 --- a/app/_server/actions/scripts/index.ts +++ b/app/_server/actions/scripts/index.ts @@ -21,7 +21,7 @@ const sanitizeScriptName = (name: string): string => { .replace(/-+/g, "-") .replace(/^-|-$/g, "") .substring(0, 50); -} +}; const generateUniqueFilename = async (baseName: string): Promise => { const scripts = await loadAllScripts(); @@ -34,14 +34,14 @@ const generateUniqueFilename = async (baseName: string): Promise => { } return filename; -} +}; const ensureScriptsDirectory = async () => { const scriptsDir = await SCRIPTS_DIR(); if (!existsSync(scriptsDir)) { await mkdir(scriptsDir, { recursive: true }); } -} +}; const ensureHostScriptsDirectory = async () => { const hostProjectDir = process.env.HOST_PROJECT_DIR || process.cwd(); @@ -50,7 +50,7 @@ const ensureHostScriptsDirectory = async () => { if (!existsSync(hostScriptsDir)) { await mkdir(hostScriptsDir, { recursive: true }); } -} +}; const saveScriptFile = async (filename: string, content: string) => { const isDocker = process.env.DOCKER === "true"; @@ -59,7 +59,13 @@ const saveScriptFile = async (filename: string, content: string) => { const scriptPath = join(scriptsDir, filename); await writeFile(scriptPath, content, "utf8"); -} + + try { + await execAsync(`chmod +x "${scriptPath}"`); + } catch (error) { + console.error(`Failed to set execute permissions on ${scriptPath}:`, error); + } +}; const deleteScriptFile = async (filename: string) => { const isDocker = process.env.DOCKER === "true"; @@ -68,11 +74,11 @@ const deleteScriptFile = async (filename: string) => { if (existsSync(scriptPath)) { await unlink(scriptPath); } -} +}; export const fetchScripts = async (): Promise => { return await loadAllScripts(); -} +}; export const createScript = async ( formData: FormData @@ -120,7 +126,7 @@ export const createScript = async ( console.error("Error creating script:", error); return { success: false, message: "Error creating script" }; } -} +}; export const updateScript = async ( formData: FormData @@ -159,7 +165,7 @@ export const updateScript = async ( console.error("Error updating script:", error); return { success: false, message: "Error updating script" }; } -} +}; export const deleteScript = async ( id: string @@ -180,7 +186,7 @@ export const deleteScript = async ( console.error("Error deleting script:", error); return { success: false, message: "Error deleting script" }; } -} +}; export const cloneScript = async ( id: string, @@ -230,7 +236,7 @@ export const cloneScript = async ( console.error("Error cloning script:", error); return { success: false, message: "Error cloning script" }; } -} +}; export const getScriptContent = async (filename: string): Promise => { try { @@ -263,9 +269,11 @@ export const getScriptContent = async (filename: string): Promise => { console.error("Error reading script content:", error); return ""; } -} +}; -export const executeScript = async (filename: string): Promise<{ +export const executeScript = async ( + filename: string +): Promise<{ success: boolean; output: string; error: string; @@ -301,4 +309,4 @@ export const executeScript = async (filename: string): Promise<{ error: error.message || "Unknown error", }; } -} +}; diff --git a/docker-compose.yml b/docker-compose.yml index 2e1865e..e4e570c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,12 @@ services: # replace root with your user - find it with: ls -asl /var/spool/cron/crontabs/ # For multiple users, use comma-separated values: HOST_CRONTAB_USER=root,user1,user2 - HOST_CRONTAB_USER=root + + # --- !! IMPORTANT !!DOCKER EXEC USER + # If you do not specify this user to be a valid user on your system, + # any cronjob containing a docker command will fail. IDEALLY you should not be running + # docker commands as root, so this is only a fallback. ONLY ONE USER IS ALLOWED. + - DOCKER_EXEC_USER=fccview volumes: # --- MOUNT DOCKER SOCKET # Mount Docker socket to execute commands on host From cda9685e6ded1c0b97d37c07c192ab5c67a33828 Mon Sep 17 00:00:00 2001 From: fccview Date: Wed, 8 Oct 2025 14:53:56 +0100 Subject: [PATCH 3/6] Add Navino to readme table --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 30de75a..61874a3 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,9 @@ I would like to thank the following members for raising issues and help test/deb
cerede2000
+ +
Navino16
+ From f96c37b55c8cbc2d012aa06d32d9de7f0e654cbd Mon Sep 17 00:00:00 2001 From: fccview Date: Wed, 8 Oct 2025 15:01:18 +0100 Subject: [PATCH 4/6] remove debugging logs --- app/_server/actions/cronjobs/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/_server/actions/cronjobs/index.ts b/app/_server/actions/cronjobs/index.ts index 534e1c3..181b2e2 100644 --- a/app/_server/actions/cronjobs/index.ts +++ b/app/_server/actions/cronjobs/index.ts @@ -270,9 +270,6 @@ export const runCronJob = async ( let executionUser = userInfo ? userInfo.username : "root"; if (dockerExecUser && executionUser === "root") { - console.log( - `Overriding root execution. Running command as user: ${dockerExecUser}` - ); executionUser = dockerExecUser; } From 8e8069ee92fc58b2128f87826e51485752e6f72e Mon Sep 17 00:00:00 2001 From: fccview Date: Wed, 8 Oct 2025 15:44:31 +0100 Subject: [PATCH 5/6] remove docker exec user and update readme with a note for docker compose as root --- README.md | 38 +++++++++++++------------- app/_server/actions/cronjobs/index.ts | 10 ++----- docker-compose.yml | 6 ---- public/repo-images/discord_icon.webp | Bin 0 -> 4084 bytes 4 files changed, 21 insertions(+), 33 deletions(-) create mode 100644 public/repo-images/discord_icon.webp diff --git a/README.md b/README.md index 61874a3..e4bba29 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,6 @@

-# ATTENTION BREAKING UPDATE!! - -> The latest `main` branch has completely changed the way this app used to run. -> The main reason being trying to address some security concerns and make the whole application work -> across multiple platform without too much trouble. -> -> If you came here due to this change trying to figure out why your app stopped working you have two options: -> -> 1 - Update your `docker-compose.yml` with the new one provided within this readme (or just copy [docker-compose.yml](docker-compose.yml)) -> -> 2 - Keep your `docker-compose.yml` file as it is and use the legacy tag in the image `image: ghcr.io/fccview/cronmaster:legacy`. However bear in mind this will not be supported going forward, any issue regarding the legacy tag will be ignored and I will only support the main branch. Feel free to fork that specific branch in case you want to work on it yourself :) - ## Features - **Modern UI**: Beautiful, responsive interface with dark/light mode. @@ -23,6 +11,23 @@ - **Docker Support**: Runs entirely from a Docker container. - **Easy Setup**: Quick presets for common cron schedules. +
+ +--- + +

+ + + +
+ Join the discord server for more info +
+

+ +--- + +
+ ## Before we start Hey there! 👋 Just a friendly heads-up: I'm a big believer in open source and love sharing my work with the community. Everything you find in my GitHub repos is and always will be 100% free. If someone tries to sell you a "premium" version of any of my projects while claiming to be me, please know that this is not legitimate. 🚫 @@ -72,12 +77,6 @@ services: # replace root with your user - find it with: ls -asl /var/spool/cron/crontabs/ # For multiple users, use comma-separated values: HOST_CRONTAB_USER=root,user1,user2 - HOST_CRONTAB_USER=root - - # --- !! IMPORTANT !!DOCKER EXEC USER - # If you do not specify this user to be a valid user on your system, - # any cronjob containing a docker command will fail. IDEALLY you should not be running - # docker commands as root, so this is only a fallback. ONLY ONE USER IS ALLOWED. - - DOCKER_EXEC_USER=fccview volumes: # --- MOUNT DOCKER SOCKET # Mount Docker socket to execute commands on host @@ -153,7 +152,6 @@ The following environment variables can be configured: | `DOCKER` | `false` | ONLY set this to true if you are runnign the app via docker, in the docker-compose.yml file | | `HOST_CRONTAB_USER` | `root` | Comma separated list of users that run cronjobs on your host machine | | `AUTH_PASSWORD` | `N/A` | If you set a password the application will be password protected with basic next-auth | -| `DOCKER_EXEC_USER` | `N/A` | If you don't set this user you won't be able to run docker commands as root. | **Example**: To change the clock update interval to 60 seconds: @@ -173,6 +171,8 @@ HOST_PROJECT_DIR=/home//homelab/cronmaster - `HOST_PROJECT_DIR` is required in order for the scripts created within the app to run properly - The `DOCKER=true` environment variable enables direct file access mode for crontab operations. This is REQUIRED when running the application in docker mode. +**Please Note**: If you want to run `docker compose` commands as `root` within your cron jobs (highly discouraged btw), you will need to install Docker with root privileges. Otherwise, `docker compose` won't be found when executing commands. + ## Usage ### Viewing System Information diff --git a/app/_server/actions/cronjobs/index.ts b/app/_server/actions/cronjobs/index.ts index 181b2e2..effa712 100644 --- a/app/_server/actions/cronjobs/index.ts +++ b/app/_server/actions/cronjobs/index.ts @@ -265,15 +265,9 @@ export const runCronJob = async ( if (isDocker) { const userInfo = await getUserInfo(job.user); - const dockerExecUser = process.env.DOCKER_EXEC_USER; - - let executionUser = userInfo ? userInfo.username : "root"; - - if (dockerExecUser && executionUser === "root") { - executionUser = dockerExecUser; - } - + const executionUser = userInfo ? userInfo.username : "root"; const escapedCommand = job.command.replace(/'/g, "'\\''"); + command = `nsenter -t 1 -m -u -i -n -p su - ${executionUser} -c '${escapedCommand}'`; } diff --git a/docker-compose.yml b/docker-compose.yml index e4e570c..2e1865e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,12 +23,6 @@ services: # replace root with your user - find it with: ls -asl /var/spool/cron/crontabs/ # For multiple users, use comma-separated values: HOST_CRONTAB_USER=root,user1,user2 - HOST_CRONTAB_USER=root - - # --- !! IMPORTANT !!DOCKER EXEC USER - # If you do not specify this user to be a valid user on your system, - # any cronjob containing a docker command will fail. IDEALLY you should not be running - # docker commands as root, so this is only a fallback. ONLY ONE USER IS ALLOWED. - - DOCKER_EXEC_USER=fccview volumes: # --- MOUNT DOCKER SOCKET # Mount Docker socket to execute commands on host diff --git a/public/repo-images/discord_icon.webp b/public/repo-images/discord_icon.webp new file mode 100644 index 0000000000000000000000000000000000000000..365ad8153eadd77af91516fc036cf5d9322384dd GIT binary patch literal 4084 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 Date: Wed, 8 Oct 2025 16:03:02 +0100 Subject: [PATCH 6/6] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e4bba29..c4d5099 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ HOST_PROJECT_DIR=/home//homelab/cronmaster - `HOST_PROJECT_DIR` is required in order for the scripts created within the app to run properly - The `DOCKER=true` environment variable enables direct file access mode for crontab operations. This is REQUIRED when running the application in docker mode. -**Please Note**: If you want to run `docker compose` commands as `root` within your cron jobs (highly discouraged btw), you will need to install Docker with root privileges. Otherwise, `docker compose` won't be found when executing commands. +**Important Note on Root Commands**: When running commands as `root` within Cronmaster, ensure that these commands also function correctly as `root` on your host machine. If a command works as `root` on your host but fails within Cronmaster, please open an issue with detailed information. ## Usage