4 Commits

Author SHA1 Message Date
fccview
d21bed64fe edit readme to push to pr 2025-10-08 14:44:15 +01:00
fccview
8329c0d030 make it so root works via a user instead, also update permissions for scripts on creation so they are executable right off the bat 2025-10-08 14:34:34 +01:00
Nathan JAUNET
6e34474993 Preserve user permission for command execution 2025-10-02 17:23:44 +02:00
fccview
65ac81d97c Merge pull request #40 from fccview/bugfix/fix-scripts-issues
Bugfix/fix scripts issues
2025-09-20 21:14:07 +01:00
5 changed files with 47 additions and 18 deletions

1
.gitignore vendored
View File

@@ -11,5 +11,6 @@ node_modules
.vscode
.DS_Store
.cursorignore
.idea
tsconfig.tsbuildinfo
docker-compose.test.yml

View File

@@ -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:

View File

@@ -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 --setuid=${userInfo.uid} --setgid=${userInfo.gid} 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, {

View File

@@ -21,7 +21,7 @@ const sanitizeScriptName = (name: string): string => {
.replace(/-+/g, "-")
.replace(/^-|-$/g, "")
.substring(0, 50);
}
};
const generateUniqueFilename = async (baseName: string): Promise<string> => {
const scripts = await loadAllScripts();
@@ -34,14 +34,14 @@ const generateUniqueFilename = async (baseName: string): Promise<string> => {
}
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<Script[]> => {
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<string> => {
try {
@@ -263,9 +269,11 @@ export const getScriptContent = async (filename: string): Promise<string> => {
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",
};
}
}
};

View File

@@ -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