mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 15:24:52 +09:00
Create config files with mode 0o600 (#1017)
Config files hold the auth secret and tokens, so the default 0o644 lets every user on the system read them. Config.save, the ctl config written by the controller bootstrap, ctl host create-config and the config migration in create now pass mode 0o600. safeOutputFile also applies the mode to the temporary file it writes, since writeFile only honours mode on creation and a leftover temporary from a failed write would otherwise keep its old mode. Closes #1005 Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
8c03ac15ab
commit
d4b6df4f63
7 files changed
+27
-4
No files matched your search
@@ -206,7 +206,7 @@ async function handleBootstrapCommand(
|
||||
console.log(content);
|
||||
} else {
|
||||
logger.info(`Writing ${args.output}`);
|
||||
await lib.safeOutputFile(args.output, content);
|
||||
await lib.safeOutputFile(args.output, content, { mode: 0o600 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ async function migrateRename(args) {
|
||||
if (await pathExists(source) && !await pathExists(destination)) {
|
||||
await safeOutputFile(destination, JSON.stringify(
|
||||
renameConfig(JSON.parse(await fs.readFile(source))), null, "\t"
|
||||
));
|
||||
), { mode: 0o600 });
|
||||
logger.info(`Migrated ${source} to ${destination}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ hostCommands.add(new lib.Command({
|
||||
} else {
|
||||
logger.info(`Writing ${args.output}`);
|
||||
try {
|
||||
await fs.writeFile(args.output, content, { flag: "wx" });
|
||||
await fs.writeFile(args.output, content, { flag: "wx", mode: 0o600 });
|
||||
} catch (err: any) {
|
||||
if (err.code === "EEXIST") {
|
||||
throw new lib.CommandError(`File ${args.output} already exists`);
|
||||
|
||||
@@ -468,7 +468,7 @@ export class Config<
|
||||
if (!this.filepath) {
|
||||
throw new Error("Cannot save config which has no filepath");
|
||||
}
|
||||
await safeOutputFile(this.filepath, JSON.stringify(this, null, "\t"));
|
||||
await safeOutputFile(this.filepath, JSON.stringify(this, null, "\t"), { mode: 0o600 });
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +168,10 @@ export async function safeOutputFile(
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if (options?.mode !== undefined) {
|
||||
// writeFile only applies mode when creating, a leftover temporary keeps its old mode.
|
||||
await fs.chmod(temporary, options.mode);
|
||||
}
|
||||
await fs.rename(temporary, file);
|
||||
}
|
||||
|
||||
|
||||
@@ -395,6 +395,15 @@ describe("lib/config/classes", function() {
|
||||
const json = JSON.parse(await fs.readFile(filepath, "utf8"));
|
||||
assert.deepEqual(json, testInstance.toJSON());
|
||||
});
|
||||
it("should not be readable by other users", async function() {
|
||||
if (process.platform === "win32") {
|
||||
this.skip();
|
||||
}
|
||||
const testInstance = new TestConfig("local", { "alpha.foo": "a" }, filepath);
|
||||
testInstance.set("alpha.foo", "b"); // Sets the dirty flag
|
||||
await testInstance.save();
|
||||
assert.equal((await fs.stat(filepath)).mode & 0o777, 0o600);
|
||||
});
|
||||
it("should clear the dirty flag after saving", async function() {
|
||||
const testInstance = new TestConfig("local", { "alpha.foo": "a" }, filepath);
|
||||
testInstance.set("alpha.foo", "b"); // Sets the dirty flag
|
||||
|
||||
@@ -100,6 +100,16 @@ describe("lib/file_ops", function() {
|
||||
await assert.rejects(fs.access(target.replace(".txt", ".tmp.txt")), "temporary was left behind");
|
||||
assert.equal(await fs.readFile(target, "utf8"), "current");
|
||||
});
|
||||
it("should apply mode to a leftover temporary file", async function() {
|
||||
if (process.platform === "win32") {
|
||||
this.skip();
|
||||
}
|
||||
let target = path.join(baseDir, "safe", "mode.txt");
|
||||
await fs.writeFile(target.replace(".txt", ".tmp.txt"), "stale", { mode: 0o644 });
|
||||
await lib.safeOutputFile(target, "private", { mode: 0o600 });
|
||||
assert.equal((await fs.stat(target)).mode & 0o777, 0o600);
|
||||
assert.equal(await fs.readFile(target, "utf8"), "private");
|
||||
});
|
||||
it("should handle creating file in current working directory", async function() {
|
||||
let target = "temporary-file-made-to-test-cwd.txt";
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user