diff --git a/.github/workflows/lima-guest.yml b/.github/workflows/lima-guest.yml index 34b3b9a98..64bfbcf34 100644 --- a/.github/workflows/lima-guest.yml +++ b/.github/workflows/lima-guest.yml @@ -33,6 +33,8 @@ jobs: with: persist-credentials: false + - uses: ./.github/actions/setup-nix + - uses: lima-vm/lima-actions/setup@55627e31b78637bf254a8b2a14da8ea7d12564e5 # v1 id: lima-setup with: @@ -48,10 +50,18 @@ jobs: restore-keys: | lima-${{ steps.lima-setup.outputs.version }}- - - name: Create Lima Instance from standard template + - name: Generate homeless templates + run: | + nix build .#lima-custom-templates + # Remove potential read-only symlink from cache + rm -rf ~/.lima/_templates + mkdir -p ~/.lima/_templates + cp result/share/lima/templates/homeless-*.yaml ~/.lima/_templates/ + + - name: Create Lima Instance from custom template run: | - limactl delete -f docker-nix || true - limactl create --yes --name=docker-nix template:docker + limactl delete -f homeless-docker-nix || true + limactl create --yes --name=homeless-docker-nix template:homeless-docker test-provisioning: needs: prepare-image @@ -76,25 +86,25 @@ jobs: restore-keys: | lima-${{ steps.lima-setup.outputs.version }}- - - name: Start docker-nix - run: limactl start --name=docker-nix --tty=false + - name: Start homeless-docker-nix + run: limactl start --name=homeless-docker-nix --tty=false - name: Install and configure Nix in Guest env: REV: ${{ github.head_ref || github.ref_name }} run: | # Use the same pipe-based command as in README, passing the current branch. - curl -fsSL "https://raw.githubusercontent.com/kachick/dotfiles/${REV}/scripts/install-nix.bash" | limactl shell docker-nix bash -s -- "${REV}" + curl -fsSL "https://raw.githubusercontent.com/kachick/dotfiles/${REV}/scripts/install-nix.bash" | limactl shell homeless-docker-nix bash -s -- "${REV}" - name: Verify Nix installation in Guest - run: limactl shell docker-nix nix --version + run: limactl shell homeless-docker-nix nix --version - name: Show Binary Cache effectiveness in Guest env: REV: ${{ github.head_ref || github.ref_name }} run: | FLAKE_URI="github:kachick/dotfiles/${REV}" - limactl shell docker-nix nix run --accept-flake-config "${FLAKE_URI}#la" -- --version + limactl shell homeless-docker-nix nix run --accept-flake-config "${FLAKE_URI}#la" -- --version - name: Show Docker works in Guest - run: limactl shell docker-nix docker run --rm hello-world + run: limactl shell homeless-docker-nix docker run --rm hello-world diff --git a/README.md b/README.md index a7fb20c8c..389099108 100644 --- a/README.md +++ b/README.md @@ -194,29 +194,29 @@ However I should keep the minimum environment for now. ## Lima -1. Start a standard Docker guest with Lima: +1. Start a custom Docker guest with Lima: ```bash - limactl start --name=docker-nix template:docker + limactl start --name=homeless-docker-nix template:homeless-docker ``` 1. Install and configure Nix in the guest: ```bash REV=main; \ - curl -fsSL "https://raw.githubusercontent.com/kachick/dotfiles/$REV/scripts/install-nix.bash" | limactl shell docker-nix bash -s -- "$REV" + curl -fsSL "https://raw.githubusercontent.com/kachick/dotfiles/$REV/scripts/install-nix.bash" | limactl shell homeless-docker-nix bash -s -- "$REV" ``` 1. Apply home-manager: ```bash - limactl shell docker-nix nix run --accept-flake-config "github:kachick/dotfiles#home-manager" -- switch -b backup --flake "github:kachick/dotfiles#user@lima" + limactl shell homeless-docker-nix nix run --accept-flake-config "github:kachick/dotfiles#home-manager" -- switch -b backup --flake "github:kachick/dotfiles#user@lima" ``` 1. Run containers: ```bash - limactl shell docker-nix docker run --rm hello-world + limactl shell homeless-docker-nix docker run --rm hello-world ``` ## How to setup secrets diff --git a/config/lima/_config/default.yaml b/config/lima/_config/default.yaml index efd137e1e..a2862bb0f 100644 --- a/config/lima/_config/default.yaml +++ b/config/lima/_config/default.yaml @@ -3,7 +3,10 @@ mounts: # TODO: Disable default mounting host home to keep secure even if it is not writable # - location: '~' # Just comment out still respects template default. And setting mountPoint as false handles the false as string... - location: '~/repos' # See git.nix in this repo - mountPoint: '{{.Home}}/repos' # Keep same behavior for ghq and the wrapped scripts + # Avoid hardcoding '{{.Home}}/repos' to ensure 'limactl shell' can + # synchronize the working directory by matching the host's absolute path. + # Also, consider and test compatibility with ghq and wrapped scripts (e.g., cdrepo). + # mountPoint: '{{.Home}}/repos' writable: true # For developing purpose, writable should be reasonable. And my system does not directly include these files # https://github.com/lima-vm/lima/issues/1015#issuecomment-4092839880 diff --git a/home-manager/lima-guest.nix b/home-manager/lima-guest.nix index a968b8cc6..ab467b6b4 100644 --- a/home-manager/lima-guest.nix +++ b/home-manager/lima-guest.nix @@ -1,6 +1,20 @@ -{ config, ... }: +{ config, lib, ... }: { # https://github.com/lima-vm/lima/blame/0d058b0eaa2d1bafc867298503a9239e89c202a8/templates/default.yaml#L295-L296 home.homeDirectory = "/home/${config.home.username}.linux"; + + # Restore access from the guest home to the host-path mounts. + # This allows 'limactl shell' to sync CWD (via absolute host paths) while keeping + # compatibility with tools expecting '~/repos' such as ghq and cdrepo. + home.activation.setupHostReposSymlink = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + for d in /home/*; do + # 1. Skip if it's the current guest home (e.g., /home/user.linux) + # 2. Check if 'repos' directory exists inside it (the host mount point) + if [ "$d" != "$HOME" ] && [ -d "$d/repos" ]; then + $DRY_RUN_CMD ln -sfn "$d/repos" "$HOME/repos" + break + fi + done + ''; } diff --git a/home-manager/lima-host.nix b/home-manager/lima-host.nix index 7649cfc8c..f403e9e82 100644 --- a/home-manager/lima-host.nix +++ b/home-manager/lima-host.nix @@ -27,6 +27,11 @@ in # See https://github.com/lima-vm/lima/blob/v1.0.1/templates/default.yaml#L536-L574 for detail file.".lima/_config/default.yaml".source = ../config/lima/_config/default.yaml; + file.".lima/_templates" = { + source = "${pkgs.local.lima-custom-templates}/share/lima/templates"; + recursive = true; + }; + shellAliases = { "lc" = "limactl"; }; diff --git a/pkgs/local/lima-custom-templates/package.nix b/pkgs/local/lima-custom-templates/package.nix new file mode 100644 index 000000000..47767dc2e --- /dev/null +++ b/pkgs/local/lima-custom-templates/package.nix @@ -0,0 +1,101 @@ +{ + lib, + stdenvNoCC, + yq-go, + gnugrep, + writableTmpDirAsHomeHook, + pkgs, +}: + +let + lima = pkgs.local.lima; +in +stdenvNoCC.mkDerivation { + pname = "lima-custom-templates"; + version = lima.version; + + dontUnpack = true; + + nativeBuildInputs = [ + # Use yq-go instead of `limactl template yq` because the latter fills in + # default values and resolves external references before evaluation, + # which would result in large, static YAML files rather than templates + # that inherit from bases. + yq-go + gnugrep + ]; + + buildPhase = '' + runHook preBuild + + for template_path in ${lima}/share/lima/templates/*.yaml; do + template_name=$(basename "$template_path") + # Skip default.yaml as it has a special configuration and isn't intended to be a standalone homeless template. + if [ "$template_name" = "default.yaml" ]; then + continue + fi + if yq '.base[] | select(. == "template:_default/mounts")' "$template_path" | grep -q .; then + yq 'del(.base[] | select(. == "template:_default/mounts"))' "$template_path" > "homeless-$template_name" + fi + done + + runHook postBuild + ''; + + doCheck = true; + + checkPhase = '' + runHook preCheck + + # Verify that the deletion actually happened for a representative template. + if [ "$(yq -o=json -I=0 '.' ${lima}/share/lima/templates/docker.yaml)" = "$(yq -o=json -I=0 '.' homeless-docker.yaml)" ]; then + echo "Error: The template was not modified. The target string might not exist anymore." >&2 + exit 1 + fi + + runHook postCheck + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out/share/lima/templates + cp homeless-*.yaml $out/share/lima/templates/ + + runHook postInstall + ''; + + doInstallCheck = true; + + nativeInstallCheckInputs = [ + lima + # Workaround for: "panic: $HOME is not defined" in limactl + writableTmpDirAsHomeHook + ]; + + installCheckPhase = '' + runHook preInstallCheck + + for template in $out/share/lima/templates/*.yaml; do + limactl validate "$template" + done + + runHook postInstallCheck + ''; + + preInstallCheck = '' + export USER=nix + ''; + + meta = { + description = "Custom Lima templates with the default home mount removed"; + longDescription = '' + Most standard Lima templates (except for a few like k3s) inherit `template:_default/mounts`, which cannot be excluded via `default.yaml`. + To improve security, this package creates templates with those mounts removed by default. + While Lima 2.1+ supports avoiding default mounts via CLI flags such as `--mount-only` or `--mount-none`, it is safer to have them disabled by default in the template. + Revisit once https://github.com/lima-vm/lima/discussions/4372 is resolved. + ''; + inherit (lima.meta) platforms; + maintainers = with lib.maintainers; [ kachick ]; + }; +}