Skip to content

Commit

Permalink
[NixOS support] Run patchelf after autoupdate download (#1468)
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaMahany authored Nov 22, 2023
1 parent e21f39f commit 54774b3
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 9 deletions.
9 changes: 6 additions & 3 deletions ee/tuf/autoupdate.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,12 @@ func (ta *TufAutoupdater) checkForUpdate() error {

// If launcher was updated, we want to exit and reload
if updatedVersion, ok := updatesDownloaded[binaryLauncher]; ok {
level.Debug(ta.logger).Log("msg", "launcher updated -- exiting to load new version", "new_binary_version", updatedVersion)
ta.signalRestart <- NewLauncherReloadNeededErr(updatedVersion)
return nil
// Only reload if we're not using a localdev path
if ta.knapsack.LocalDevelopmentPath() == "" {
level.Debug(ta.logger).Log("msg", "launcher updated -- exiting to load new version", "new_binary_version", updatedVersion)
ta.signalRestart <- NewLauncherReloadNeededErr(updatedVersion)
return nil
}
}

// For non-launcher binaries (i.e. osqueryd), call any reload functions we have saved
Expand Down
1 change: 1 addition & 0 deletions ee/tuf/autoupdate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func TestExecute_launcherUpdate(t *testing.T) {
mockKnapsack.On("TufServerURL").Return(tufServerUrl)
mockKnapsack.On("UpdateDirectory").Return("")
mockKnapsack.On("MirrorServerURL").Return("https://example.com")
mockKnapsack.On("LocalDevelopmentPath").Return("")
mockQuerier := newMockQuerier(t)

// Set up autoupdater
Expand Down
69 changes: 69 additions & 0 deletions ee/tuf/finalize_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//go:build linux
// +build linux

package tuf

import (
"context"
"fmt"
"os"
"strings"
"time"

"github.com/kolide/launcher/pkg/allowedcmd"
)

// patchExecutable updates the downloaded binary as necessary for it to be able to
// run on this system. On NixOS, we have to set the interpreter for any non-NixOS
// executable we want to run.
// See: https://unix.stackexchange.com/a/522823
func patchExecutable(executableLocation string) error {
if !allowedcmd.IsNixOS() {
return nil
}

interpreter, err := getInterpreter(executableLocation)
if err != nil {
return fmt.Errorf("getting interpreter for %s: %w", executableLocation, err)
}

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

cmd, err := allowedcmd.Patchelf(ctx, "--set-interpreter", interpreter, executableLocation)
if err != nil {
return fmt.Errorf("creating patchelf command: %w", err)
}

if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("running patchelf: output `%s`, error `%w`", string(out), err)
}

return nil
}

// getInterpreter asks patchelf what the interpreter is for the current running
// executable, assuming that's a reasonable choice given that the current executable
// is able to run.
func getInterpreter(executableLocation string) (string, error) {
currentExecutable, err := os.Executable()
if err != nil {
return "", fmt.Errorf("getting current running executable: %w", err)
}

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

cmd, err := allowedcmd.Patchelf(ctx, "--print-interpreter", currentExecutable)
if err != nil {
return "", fmt.Errorf("creating patchelf command: %w", err)
}

out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("running patchelf: output `%s`, error `%w`", string(out), err)

}

return strings.TrimSpace(string(out)), nil
}
8 changes: 8 additions & 0 deletions ee/tuf/finalize_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//go:build !linux
// +build !linux

package tuf

func patchExecutable(executableLocation string) error {
return nil
}
17 changes: 17 additions & 0 deletions ee/tuf/finalize_other_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//go:build !linux
// +build !linux

package tuf

import (
"testing"

"github.com/stretchr/testify/require"
)

func Test_patchExecutable(t *testing.T) {
t.Parallel()

// patchExecutable is a no-op on windows and darwin
require.NoError(t, patchExecutable(""))
}
5 changes: 5 additions & 0 deletions ee/tuf/library_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ func (ulm *updateLibraryManager) moveVerifiedUpdate(binary autoupdatableBinary,
return fmt.Errorf("could not set +x permissions on executable: %w", err)
}

// If necessary, patch the executable (NixOS only)
if err := patchExecutable(executableLocation(stagedVersionedDirectory, binary)); err != nil {
return fmt.Errorf("could not patch executable: %w", err)
}

// Validate the executable
if err := autoupdate.CheckExecutable(context.TODO(), executableLocation(stagedVersionedDirectory, binary), "--version"); err != nil {
return fmt.Errorf("could not verify executable: %w", err)
Expand Down
22 changes: 16 additions & 6 deletions pkg/allowedcmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
)

type AllowedCommand func(ctx context.Context, arg ...string) (*exec.Cmd, error)
Expand Down Expand Up @@ -38,14 +37,25 @@ func validatedCommand(ctx context.Context, knownPath string, arg ...string) (*ex
}

func allowSearchPath() bool {
if runtime.GOOS != "linux" {
return false
return IsNixOS()
}

// Save results of lookup so we don't have to stat for /etc/NIXOS every time
// we want to know.
var (
checkedIsNixOS = false
isNixOS = false
)

func IsNixOS() bool {
if checkedIsNixOS {
return isNixOS
}

// We only allow searching for binaries in PATH on NixOS
if _, err := os.Stat("/etc/NIXOS"); err == nil {
return true
isNixOS = true
}

return false
checkedIsNixOS = true
return isNixOS
}
4 changes: 4 additions & 0 deletions pkg/allowedcmd/cmd_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ func Pacman(ctx context.Context, arg ...string) (*exec.Cmd, error) {
return validatedCommand(ctx, "/usr/bin/pacman", arg...)
}

func Patchelf(ctx context.Context, arg ...string) (*exec.Cmd, error) {
return validatedCommand(ctx, "/run/current-system/sw/bin/patchelf", arg...)
}

func Ps(ctx context.Context, arg ...string) (*exec.Cmd, error) {
return validatedCommand(ctx, "/usr/bin/ps", arg...)
}
Expand Down

0 comments on commit 54774b3

Please sign in to comment.