diff --git a/.goreleaser.yml b/.goreleaser.yml index e7e2cb1..0ba669b 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -34,9 +34,9 @@ builds: - s390x - ppc64le goarm: - - 5 - - 6 - - 7 + - "5" + - "6" + - "7" ignore: - goos: darwin goarch: arm @@ -54,7 +54,7 @@ builds: goarch: s390x - goos: freebsd goarch: arm - goarm: 5 + goarm: "5" flags: - -trimpath ldflags: @@ -64,8 +64,15 @@ archives: - format_overrides: - goos: windows format: zip - replacements: - darwin: mac + name_template: >- + {{ .ProjectName }}_ + {{- .Version }}_ + {{- if eq .Os "darwin" }}mac{{ else }}{{ .Os }}{{ end }}_ + {{- .Arch }} + {{- with .Arm }}v{{ . }}{{ end }} + {{- with .Mips }}_{{ . }}{{ end }} + {{- if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }} + checksum: algorithm: sha512 diff --git a/README.md b/README.md index bf532a5..6b4dd86 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ Because the subcommands and flags are constrained to benefit rapid plugin protot - `CADDY_VERSION` sets the version of Caddy to build. - `XCADDY_RACE_DETECTOR=1` enables the Go race detector in the build. - `XCADDY_DEBUG=1` enables the DWARF debug information in the build. -- `XCADDY_SETCAP=1` will run `sudo setcap cap_net_bind_service=+ep` on the temporary binary before running it when in dev mode. +- `XCADDY_SETCAP=1` will run `sudo setcap cap_net_bind_service=+ep` on the resulting binary. By default, the `sudo` command will be used if it is found; set `XCADDY_SUDO=0` to avoid using `sudo` if necessary. - `XCADDY_SKIP_BUILD=1` causes xcaddy to not compile the program, it is used in conjunction with build tools such as [GoReleaser](https://goreleaser.com). Implies `XCADDY_SKIP_CLEANUP=1`. - `XCADDY_SKIP_CLEANUP=1` causes xcaddy to leave build artifacts on disk after exiting. - `XCADDY_WHICH_GO` sets the go command to use when for example more then 1 version of go is installed. diff --git a/cmd/main.go b/cmd/main.go index 36bb2c8..ede7cd8 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -144,6 +144,12 @@ func runBuild(ctx context.Context, args []string) error { log.Fatalf("[FATAL] %v", err) } + // if requested, run setcap to allow binding to low ports + err = setcapIfRequested(output) + if err != nil { + return err + } + // prove the build is working by printing the version if runtime.GOOS == os.Getenv("GOOS") && runtime.GOARCH == os.Getenv("GOARCH") { if !filepath.IsAbs(output) { @@ -223,14 +229,10 @@ func runDev(ctx context.Context, args []string) error { return err } - if os.Getenv("XCADDY_SETCAP") == "1" { - cmd = exec.Command("sudo", "setcap", "cap_net_bind_service=+ep", binOutput) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - log.Printf("[INFO] Setting capabilities (requires admin privileges): %v", cmd.Args) - if err = cmd.Run(); err != nil { - return err - } + // if requested, run setcap to allow binding to low ports + err = setcapIfRequested(binOutput) + if err != nil { + return err } log.Printf("[INFO] Running %v\n\n", append([]string{binOutput}, args...)) @@ -257,6 +259,34 @@ func runDev(ctx context.Context, args []string) error { return cmd.Wait() } +func setcapIfRequested(output string) error { + if os.Getenv("XCADDY_SETCAP") != "1" { + return nil + } + + args := []string{"setcap", "cap_net_bind_service=+ep", output} + + // check if sudo isn't available, or we were instructed not to use it + _, sudoNotFound := exec.LookPath("sudo") + skipSudo := sudoNotFound != nil || os.Getenv("XCADDY_SUDO") == "0" + + var cmd *exec.Cmd + if skipSudo { + cmd = exec.Command(args[0], args[1:]...) + } else { + cmd = exec.Command("sudo", args...) + } + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + log.Printf("[INFO] Setting capabilities (requires admin privileges): %v", cmd.Args) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to setcap on the binary: %v", err) + } + + return nil +} + type module struct { Path string // module path Version string // module version