mirror of
https://github.com/fosrl/newt.git
synced 2026-07-20 06:31:29 +02:00
Add permissions check, shasum check, & build info
Former-commit-id: 357f718e00
This commit is contained in:
20
Makefile
20
Makefile
@@ -43,31 +43,31 @@ go-build-release: \
|
||||
go-build-release-freebsd-arm64
|
||||
|
||||
go-build-release-linux-arm64:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o bin/newt_linux_arm64
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS) -X main.newtPlatform=linux_arm64" -o bin/newt_linux_arm64
|
||||
|
||||
go-build-release-linux-arm32-v7:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags "$(LDFLAGS)" -o bin/newt_linux_arm32
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags "$(LDFLAGS) -X main.newtPlatform=linux_arm32" -o bin/newt_linux_arm32
|
||||
|
||||
go-build-release-linux-arm32-v6:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags "$(LDFLAGS)" -o bin/newt_linux_arm32v6
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags "$(LDFLAGS) -X main.newtPlatform=linux_arm32v6" -o bin/newt_linux_arm32v6
|
||||
|
||||
go-build-release-linux-amd64:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/newt_linux_amd64
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS) -X main.newtPlatform=linux_amd64" -o bin/newt_linux_amd64
|
||||
|
||||
go-build-release-linux-riscv64:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=riscv64 go build -ldflags "$(LDFLAGS)" -o bin/newt_linux_riscv64
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=riscv64 go build -ldflags "$(LDFLAGS) -X main.newtPlatform=linux_riscv64" -o bin/newt_linux_riscv64
|
||||
|
||||
go-build-release-darwin-arm64:
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o bin/newt_darwin_arm64
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "$(LDFLAGS) -X main.newtPlatform=darwin_arm64" -o bin/newt_darwin_arm64
|
||||
|
||||
go-build-release-darwin-amd64:
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/newt_darwin_amd64
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS) -X main.newtPlatform=darwin_amd64" -o bin/newt_darwin_amd64
|
||||
|
||||
go-build-release-windows-amd64:
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/newt_windows_amd64.exe
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "$(LDFLAGS) -X main.newtPlatform=windows_amd64" -o bin/newt_windows_amd64.exe
|
||||
|
||||
go-build-release-freebsd-amd64:
|
||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/newt_freebsd_amd64
|
||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS) -X main.newtPlatform=freebsd_amd64" -o bin/newt_freebsd_amd64
|
||||
|
||||
go-build-release-freebsd-arm64:
|
||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o bin/newt_freebsd_arm64
|
||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -ldflags "$(LDFLAGS) -X main.newtPlatform=freebsd_arm64" -o bin/newt_freebsd_arm64
|
||||
24
main.go
24
main.go
@@ -147,18 +147,19 @@ var (
|
||||
authDaemonEnabled bool
|
||||
authDaemonGenerateRandomPassword bool
|
||||
// Build/version (can be overridden via -ldflags "-X main.newtVersion=...")
|
||||
newtVersion = "version_replaceme"
|
||||
newtVersion = "version_replaceme"
|
||||
newtPlatform = "" // embedded at build time via -X main.newtPlatform=<os>_<arch>
|
||||
|
||||
// Observability/metrics flags
|
||||
metricsEnabled bool
|
||||
otlpEnabled bool
|
||||
adminAddr string
|
||||
region string
|
||||
metricsAsyncBytes bool
|
||||
pprofEnabled bool
|
||||
blueprintFile string
|
||||
provisioningBlueprintFile string
|
||||
noCloud bool
|
||||
metricsEnabled bool
|
||||
otlpEnabled bool
|
||||
adminAddr string
|
||||
region string
|
||||
metricsAsyncBytes bool
|
||||
pprofEnabled bool
|
||||
blueprintFile string
|
||||
provisioningBlueprintFile string
|
||||
noCloud bool
|
||||
|
||||
// New mTLS configuration variables
|
||||
tlsClientCert string
|
||||
@@ -676,6 +677,7 @@ func runNewtMain(ctx context.Context) {
|
||||
NewtID: id,
|
||||
Secret: secret,
|
||||
CurrentVersion: newtVersion,
|
||||
Platform: newtPlatform,
|
||||
TLSConfig: tlsCfg,
|
||||
}); err != nil {
|
||||
logger.Fatal("Auto-update failed: %v", err)
|
||||
@@ -1867,7 +1869,7 @@ persistent_keepalive_interval=5`, util.FixKey(privateKey.String()), util.FixKey(
|
||||
} else {
|
||||
logger.Warn("CLIENTS WILL NOT WORK ON THIS VERSION OF NEWT WITH THIS VERSION OF PANGOLIN, PLEASE UPDATE THE SERVER TO 1.13 OR HIGHER OR DOWNGRADE NEWT")
|
||||
}
|
||||
|
||||
|
||||
sendBlueprint(client, blueprintFile)
|
||||
if client.WasJustProvisioned() {
|
||||
logger.Info("Provisioning detected – sending provisioning blueprint")
|
||||
|
||||
@@ -3,7 +3,9 @@ package updates
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -26,6 +28,10 @@ type SelfUpdateConfig struct {
|
||||
Secret string
|
||||
// CurrentVersion is the version of the currently running binary.
|
||||
CurrentVersion string
|
||||
// Platform is the OS+arch string embedded at build time via ldflags
|
||||
// (e.g. "linux_amd64", "darwin_arm64"). When non-empty it is used
|
||||
// directly; when empty the value is derived from runtime.GOOS/GOARCH.
|
||||
Platform string
|
||||
// TLSConfig is an optional TLS configuration for the HTTP client (may be nil).
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
@@ -33,9 +39,10 @@ type SelfUpdateConfig struct {
|
||||
// versionResponse mirrors the JSON returned by POST /api/v1/auth/newt/version
|
||||
type versionResponse struct {
|
||||
Data struct {
|
||||
LatestVersion string `json:"latestVersion"`
|
||||
LatestVersion string `json:"latestVersion"`
|
||||
CurrentIsLatest bool `json:"currentIsLatest"`
|
||||
DownloadUrl string `json:"downloadUrl"`
|
||||
DownloadUrl string `json:"downloadUrl"`
|
||||
Sha256 string `json:"sha256"`
|
||||
} `json:"data"`
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
@@ -51,6 +58,7 @@ func isOfficialContainer() bool {
|
||||
|
||||
// platform returns the OS+arch string used in the newt release binary names,
|
||||
// e.g. "linux_amd64", "darwin_arm64", "windows_amd64".
|
||||
// It is used as a fallback when no platform was embedded at build time.
|
||||
func platform() string {
|
||||
goarch := runtime.GOARCH
|
||||
goos := runtime.GOOS
|
||||
@@ -71,6 +79,26 @@ func platform() string {
|
||||
return fmt.Sprintf("%s_%s", goos, arch)
|
||||
}
|
||||
|
||||
// verifySHA256 checks that the file at path has the expected SHA-256 hex digest.
|
||||
func verifySHA256(path, expected string) error {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file for hashing: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return fmt.Errorf("failed to hash file: %w", err)
|
||||
}
|
||||
|
||||
got := hex.EncodeToString(h.Sum(nil))
|
||||
if !strings.EqualFold(got, expected) {
|
||||
return fmt.Errorf("sha256 mismatch: expected %s, got %s", expected, got)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckAndSelfUpdate contacts the pangolin server, checks whether a newer
|
||||
// version of newt is available, downloads it if so, replaces the running
|
||||
// binary on disk, and re-executes from the new binary.
|
||||
@@ -107,10 +135,14 @@ func CheckAndSelfUpdate(cfg SelfUpdateConfig) error {
|
||||
}
|
||||
|
||||
// --- Step 1: Ask the server for the latest version ---
|
||||
plat := cfg.Platform
|
||||
if plat == "" {
|
||||
plat = platform()
|
||||
}
|
||||
reqBody, err := json.Marshal(map[string]string{
|
||||
"newtId": cfg.NewtID,
|
||||
"secret": cfg.Secret,
|
||||
"platform": platform(),
|
||||
"platform": plat,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal version request: %w", err)
|
||||
@@ -159,6 +191,16 @@ func CheckAndSelfUpdate(cfg SelfUpdateConfig) error {
|
||||
fmt.Printf("Update available: %s → %s\n", cfg.CurrentVersion, verResp.Data.LatestVersion)
|
||||
fmt.Printf("Downloading from: %s\n", verResp.Data.DownloadUrl)
|
||||
|
||||
// --- Pre-download: verify we can write to the binary's directory ---
|
||||
// Do this before downloading so a permission failure doesn't waste bandwidth.
|
||||
exeDir := filepath.Dir(exePath)
|
||||
writeTestFile, err := os.CreateTemp(exeDir, ".newt-write-test-*")
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot write to %s (you may need to run as root or with elevated permissions): %w", exeDir, err)
|
||||
}
|
||||
writeTestFile.Close()
|
||||
_ = os.Remove(writeTestFile.Name())
|
||||
|
||||
// --- Step 2: Download the new binary ---
|
||||
dlCtx, dlCancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer dlCancel()
|
||||
@@ -180,11 +222,7 @@ func CheckAndSelfUpdate(cfg SelfUpdateConfig) error {
|
||||
|
||||
// Write to a temp file in the same directory as the current binary so that
|
||||
// an atomic rename works even across filesystem boundaries.
|
||||
exeDir := filepath.Dir(exePath)
|
||||
tmpFile, err := os.CreateTemp(exeDir, ".newt-update-*")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create temp file for download: %w", err)
|
||||
}
|
||||
tmpPath := tmpFile.Name()
|
||||
|
||||
// Ensure the temp file is cleaned up on any error path.
|
||||
@@ -200,6 +238,17 @@ func CheckAndSelfUpdate(cfg SelfUpdateConfig) error {
|
||||
return fmt.Errorf("failed to close temp file: %w", err)
|
||||
}
|
||||
|
||||
// --- Verify SHA256 checksum if the server provided one ---
|
||||
if verResp.Data.Sha256 != "" {
|
||||
fmt.Println("Verifying SHA256 checksum...")
|
||||
if err := verifySHA256(tmpPath, verResp.Data.Sha256); err != nil {
|
||||
return fmt.Errorf("binary integrity check failed: %w", err)
|
||||
}
|
||||
fmt.Println("SHA256 checksum verified.")
|
||||
} else {
|
||||
fmt.Println("Warning: no SHA256 checksum provided by server, skipping verification.")
|
||||
}
|
||||
|
||||
// Make the new binary executable.
|
||||
if err := os.Chmod(tmpPath, 0755); err != nil {
|
||||
return fmt.Errorf("failed to set executable permission: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user