Skip to content

Commit 3ce9e7a

Browse files
authored
Merge pull request #217 from mook-as/hyperkit-sudo
Hyperkit: sudo prompt to set hyperkit exe permissions
2 parents 6cfc88c + 033c754 commit 3ce9e7a

1 file changed

Lines changed: 44 additions & 0 deletions

File tree

src/k8s-engine/hyperkit.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import stream from 'stream';
99
import timers from 'timers';
1010
import util from 'util';
1111

12+
import Electron from 'electron';
1213
import fetch from 'node-fetch';
1314
import XDGAppPaths from 'xdg-app-paths';
15+
import { exec as sudo } from 'sudo-prompt';
1416

1517
import { Settings } from '../config/settings';
1618
import resources from '../resources';
@@ -19,6 +21,8 @@ import * as K8s from './k8s';
1921
import K3sHelper from './k3sHelper';
2022

2123
const paths = XDGAppPaths('rancher-desktop');
24+
/** The GID of the 'admin' group on macOS */
25+
const adminGroup = 80;
2226

2327
/**
2428
* The possible states for the docker-machine driver.
@@ -135,6 +139,44 @@ export default class HyperkitBackend extends events.EventEmitter implements K8s.
135139
return this.dockerMachineConfig.then(v => v?.Driver.Memory ?? 0);
136140
}
137141

142+
/**
143+
* Ensure that Hyperkit and associated binaries have the correct owner / is
144+
* set as suid.
145+
*/
146+
protected async ensureHyperkitOwnership() {
147+
const commands = [];
148+
// Check that the hyperkit driver is owned by root
149+
const { driver: driverExecutable } = this.hyperkitArgs;
150+
const { uid, mode } = await fs.promises.stat(driverExecutable);
151+
152+
if (uid !== 0) {
153+
commands.push(`chown root "${ driverExecutable }"`);
154+
}
155+
if ((mode & 0o4000) === 0) {
156+
commands.push(`chmod u+s "${ driverExecutable }"`);
157+
}
158+
159+
// Check that the hyperkit binary is in the 'admin' group
160+
const hyperkitExecutable = resources.executable('hyperkit');
161+
const { gid: hyperkitGid } = await fs.promises.stat(hyperkitExecutable);
162+
163+
if (hyperkitGid !== adminGroup) {
164+
commands.push(`chown :admin "${ hyperkitExecutable }"`);
165+
}
166+
167+
if (commands.length > 0) {
168+
const command = `sh -c '${ commands.join(' && ') }'`;
169+
const options = { name: Electron.app.name };
170+
171+
console.log(command);
172+
await new Promise<void>((resolve, reject) => {
173+
sudo(command, options, (error, stdout, stderr) => {
174+
return error ? reject(error) : resolve();
175+
});
176+
});
177+
}
178+
}
179+
138180
protected get hyperkitArgs(): { driver: string, defaultArgs: string[] } {
139181
return {
140182
driver: resources.executable('docker-machine-driver-hyperkit'),
@@ -291,6 +333,7 @@ export default class HyperkitBackend extends events.EventEmitter implements K8s.
291333

292334
await Promise.all([
293335
this.ensureImage(),
336+
this.ensureHyperkitOwnership(),
294337
this.k3sHelper.ensureK3sImages(desiredVersion),
295338
]);
296339

@@ -374,6 +417,7 @@ export default class HyperkitBackend extends events.EventEmitter implements K8s.
374417
async stop(): Promise<number> {
375418
try {
376419
this.setState(K8s.State.STOPPING);
420+
await this.ensureHyperkitOwnership();
377421
this.process?.kill('SIGTERM');
378422
if (await this.vmState === DockerMachineDriverState.Running) {
379423
await this.hyperkit('stop');

0 commit comments

Comments
 (0)