7 Commits
Author SHA1 Message Date
Autumn Liu 9721112d67 update
diff-pr / dix (pull_request) Successful in 7m2s
flake-check / flake-check (pull_request) Failing after 3m56s
lint / lint (pull_request) Successful in 1m48s
2026-09-11 14:43:43 -05:00
Autumn Liu 3605fc36ef meow2
diff-pr / dix (pull_request) Successful in 6m20s
flake-check / flake-check (pull_request) Successful in 52s
lint / lint (pull_request) Successful in 1m50s
2026-09-11 02:08:07 -05:00
Autumn Liu bb79660386 meow
diff-pr / dix (pull_request) Successful in 6m46s
flake-check / flake-check (pull_request) Successful in 4m40s
lint / lint (pull_request) Failing after 1m54s
2026-09-11 01:48:02 -05:00
Autumn Liu a54cbc14bd Merge branch 'frcc-kvm' of https://git.floweytf.com/server/server-config into frcc-kvm 2026-09-11 01:47:19 -05:00
Floweynt 06ed76e5ab Merge branch 'master' into frcc-kvm
diff-pr / dix (pull_request) Successful in 18m18s
flake-check / flake-check (pull_request) Successful in 4m40s
lint / lint (pull_request) Failing after 1m57s
2026-09-10 19:30:53 -05:00
Autumn Liu 096908ac39 frcc experiments
diff-pr / dix (pull_request) Successful in 6m43s
flake-check / flake-check (pull_request) Successful in 4m42s
lint / lint (pull_request) Failing after 1m47s
2026-09-10 18:31:09 -05:00
Autumn Liu 33ccab9ad4 frcc experiments
diff-pr / dix (pull_request) Canceled after 0s
flake-check / flake-check (pull_request) Canceled after 0s
lint / lint (pull_request) Canceled after 0s
2026-09-09 17:58:14 -05:00
7 changed files with 20 additions and 50 deletions
+5 -1
View File
@@ -5,6 +5,10 @@ on:
- cron: "0 6 * * 1" - cron: "0 6 * * 1"
workflow_dispatch: workflow_dispatch:
permissions:
contents: read
issues: write
jobs: jobs:
scan: scan:
runs-on: nix runs-on: nix
@@ -21,7 +25,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
API: ${{ github.api_url }} API: ${{ github.api_url }}
REPO: ${{ github.repository }} REPO: ${{ github.repository }}
run: 'nix run -L .#ci -- upsert-issue vulnix "vulnix: known vulnerabilities in system closure" "$RUNNER_TEMP/issue.md"' run: nix run -L .#ci -- upsert-issue vulnix "vulnix: known vulnerabilities in system closure" "$RUNNER_TEMP/issue.md"
- name: close resolved issue - name: close resolved issue
if: steps.scan.outputs.count == '0' if: steps.scan.outputs.count == '0'
-7
View File
@@ -1,17 +1,10 @@
{ lib, pkgs }: { lib, pkgs }:
let
# vulnix's bundled nix-2.34 broke its own --experimental-features detection
nixConf = pkgs.writeText "ci-nix.conf" "experimental-features = nix-command flakes";
in
# tools must track ci.py's subprocess calls; nix stays ambient (uses the # tools must track ci.py's subprocess calls; nix stays ambient (uses the
# runner's own nix, not a pinned one). # runner's own nix, not a pinned one).
pkgs.writers.writePython3Bin "ci" { pkgs.writers.writePython3Bin "ci" {
flakeIgnore = [ "E501" ]; flakeIgnore = [ "E501" ];
libraries = [ pkgs.python3Packages.subprocess-tee ]; libraries = [ pkgs.python3Packages.subprocess-tee ];
makeWrapperArgs = [ makeWrapperArgs = [
"--set-default"
"NIX_USER_CONF_FILES"
(toString nixConf)
"--prefix" "--prefix"
"PATH" "PATH"
":" ":"
+5 -9
View File
@@ -18,8 +18,7 @@ def run(*args, **kw):
def nix(*args): def nix(*args):
# tee to stderr - stdout leaks into GITHUB_OUTPUT $() captures return subprocess_tee.run(("nix", "--extra-experimental-features", "nix-command flakes", "-L", *args), check=True, text=True).stdout
return subprocess_tee.run(("nix", "--extra-experimental-features", "nix-command flakes", "-L", *args), check=True, text=True, stdout=sys.stderr).stdout
def git(*args, check=True): def git(*args, check=True):
@@ -33,16 +32,13 @@ def gitea(method, path, body=None):
req.add_header("Authorization", f'token {os.environ["GH_TOKEN"]}') req.add_header("Authorization", f'token {os.environ["GH_TOKEN"]}')
if data is not None: if data is not None:
req.add_header("Content-Type", "application/json") req.add_header("Content-Type", "application/json")
try: with urllib.request.urlopen(req) as resp:
with urllib.request.urlopen(req) as resp: raw = resp.read()
raw = resp.read()
except urllib.error.HTTPError as e:
sys.exit(f"gitea {method} {path}: {e.code} {e.reason}\n{e.read().decode()}")
return json.loads(raw) if raw else None return json.loads(raw) if raw else None
def find_open_issue(label): def find_open_issue(label):
issues = gitea("GET", f"/issues?state=open&type=issues&labels={label}") issues = gitea("GET", f"/issues?state=open&labels={label}")
return issues[0] if issues else None return issues[0] if issues else None
@@ -239,7 +235,7 @@ def format_vulnix(vulns):
for v in vulns: for v in vulns:
for cve in v["affected_by"]: for cve in v["affected_by"]:
score = v.get("cvssv3_basescore", {}).get(cve) score = v.get("cvssv3_basescore", {}).get(cve)
desc = v.get("description", {}).get(cve, "").replace("\n", " ").replace("|", "∣") desc = v.get("description", {}).get(cve, "")
if len(desc) > 120: if len(desc) > 120:
desc = desc[:117] + "..." desc = desc[:117] + "..."
rows.append((v["pname"], v["version"], cve, score, desc)) rows.append((v["pname"], v["version"], cve, score, desc))
+4 -3
View File
@@ -163,9 +163,10 @@ in
trusted-public-keys = [ "microvm.cachix.org-1:oXnBc6hRE3eX5rSYdRyMYXnfzcCxC7yKPTbZXALsqys=" ]; trusted-public-keys = [ "microvm.cachix.org-1:oXnBc6hRE3eX5rSYdRyMYXnfzcCxC7yKPTbZXALsqys=" ];
}; };
nix.settings = { nix.gc = {
min-free = 5 * 1024 * 1024 * 1024; automatic = true;
max-free = 10 * 1024 * 1024 * 1024; dates = "daily";
options = "--delete-older-than 14d";
}; };
# nix.optimise.automatic = true; - doesn't work with microvm.writableStoreOverlay # nix.optimise.automatic = true; - doesn't work with microvm.writableStoreOverlay
+1
View File
@@ -75,6 +75,7 @@ let
packages = with pkgs; [ tmux ] ++ packages; packages = with pkgs; [ tmux ] ++ packages;
miscProperties = self._sshpiperRegister routingInfo; miscProperties = self._sshpiperRegister routingInfo;
}; };
in in
{ {
ssh-admin = mkSsh { ssh-admin = mkSsh {
+5 -12
View File
@@ -222,14 +222,10 @@ rec {
: Bind mounts, keyed by mount point. : Bind mounts, keyed by mount point.
`resources.devices` `resources.devices`
: Host device nodes to expose, as paths. Bound into the container at the : Host device nodes to expose, as paths. Bound into the container's `/dev`
same path and added to the unit's `DeviceAllow=` - the cgroup and added to the unit's `DeviceAllow=` - the bind alone leaves the cgroup
permission alone doesn't get you a working node (nothing can `mknod` a denying `open()`. Nodes keep their host ownership, so a consumer inside
device inside a user namespace), and the bind alone leaves the cgroup needs `isolation.privateUsers = "identity"` for the owning gid to mean
denying `open()`, so both are needed. The bind is unconditional, so
the device must actually exist on whatever host runs the container.
Nodes keep their host ownership, so a consumer inside needs
`isolation.privateUsers = "identity"` for the owning gid to mean
anything. anything.
`isolation.nixDaemon` `isolation.nixDaemon`
@@ -289,7 +285,7 @@ rec {
allBindMounts = builtins.foldl' (acc: t: acc // (t.resources.fs or { })) { } traits // bindMounts; allBindMounts = builtins.foldl' (acc: t: acc // (t.resources.fs or { })) { } traits // bindMounts;
userModules = map mkUser (lib.concatMap (t: t.users or [ ]) traits ++ users); userModules = map mkUser (lib.concatMap (t: t.users or [ ]) traits ++ users);
allPorts = ports ++ map (p: (normalizePort p).container) hostPorts; allPorts = ports ++ map (p: (normalizePort p).container) hostPorts;
useIdmap = privateUsers == "pick" || (builtins.isInt privateUsers && privateUsers > 0); useIdmap = privateUsers != "no";
bindMountExtraFlags = lib.mapAttrsToList ( bindMountExtraFlags = lib.mapAttrsToList (
mountPoint: bm: mountPoint: bm:
(if bm.isReadOnly then "--bind-ro=" else "--bind=") (if bm.isReadOnly then "--bind-ro=" else "--bind=")
@@ -297,9 +293,6 @@ rec {
+ lib.optionalString useIdmap ":idmap" + lib.optionalString useIdmap ":idmap"
) allBindMounts; ) allBindMounts;
bindMountHostPaths = map (bm: bm.hostPath) (lib.attrValues allBindMounts); bindMountHostPaths = map (bm: bm.hostPath) (lib.attrValues allBindMounts);
# DeviceAllow= alone only grants cgroup permission - nspawn can't
# mknod a device node inside a user namespace, so the node still
# needs an explicit bind from the host.
deviceFlags = map (d: "--bind=${d}") devices; deviceFlags = map (d: "--bind=${d}") devices;
inherit (deriveIdentity ifPrefix.nspawn name) inherit (deriveIdentity ifPrefix.nspawn name)
iface iface
-18
View File
@@ -337,24 +337,6 @@ with subtest("(functional): declared devices are usable in-container"):
server.succeed(ct_cmd(name, f"exec 3<> {dev}")) server.succeed(ct_cmd(name, f"exec 3<> {dev}"))
with subtest("(functional): /dev/kvm actually initializes, not just opens"):
# a wrong-gid or unmapped node still opens but fails the ioctls qemu
# needs, so boot with no disk (never completes) and kill it - the
# failure mode this catches is qemu refusing to start at all.
for name, devices in DEVICES.items():
if "/dev/kvm" not in devices:
continue
out = server.succeed(
ct_cmd(
name,
f"{TIMEOUT} 3 qemu-system-x86_64 -enable-kvm -m 256 -nographic -no-reboot "
">/tmp/qemu.out 2>&1; cat /tmp/qemu.out",
)
)
assert "Permission denied" not in out, f"{name}: qemu couldn't use /dev/kvm:\n{out}"
assert "Could not access KVM" not in out, f"{name}: qemu couldn't use /dev/kvm:\n{out}"
with subtest("(isolation): devices reach only the container that declared them"): with subtest("(isolation): devices reach only the container that declared them"):
for name in enterable: for name in enterable:
for dev in {d for ds in DEVICES.values() for d in ds} - set(DEVICES.get(name, [])): for dev in {d for ds in DEVICES.values() for d in ds} - set(DEVICES.get(name, [])):