Merge nucleic/quiet-opal-gecko-ah7w into dev
This commit is contained in:
+23
-2
@@ -100,6 +100,25 @@ in-tree means the patch can't be lost to a dependency re-resolve.
|
|||||||
paths close both ends so a failed connection fails FAST for the peer and leaks no fds. Marked
|
paths close both ends so a failed connection fails FAST for the peer and leaks no fds. Marked
|
||||||
`[Nucleic vendored patch]`.
|
`[Nucleic vendored patch]`.
|
||||||
|
|
||||||
|
13. **`Sources/ContainerizationOS/Keychain/KeychainQuery.swift` — prompt-free registry-credential
|
||||||
|
reads.** Upstream's `get`/`list`/`exists` call `SecItemCopyMatching` with the legacy login
|
||||||
|
Keychain's interactive authorization panel enabled, so any process that isn't on a registry
|
||||||
|
internet-password item's ACL raises the macOS *"'cctl' wants to use your confidential information
|
||||||
|
stored in 'ghcr.io' in your keychain"* panel when it reads that item — e.g. a `cctl` binary
|
||||||
|
re-signed ad-hoc by a fresh `make vminit-image` reading a GHCR token an earlier build stored, or
|
||||||
|
any tool linking `KeychainHelper.lookup` during an image pull/push/list. Nucleic's rule is that no
|
||||||
|
automatic credential lookup may ever raise a Keychain panel. This patch wraps the three
|
||||||
|
`SecItemCopyMatching` reads in `withoutInteractiveUI` (`SecKeychainSetUserInteractionAllowed(false)`
|
||||||
|
— the only switch that governs the legacy ACL/partition-list dialog; the data-protection
|
||||||
|
`kSecUseAuthenticationUI*` flags do NOT), so an already-trusted item reads silently while anything
|
||||||
|
else fails with `errSecInteractionNotAllowed` — which `isQuerySuccessful` now treats as "not found"
|
||||||
|
so the caller falls back to anonymous / `REGISTRY_HOST`/`USERNAME`/`TOKEN` env auth. `save`
|
||||||
|
(the `cctl login` write path) gains a delete-and-retry on `errSecDuplicateItem`, since the
|
||||||
|
now-silent `exists` can under-report an unreadable pre-existing item. Mirrors
|
||||||
|
`KeychainOwnedAccess.withoutLegacyKeychainUI` in NucleicCore. Host-side (shipped by a normal
|
||||||
|
`swift build`); `SecKeychain*` deprecation warnings are expected (built with
|
||||||
|
`WARNINGS_AS_ERRORS=false`). Marked `[Nucleic vendored patch]`.
|
||||||
|
|
||||||
### GUEST-side patches (require rebuilding the initfs — see below)
|
### GUEST-side patches (require rebuilding the initfs — see below)
|
||||||
|
|
||||||
Patches #1–#7 are host-side (the `Containerization` library), shipped by a normal `swift build`.
|
Patches #1–#7 are host-side (the `Containerization` library), shipped by a normal `swift build`.
|
||||||
@@ -191,8 +210,10 @@ rebuild whenever a guest patch changes. Built locally, not in CI: the host frame
|
|||||||
for `[Nucleic vendored patch]` to find every site, and patch #9 (per-exec cgroups) across
|
for `[Nucleic vendored patch]` to find every site, and patch #9 (per-exec cgroups) across
|
||||||
`Cgroup2Manager.swift` / `ManagedContainer.swift` / `ManagedProcess.swift`, patch #10
|
`Cgroup2Manager.swift` / `ManagedContainer.swift` / `ManagedProcess.swift`, patch #10
|
||||||
(`Socket.acceptStream` transient-error tolerance + `isTransientAcceptError`), patch #11 (the
|
(`Socket.acceptStream` transient-error tolerance + `isTransientAcceptError`), patch #11 (the
|
||||||
`UnixSocketRelay` per-connection containment + fail-fast closes), and patch #12 (the `VsockProxy`
|
`UnixSocketRelay` per-connection containment + fail-fast closes), patch #12 (the `VsockProxy`
|
||||||
cleanup/`try!`/listener hardening in `vminitd/`). After re-applying any
|
cleanup/`try!`/listener hardening in `vminitd/`), and patch #13 (the prompt-free
|
||||||
|
`KeychainQuery` reads: `withoutInteractiveUI` + the `errSecInteractionNotAllowed` handling +
|
||||||
|
the `save` duplicate retry). After re-applying any
|
||||||
`vminitd/` patch, rebuild + publish the custom init image
|
`vminitd/` patch, rebuild + publish the custom init image
|
||||||
with `make vminit-image` + `make vminit-image-push`, and bump `ContainerEngine.vminitReference`.
|
with `make vminit-image` + `make vminit-image-push`, and bump `ContainerEngine.vminitReference`.
|
||||||
5. Update the commit hash above and in the root `Package.swift` comment.
|
5. Update the commit hash above and in the root `Package.swift` comment.
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import FoundationEssentials
|
|||||||
#else
|
#else
|
||||||
import Foundation
|
import Foundation
|
||||||
#endif
|
#endif
|
||||||
|
import Security // [Nucleic vendored patch] SecKeychain*UserInteractionAllowed for prompt-free reads
|
||||||
|
|
||||||
/// Holds the result of a query to the keychain.
|
/// Holds the result of a query to the keychain.
|
||||||
public struct KeychainQueryResult {
|
public struct KeychainQueryResult {
|
||||||
@@ -68,7 +69,15 @@ public struct KeychainQuery {
|
|||||||
query[kSecAttrAccessGroup as String] = accessGroup
|
query[kSecAttrAccessGroup as String] = accessGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = SecItemAdd(query as CFDictionary, nil)
|
var status = SecItemAdd(query as CFDictionary, nil)
|
||||||
|
// [Nucleic vendored patch] `exists` above no longer prompts, so it can under-report an
|
||||||
|
// item this process isn't trusted to read; an explicit `login` re-save then hits
|
||||||
|
// `errSecDuplicateItem`. Deleting (no read authorization required) and retrying once keeps
|
||||||
|
// re-login working without ever raising a Keychain panel.
|
||||||
|
if status == errSecDuplicateItem {
|
||||||
|
try delete(securityDomain: securityDomain, accessGroup: accessGroup, hostname: hostname)
|
||||||
|
status = SecItemAdd(query as CFDictionary, nil)
|
||||||
|
}
|
||||||
guard status == errSecSuccess else { throw Self.Error.unhandledError(status: status) }
|
guard status == errSecSuccess else { throw Self.Error.unhandledError(status: status) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +123,7 @@ public struct KeychainQuery {
|
|||||||
query[kSecAttrAccessGroup as String] = accessGroup
|
query[kSecAttrAccessGroup as String] = accessGroup
|
||||||
}
|
}
|
||||||
var item: CFTypeRef?
|
var item: CFTypeRef?
|
||||||
let status = SecItemCopyMatching(query as CFDictionary, &item)
|
let status = Self.withoutInteractiveUI { SecItemCopyMatching(query as CFDictionary, &item) }
|
||||||
let exists = try isQuerySuccessful(status)
|
let exists = try isQuerySuccessful(status)
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil
|
return nil
|
||||||
@@ -164,7 +173,7 @@ public struct KeychainQuery {
|
|||||||
query[kSecAttrAccessGroup as String] = accessGroup
|
query[kSecAttrAccessGroup as String] = accessGroup
|
||||||
}
|
}
|
||||||
var item: CFTypeRef?
|
var item: CFTypeRef?
|
||||||
let status = SecItemCopyMatching(query as CFDictionary, &item)
|
let status = Self.withoutInteractiveUI { SecItemCopyMatching(query as CFDictionary, &item) }
|
||||||
let exists = try isQuerySuccessful(status)
|
let exists = try isQuerySuccessful(status)
|
||||||
if !exists {
|
if !exists {
|
||||||
return []
|
return []
|
||||||
@@ -217,12 +226,16 @@ public struct KeychainQuery {
|
|||||||
query[kSecAttrAccessGroup as String] = accessGroup
|
query[kSecAttrAccessGroup as String] = accessGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = SecItemCopyMatching(query as CFDictionary, nil)
|
let status = Self.withoutInteractiveUI { SecItemCopyMatching(query as CFDictionary, nil) }
|
||||||
return try isQuerySuccessful(status)
|
return try isQuerySuccessful(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isQuerySuccessful(_ status: Int32) throws -> Bool {
|
private func isQuerySuccessful(_ status: Int32) throws -> Bool {
|
||||||
guard status != errSecItemNotFound else {
|
// [Nucleic vendored patch] With interactive UI suppressed (see `withoutInteractiveUI`), a
|
||||||
|
// matching item the caller isn't trusted to read returns `errSecInteractionNotAllowed`
|
||||||
|
// instead of prompting. Treat it like "not found" so registry lookups degrade to anonymous
|
||||||
|
// / env-var auth rather than surfacing an error — and never raise a Keychain panel.
|
||||||
|
guard status != errSecItemNotFound, status != errSecInteractionNotAllowed else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
guard status == errSecSuccess else {
|
guard status == errSecSuccess else {
|
||||||
@@ -230,6 +243,28 @@ public struct KeychainQuery {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [Nucleic vendored patch] Run `body` with the legacy login Keychain's interactive
|
||||||
|
/// authorization panel disabled, restoring the prior setting afterward.
|
||||||
|
///
|
||||||
|
/// Nucleic requires that no automatic registry-credential lookup (image pull / push / list, via
|
||||||
|
/// `KeychainHelper`) can ever raise the macOS "<app> wants to use your confidential information
|
||||||
|
/// stored in 'ghcr.io' in your keychain" panel. That panel appears whenever a process that isn't
|
||||||
|
/// on an internet-password item's ACL reads it — e.g. a `cctl` binary re-signed ad-hoc by a fresh
|
||||||
|
/// `make vminit-image` reading a token an earlier build stored. The data-protection
|
||||||
|
/// `kSecUseAuthenticationUI*` flags do NOT govern that legacy ACL/partition-list dialog; the only
|
||||||
|
/// switch that does is `SecKeychainSetUserInteractionAllowed(false)`, which makes a read that
|
||||||
|
/// would otherwise prompt fail with `errSecInteractionNotAllowed`. An already-trusted item still
|
||||||
|
/// reads silently; anything else fails silently and the caller falls back to anonymous / env-var
|
||||||
|
/// auth. `SecKeychain*` is deprecated but remains the only API covering this panel. Mirrors
|
||||||
|
/// `KeychainOwnedAccess.withoutLegacyKeychainUI` in NucleicCore.
|
||||||
|
private static func withoutInteractiveUI<T>(_ body: () -> T) -> T {
|
||||||
|
var previous = DarwinBoolean(true)
|
||||||
|
SecKeychainGetUserInteractionAllowed(&previous)
|
||||||
|
SecKeychainSetUserInteractionAllowed(false)
|
||||||
|
defer { SecKeychainSetUserInteractionAllowed(previous.boolValue) }
|
||||||
|
return body()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension KeychainQuery {
|
extension KeychainQuery {
|
||||||
|
|||||||
Reference in New Issue
Block a user