mirror of
https://github.com/apple/container.git
synced 2026-07-15 05:57:01 +00:00
57b07fa2ac
Closes https://github.com/apple/container/issues/1687 The default kernel archive is downloaded from a remote release URL during first-run setup and via `container system kernel set --recommended`. Previously, the archive contents were not verified after download, so integrity depended on HTTPS and the release artifact remaining unchanged. This change adds digest verification for kernel archives. The recommended/default kernel now has pinned digest metadata using an algorithm-prefixed value such as `sha256:<hex>`. `container system kernel set --tar` accepts `--digest`; remote tar URLs require it, and local tar archives can also be verified before unpacking and installation. The system config also supports `kernel.digest`, and a custom `kernel.url` must provide a digest for that archive.
112 lines
4.0 KiB
Swift
112 lines
4.0 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
// Copyright © 2026 Apple Inc. and the container project authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import ContainerXPC
|
|
import Containerization
|
|
import ContainerizationError
|
|
import ContainerizationOCI
|
|
import Foundation
|
|
import TerminalProgress
|
|
|
|
public struct ClientKernel {
|
|
static let serviceIdentifier = "com.apple.container.apiserver"
|
|
}
|
|
|
|
extension ClientKernel {
|
|
private static func newClient() -> XPCClient {
|
|
XPCClient(service: serviceIdentifier)
|
|
}
|
|
|
|
public static func installKernel(kernelFilePath: String, platform: SystemPlatform, force: Bool) async throws {
|
|
let client = newClient()
|
|
let message = XPCMessage(route: .installKernel)
|
|
|
|
message.set(key: .kernelFilePath, value: kernelFilePath)
|
|
message.set(key: .kernelForce, value: force)
|
|
|
|
let platformData = try JSONEncoder().encode(platform)
|
|
message.set(key: .systemPlatform, value: platformData)
|
|
try await client.send(message)
|
|
}
|
|
|
|
public static func installKernelFromTar(
|
|
tarFile: String,
|
|
kernelFilePath: String,
|
|
platform: SystemPlatform,
|
|
progressUpdate: ProgressUpdateHandler? = nil,
|
|
expectedDigest: String? = nil,
|
|
force: Bool
|
|
) async throws {
|
|
let client = newClient()
|
|
let message = XPCMessage(route: .installKernel)
|
|
|
|
message.set(key: .kernelTarURL, value: tarFile)
|
|
message.set(key: .kernelFilePath, value: kernelFilePath)
|
|
message.set(key: .kernelForce, value: force)
|
|
if let expectedDigest {
|
|
message.set(key: .kernelDigest, value: expectedDigest)
|
|
}
|
|
|
|
let platformData = try JSONEncoder().encode(platform)
|
|
message.set(key: .systemPlatform, value: platformData)
|
|
|
|
var progressUpdateClient: ProgressUpdateClient?
|
|
if let progressUpdate {
|
|
progressUpdateClient = await ProgressUpdateClient(for: progressUpdate, request: message)
|
|
}
|
|
|
|
try await client.send(message)
|
|
await progressUpdateClient?.finish()
|
|
}
|
|
|
|
@discardableResult
|
|
public static func getDefaultKernel(for platform: SystemPlatform) async throws -> Kernel {
|
|
let client = newClient()
|
|
let message = XPCMessage(route: .getDefaultKernel)
|
|
|
|
let platformData = try JSONEncoder().encode(platform)
|
|
message.set(key: .systemPlatform, value: platformData)
|
|
do {
|
|
let reply = try await client.send(message)
|
|
guard let kData = reply.dataNoCopy(key: .kernel) else {
|
|
throw ContainerizationError(.internalError, message: "missing kernel data from XPC response")
|
|
}
|
|
|
|
let kernel = try JSONDecoder().decode(Kernel.self, from: kData)
|
|
return kernel
|
|
} catch let err as ContainerizationError {
|
|
guard err.isCode(.notFound) else {
|
|
throw err
|
|
}
|
|
throw ContainerizationError(
|
|
.notFound, message: "default kernel not configured for architecture \(platform.architecture), please use the `container system kernel set` command to configure it")
|
|
}
|
|
}
|
|
}
|
|
|
|
extension SystemPlatform {
|
|
public static var current: SystemPlatform {
|
|
switch Platform.current.architecture {
|
|
case "arm64":
|
|
return .linuxArm
|
|
case "amd64":
|
|
return .linuxAmd
|
|
default:
|
|
fatalError("unknown architecture")
|
|
}
|
|
}
|
|
}
|