ZFS and sudo permissions related improvements
This commit is contained in:
parent
20b3fc249f
commit
8ef05936d8
|
|
@ -15,6 +15,15 @@ zfs:
|
|||
# the example below is useful for TrueNAS 12
|
||||
#cli:
|
||||
# sudoEnabled: true
|
||||
#
|
||||
# instead of setting sudoEnabled to true, sudo can be
|
||||
# reduced only to specific (supported) zfs commands
|
||||
# sudoEnabledCommands:
|
||||
# - mount
|
||||
# - unmount
|
||||
# - share
|
||||
# - unshare
|
||||
#
|
||||
# paths:
|
||||
# zfs: /usr/local/sbin/zfs
|
||||
# zpool: /usr/local/sbin/zpool
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
|
|||
}
|
||||
|
||||
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
||||
options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []);
|
||||
|
||||
if (typeof this.setZetabyteCustomOptions === "function") {
|
||||
await this.setZetabyteCustomOptions(options);
|
||||
|
|
@ -79,6 +80,37 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
|
|||
return name;
|
||||
}
|
||||
|
||||
async setShareProperty(zb, datasetName, propertyDict) {
|
||||
try {
|
||||
await zb.zfs.set(datasetName, propertyDict);
|
||||
} catch (err) {
|
||||
if (
|
||||
err.toString().includes("unable to reshare") &&
|
||||
zb.options.sudoCommands.includes("mount")
|
||||
) {
|
||||
await zb.zfs.share(datasetName);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async unsetShareProperty(zb, datasetName, propertyKey) {
|
||||
if (zb.options.sudoCommands.includes("unshare")) {
|
||||
await zb.zfs.unshare(datasetName, { idempotent: true });
|
||||
}
|
||||
|
||||
try {
|
||||
await zb.zfs.inherit(datasetName, propertyKey);
|
||||
} catch (err) {
|
||||
if (err.toString().includes("dataset does not exist")) {
|
||||
// do nothing
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* should create any necessary share resources
|
||||
* should set the SHARE_VOLUME_CONTEXT_PROPERTY_NAME propery
|
||||
|
|
@ -105,7 +137,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
|
|||
key
|
||||
]
|
||||
) {
|
||||
await zb.zfs.set(datasetName, {
|
||||
await this.setShareProperty(zb, datasetName, {
|
||||
[key]:
|
||||
this.options.nfs.shareStrategySetDatasetProperties
|
||||
.properties[key],
|
||||
|
|
@ -139,7 +171,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
|
|||
key
|
||||
]
|
||||
) {
|
||||
await zb.zfs.set(datasetName, {
|
||||
await this.setShareProperty(zb, datasetName, {
|
||||
[key]:
|
||||
this.options.smb.shareStrategySetDatasetProperties
|
||||
.properties[key],
|
||||
|
|
@ -316,15 +348,7 @@ create /backstores/block/${iscsiName}
|
|||
key
|
||||
]
|
||||
) {
|
||||
try {
|
||||
await zb.zfs.inherit(datasetName, key);
|
||||
} catch (err) {
|
||||
if (err.toString().includes("dataset does not exist")) {
|
||||
// do nothing
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
await this.unsetShareProperty(zb, datasetName, key);
|
||||
}
|
||||
}
|
||||
await GeneralUtils.sleep(2000); // let things settle
|
||||
|
|
@ -346,15 +370,7 @@ create /backstores/block/${iscsiName}
|
|||
key
|
||||
]
|
||||
) {
|
||||
try {
|
||||
await zb.zfs.inherit(datasetName, key);
|
||||
} catch (err) {
|
||||
if (err.toString().includes("dataset does not exist")) {
|
||||
// do nothing
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
await this.unsetShareProperty(zb, datasetName, key);
|
||||
}
|
||||
}
|
||||
await GeneralUtils.sleep(2000); // let things settle
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ class ControllerZfsLocalDriver extends ControllerZfsBaseDriver {
|
|||
};
|
||||
|
||||
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
||||
options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []);
|
||||
|
||||
if (typeof this.setZetabyteCustomOptions === "function") {
|
||||
await this.setZetabyteCustomOptions(options);
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
|
|||
driver.ctx.logger.verbose("whoami command: %s", command);
|
||||
const response = await execClient.exec(command);
|
||||
if (response.code !== 0) {
|
||||
throw new Error("failed to run uname to determine max zvol name length");
|
||||
throw new Error("failed to run whoami to determine name of user");
|
||||
} else {
|
||||
return response.stdout.trim();
|
||||
}
|
||||
|
|
@ -475,9 +475,10 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
|
|||
async setFilesystemMode(path, mode) {
|
||||
const driver = this;
|
||||
const execClient = this.getExecClient();
|
||||
const sudoEnabled = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
||||
|
||||
let command = execClient.buildCommand("chmod", [mode, path]);
|
||||
if ((await driver.getWhoAmI()) != "root") {
|
||||
if ((await driver.getWhoAmI()) != "root" && sudoEnabled) {
|
||||
command = (await driver.getSudoPath()) + " " + command;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
|
|||
}
|
||||
|
||||
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
||||
options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []);
|
||||
|
||||
if (typeof this.setZetabyteCustomOptions === "function") {
|
||||
await this.setZetabyteCustomOptions(options);
|
||||
|
|
|
|||
180
src/utils/zfs.js
180
src/utils/zfs.js
|
|
@ -904,6 +904,13 @@ class Zetabyte {
|
|||
};
|
||||
|
||||
zb.zfs = {
|
||||
SUDO_COMMANDS: [
|
||||
"mount",
|
||||
"unmount",
|
||||
"share",
|
||||
"unshare"
|
||||
],
|
||||
|
||||
/**
|
||||
* zfs create [-pu] [-o property=value]... filesystem
|
||||
* zfs create [-ps] [-b blocksize] [-o property=value]... -V size volume
|
||||
|
|
@ -913,6 +920,7 @@ class Zetabyte {
|
|||
*/
|
||||
create: function (dataset, options = {}) {
|
||||
if (!(arguments.length >= 1)) throw new (Error("Invalid arguments"))();
|
||||
const zfs = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const idempotent =
|
||||
|
|
@ -921,11 +929,12 @@ class Zetabyte {
|
|||
: "idempotent" in zb.options
|
||||
? zb.options.idempotent
|
||||
: false;
|
||||
const shouldSudoMount = !zb.options.sudo && zb.options.sudoCommands.includes("mount");
|
||||
|
||||
let args = [];
|
||||
args.push("create");
|
||||
if (options.parents) args.push("-p");
|
||||
if (options.unmounted) args.push("-u");
|
||||
if (options.unmounted || shouldSudoMount) args.push("-u");
|
||||
if (options.blocksize) args = args.concat(["-b", options.blocksize]);
|
||||
if (options.properties) {
|
||||
for (let [key, value] of Object.entries(options.properties)) {
|
||||
|
|
@ -945,10 +954,18 @@ class Zetabyte {
|
|||
if (
|
||||
error &&
|
||||
!(idempotent && stderr.includes("dataset already exists"))
|
||||
)
|
||||
) {
|
||||
return reject(zb.helpers.zfsError(error, stderr));
|
||||
}
|
||||
|
||||
if (shouldSudoMount) {
|
||||
zfs.mount(dataset, { idempotent })
|
||||
.then(out => resolve(stdout + "; " + out))
|
||||
.catch(err => reject(err));
|
||||
} else {
|
||||
return resolve(stdout);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
|
@ -964,6 +981,7 @@ class Zetabyte {
|
|||
*/
|
||||
destroy: function (dataset, options = {}) {
|
||||
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
|
||||
const zfs = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const idempotent =
|
||||
|
|
@ -985,6 +1003,7 @@ class Zetabyte {
|
|||
if (options.defer) args.push("-d");
|
||||
args.push(dataset);
|
||||
|
||||
const destroyExec = function() {
|
||||
zb.exec(
|
||||
zb.options.paths.zfs,
|
||||
args,
|
||||
|
|
@ -1002,6 +1021,14 @@ class Zetabyte {
|
|||
return resolve(stdout);
|
||||
}
|
||||
);
|
||||
};
|
||||
if (zb.options.sudoCommands.includes("unmount")) {
|
||||
zfs.unmount(dataset, { idempotent })
|
||||
.then(() => destroyExec())
|
||||
.catch(err => reject(err));
|
||||
} else {
|
||||
destroyExec();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -1224,7 +1251,7 @@ class Zetabyte {
|
|||
let args = [];
|
||||
args.push("rename");
|
||||
if (options.parents) args.push("-p");
|
||||
if (options.unmounted) args.push("-u");
|
||||
if (options.unmounted || (!zb.options.sudo && zb.options.sudoCommands.includes("mount"))) args.push("-u");
|
||||
if (options.force) args.push("-f");
|
||||
if (options.recurse) args.push("-r");
|
||||
args.push(source);
|
||||
|
|
@ -1522,6 +1549,145 @@ class Zetabyte {
|
|||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* zfs mount filesystem
|
||||
*
|
||||
* @param {*} dataset
|
||||
* @param {*} options
|
||||
*/
|
||||
mount: function (dataset, options = {}) {
|
||||
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const idempotent =
|
||||
"idempotent" in options
|
||||
? options.idempotent
|
||||
: "idempotent" in zb.options
|
||||
? zb.options.idempotent
|
||||
: false;
|
||||
|
||||
let args = [];
|
||||
args.push("mount");
|
||||
args.push(dataset);
|
||||
|
||||
zb.exec(
|
||||
zb.options.paths.zfs,
|
||||
args,
|
||||
{ timeout: zb.options.timeout },
|
||||
function (error, stdout, stderr) {
|
||||
if (
|
||||
error &&
|
||||
!(idempotent && stderr.includes("filesystem already mounted"))
|
||||
) {
|
||||
return reject(zb.helpers.zfsError(error, stderr));
|
||||
}
|
||||
return resolve(stdout);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* zfs unmount filesystem
|
||||
*
|
||||
* @param {*} dataset
|
||||
* @param {*} options
|
||||
*/
|
||||
unmount: function (dataset, options = {}) {
|
||||
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const idempotent =
|
||||
"idempotent" in options
|
||||
? options.idempotent
|
||||
: "idempotent" in zb.options
|
||||
? zb.options.idempotent
|
||||
: false;
|
||||
|
||||
let args = [];
|
||||
args.push("unmount");
|
||||
args.push(dataset);
|
||||
|
||||
zb.exec(
|
||||
zb.options.paths.zfs,
|
||||
args,
|
||||
{ timeout: zb.options.timeout },
|
||||
function (error, stdout, stderr) {
|
||||
if (
|
||||
error &&
|
||||
!(idempotent && stderr.includes("not currently mounted"))
|
||||
) {
|
||||
return reject(zb.helpers.zfsError(error, stderr));
|
||||
}
|
||||
return resolve(stdout);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* zfs share filesystem
|
||||
*
|
||||
* @param {*} dataset
|
||||
*/
|
||||
share: function (dataset) {
|
||||
if (arguments.length != 1) throw Error("Invalid arguments");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let args = [];
|
||||
args.push("share");
|
||||
args.push(dataset);
|
||||
|
||||
zb.exec(
|
||||
zb.options.paths.zfs,
|
||||
args,
|
||||
{ timeout: zb.options.timeout },
|
||||
function (error, stdout, stderr) {
|
||||
if (error) return reject(zb.helpers.zfsError(error, stderr));
|
||||
return resolve(stdout);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* zfs unshare filesystem
|
||||
*
|
||||
* @param {*} dataset
|
||||
* @param {*} options
|
||||
*/
|
||||
unshare: function (dataset, options = {}) {
|
||||
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const idempotent =
|
||||
"idempotent" in options
|
||||
? options.idempotent
|
||||
: "idempotent" in zb.options
|
||||
? zb.options.idempotent
|
||||
: false;
|
||||
|
||||
let args = [];
|
||||
args.push("unshare");
|
||||
args.push(dataset);
|
||||
|
||||
zb.exec(
|
||||
zb.options.paths.zfs,
|
||||
args,
|
||||
{ timeout: zb.options.timeout },
|
||||
function (error, stdout, stderr) {
|
||||
if (
|
||||
error &&
|
||||
!(idempotent && stderr.includes("not currently shared"))
|
||||
) {
|
||||
return reject(zb.helpers.zfsError(error, stderr));
|
||||
}
|
||||
return resolve(stdout);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1564,7 +1730,13 @@ class Zetabyte {
|
|||
use_sudo = options.sudo;
|
||||
}
|
||||
|
||||
if (use_sudo) {
|
||||
if (
|
||||
use_sudo || (
|
||||
Array.isArray(args) && args.length > 0 &&
|
||||
zb.zfs.SUDO_COMMANDS.includes(args[0]) &&
|
||||
zb.options.sudoCommands.includes(args[0])
|
||||
)
|
||||
) {
|
||||
args = args || [];
|
||||
args.unshift(command);
|
||||
command = zb.options.paths.sudo;
|
||||
|
|
|
|||
Loading…
Reference in New Issue