mirror of https://github.com/cirruslabs/tart.git
Try to set a SUID bit on Softnet using Sudo before failing (#421)
* Try to set a SUID bit on Softnet using Sudo before failing * .cirrus.yml: switch to the new Mac machine
This commit is contained in:
parent
9f1f3f1b40
commit
2be5eedeb6
|
|
@ -5,7 +5,7 @@ task:
|
|||
alias: test
|
||||
persistent_worker:
|
||||
labels:
|
||||
name: scaleway-m1
|
||||
name: dev-mini
|
||||
test_script:
|
||||
- swift test
|
||||
integration_test_script:
|
||||
|
|
|
|||
|
|
@ -98,6 +98,10 @@ struct Run: AsyncParsableCommand {
|
|||
func run() async throws {
|
||||
let vmDir = try VMStorageLocal().open(name)
|
||||
|
||||
if netSoftnet && isInteractiveSession() {
|
||||
try Softnet.configureSUIDBitIfNeeded()
|
||||
}
|
||||
|
||||
let additionalDiskAttachments = try additionalDiskAttachments()
|
||||
|
||||
// Error out if the disk is locked by the host (e.g. it was mounted in Finder),
|
||||
|
|
@ -194,6 +198,10 @@ struct Run: AsyncParsableCommand {
|
|||
}
|
||||
}
|
||||
|
||||
func isInteractiveSession() -> Bool {
|
||||
isatty(STDOUT_FILENO) == 1
|
||||
}
|
||||
|
||||
func userSpecifiedNetwork(vmDir: VMDirectory) throws -> Network? {
|
||||
if netSoftnet {
|
||||
let config = try VMConfig.init(fromURL: vmDir.configURL)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import Foundation
|
||||
import Virtualization
|
||||
import Atomics
|
||||
import System
|
||||
|
||||
enum SoftnetError: Error {
|
||||
case InitializationFailed(why: String)
|
||||
|
|
@ -15,12 +16,6 @@ class Softnet: Network {
|
|||
let vmFD: Int32
|
||||
|
||||
init(vmMACAddress: String) throws {
|
||||
let binaryName = "softnet"
|
||||
|
||||
guard let executableURL = resolveBinaryPath(binaryName) else {
|
||||
throw SoftnetError.InitializationFailed(why: "\(binaryName) not found in PATH")
|
||||
}
|
||||
|
||||
let fds = UnsafeMutablePointer<Int32>.allocate(capacity: MemoryLayout<Int>.stride * 2)
|
||||
|
||||
let ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, fds)
|
||||
|
|
@ -34,11 +29,21 @@ class Softnet: Network {
|
|||
try setSocketBuffers(vmFD, 1 * 1024 * 1024);
|
||||
try setSocketBuffers(softnetFD, 1 * 1024 * 1024);
|
||||
|
||||
process.executableURL = executableURL
|
||||
process.executableURL = try Self.softnetExecutableURL()
|
||||
process.arguments = ["--vm-fd", String(STDIN_FILENO), "--vm-mac-address", vmMACAddress]
|
||||
process.standardInput = FileHandle(fileDescriptor: softnetFD, closeOnDealloc: false)
|
||||
}
|
||||
|
||||
static func softnetExecutableURL() throws -> URL {
|
||||
let binaryName = "softnet"
|
||||
|
||||
guard let executableURL = resolveBinaryPath(binaryName) else {
|
||||
throw SoftnetError.InitializationFailed(why: "\(binaryName) not found in PATH")
|
||||
}
|
||||
|
||||
return executableURL
|
||||
}
|
||||
|
||||
func run(_ sema: DispatchSemaphore) throws {
|
||||
try process.run()
|
||||
|
||||
|
|
@ -91,4 +96,63 @@ class Softnet: Network {
|
|||
let fh = FileHandle.init(fileDescriptor: vmFD)
|
||||
return VZFileHandleNetworkDeviceAttachment(fileHandle: fh)
|
||||
}
|
||||
|
||||
static func configureSUIDBitIfNeeded() throws {
|
||||
// Obtain the Softnet executable path
|
||||
//
|
||||
// It's important to use resolvingSymlinksInPath() here, because otherwise
|
||||
// we will get something like "/opt/homebrew/bin/softnet" instead of
|
||||
// "/opt/homebrew/Cellar/softnet/0.6.2/bin/softnet"
|
||||
let softnetExecutablePath = try Softnet.softnetExecutableURL().resolvingSymlinksInPath().path
|
||||
|
||||
// Check if the SUID bit is already configured
|
||||
let info = try FileManager.default.attributesOfItem(atPath: softnetExecutablePath) as NSDictionary
|
||||
if info.fileOwnerAccountID() == 0 && (info.filePosixPermissions() & Int(S_ISUID)) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the passwordless Sudo is already configured for Softnet
|
||||
let sudoBinaryName = "sudo"
|
||||
|
||||
guard let sudoExecutableURL = resolveBinaryPath(sudoBinaryName) else {
|
||||
throw SoftnetError.InitializationFailed(why: "\(sudoBinaryName) not found in PATH")
|
||||
}
|
||||
|
||||
var process = Process()
|
||||
process.executableURL = sudoExecutableURL
|
||||
process.arguments = ["--non-interactive", "softnet", "--help"]
|
||||
process.standardInput = nil
|
||||
process.standardOutput = nil
|
||||
process.standardError = nil
|
||||
try process.run()
|
||||
process.waitUntilExit()
|
||||
if process.terminationStatus == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Configure the SUID bit by spawning the Sudo process in interactive mode
|
||||
// and asking the user for password required to run chown & chmod
|
||||
fputs("Softnet requires a Sudo password to set the SUID bit on the Softnet executable, please enter it below.\n",
|
||||
stderr)
|
||||
|
||||
process = try Process.run(sudoExecutableURL, arguments: [
|
||||
"sh",
|
||||
"-c",
|
||||
"chown root \(softnetExecutablePath) && chmod u+s \(softnetExecutablePath)",
|
||||
])
|
||||
|
||||
// Set TTY's foreground process group to that of the Sudo process,
|
||||
// otherwise it will get stopped by a SIGTTIN once user input arrives
|
||||
if tcsetpgrp(STDIN_FILENO, process.processIdentifier) == -1 {
|
||||
let details = Errno(rawValue: CInt(errno))
|
||||
|
||||
throw RuntimeError.SoftnetFailed("tcsetpgrp(2) failed: \(details)")
|
||||
}
|
||||
|
||||
process.waitUntilExit()
|
||||
|
||||
if process.terminationStatus != 0 {
|
||||
throw RuntimeError.SoftnetFailed("failed to configure SUID bit on Softnet executable with Sudo")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ enum RuntimeError : Error {
|
|||
case VMDirectoryAlreadyInitialized(_ message: String)
|
||||
case ExportFailed(_ message: String)
|
||||
case ImportFailed(_ message: String)
|
||||
case SoftnetFailed(_ message: String)
|
||||
}
|
||||
|
||||
protocol HasExitCode {
|
||||
|
|
@ -92,6 +93,8 @@ extension RuntimeError : CustomStringConvertible {
|
|||
return "VM export failed: \(message)"
|
||||
case .ImportFailed(let message):
|
||||
return "VM import failed: \(message)"
|
||||
case .SoftnetFailed(let message):
|
||||
return "Softnet failed: \(message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue