Path: blob/master/pkg/driver/qemu/entitlementutil/entitlementutil.go
2649 views
// SPDX-FileCopyrightText: Copyright The Lima Authors1// SPDX-License-Identifier: Apache-2.023// Package entitlementutil provides a workaround for https://github.com/lima-vm/lima/issues/17424package entitlementutil56import (7"context"8"fmt"9"os"10"os/exec"11"strings"1213"github.com/mattn/go-isatty"14"github.com/sirupsen/logrus"1516"github.com/lima-vm/lima/v2/pkg/uiutil"17)1819// IsSigned returns an error if the binary is not signed, or the sign is invalid,20// or not associated with the "com.apple.security.hypervisor" entitlement.21func IsSigned(ctx context.Context, qExe string) error {22cmd := exec.CommandContext(ctx, "codesign", "--verify", qExe)23out, err := cmd.CombinedOutput()24logrus.WithError(err).Debugf("Executed %v: out=%q", cmd.Args, string(out))25if err != nil {26return fmt.Errorf("failed to run %v: %w (out=%q)", cmd.Args, err, string(out))27}2829cmd = exec.CommandContext(ctx, "codesign", "--display", "--entitlements", "-", "--xml", qExe)30out, err = cmd.CombinedOutput()31logrus.WithError(err).Debugf("Executed %v: out=%q", cmd.Args, string(out))32if err != nil {33return fmt.Errorf("failed to run %v: %w (out=%q)", cmd.Args, err, string(out))34}35if !strings.Contains(string(out), "com.apple.security.hypervisor") {36return fmt.Errorf("binary %q seems signed but lacking the \"com.apple.security.hypervisor\" entitlement", qExe)37}38return nil39}4041func Sign(ctx context.Context, qExe string) error {42ent, err := os.CreateTemp("", "lima-qemu-entitlements-*.xml")43if err != nil {44return fmt.Errorf("failed to create a temporary file for signing QEMU binary: %w", err)45}46entName := ent.Name()47defer os.RemoveAll(entName)48const entXML = `<?xml version="1.0" encoding="UTF-8"?>49<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">50<plist version="1.0">51<dict>52<key>com.apple.security.hypervisor</key>53<true/>54</dict>55</plist>`56if _, err = ent.WriteString(entXML); err != nil {57ent.Close()58return fmt.Errorf("failed to write to a temporary file %q for signing QEMU binary: %w", entName, err)59}60ent.Close()61signCmd := exec.CommandContext(ctx, "codesign", "--sign", "-", "--entitlements", entName, "--force", qExe)62out, err := signCmd.CombinedOutput()63logrus.WithError(err).Debugf("Executed %v: out=%q", signCmd.Args, string(out))64if err != nil {65return fmt.Errorf("failed to run %v: %w (out=%q)", signCmd.Args, err, string(out))66}67return nil68}6970// isColimaWrapper__useThisFunctionOnlyForPrintingHints returns true71// if qExe is like "/Users/<USER>/.colima/_wrapper/4e1b408f843d1c63afbbdcf80c40e4c88d33509f/bin/qemu-system-x86_64".72//73// The result can be used *ONLY* for controlling hint messages.74// DO NOT change the behavior of Lima depending on this result.75//76//nolint:revive,staticcheck // underscores in this function name intentionally added77func isColimaWrapper__useThisFunctionOnlyForPrintingHints__(qExe string) bool {78return strings.Contains(qExe, "/.colima/_wrapper/")79}8081// AskToSignIfNotSignedProperly asks to sign the QEMU binary with the "com.apple.security.hypervisor" entitlement.82//83// On Homebrew, QEMU binaries are usually already signed, but Homebrew's signing infrastructure is broken for Intel as of August 2023.84// https://github.com/lima-vm/lima/issues/174285func AskToSignIfNotSignedProperly(ctx context.Context, qExe string) {86if isSignedErr := IsSigned(ctx, qExe); isSignedErr != nil {87logrus.WithError(isSignedErr).Warnf("QEMU binary %q does not seem properly signed with the \"com.apple.security.hypervisor\" entitlement", qExe)88if isColimaWrapper__useThisFunctionOnlyForPrintingHints__(qExe) {89logrus.Info("Hint: the warning above is usually negligible for colima ( Printed due to https://github.com/abiosoft/colima/issues/796 )")90}91var ans bool92if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {93message := fmt.Sprintf("Try to sign %q with the \"com.apple.security.hypervisor\" entitlement?", qExe)94var askErr error95ans, askErr = uiutil.Confirm(message, true)96if askErr != nil {97logrus.WithError(askErr).Warn("No answer was given")98}99}100if ans {101if signErr := Sign(ctx, qExe); signErr != nil {102logrus.WithError(signErr).Warnf("Failed to sign %q", qExe)103} else {104logrus.Infof("Successfully signed %q with the \"com.apple.security.hypervisor\" entitlement", qExe)105}106} else {107logrus.Warn("If QEMU does not start up, you may have to sign the QEMU binary with the \"com.apple.security.hypervisor\" entitlement manually. See https://github.com/lima-vm/lima/issues/1742 .")108}109}110}111112113