| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- // Command sign_server runs a small HTTP server on the host that signs Windows PEs
- // with scripts/sign_windows.sh (PKCS#11 / USB token). GoReleaser inside Docker can call
- // http://host.docker.internal:<port>/sign so the binary is signed on the host filesystem
- // (same volume mount as the container).
- package main
- import (
- "encoding/json"
- "errors"
- "fmt"
- "log"
- "net/http"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
- )
- func main() {
- addr := os.Getenv("SIGN_SERVER_ADDR")
- if addr == "" {
- addr = "127.0.0.1:8765"
- }
- repoRoot := os.Getenv("SIGN_SERVER_REPO_ROOT")
- if repoRoot == "" {
- wd, err := os.Getwd()
- if err != nil {
- log.Fatal(err)
- }
- repoRoot = wd
- }
- var err error
- repoRoot, err = filepath.Abs(repoRoot)
- if err != nil {
- log.Fatal(err)
- }
- token := os.Getenv("SIGN_SERVER_TOKEN")
- mux := http.NewServeMux()
- mux.HandleFunc("/sign", func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
- return
- }
- if token != "" {
- got := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
- if got != token {
- http.Error(w, "unauthorized", http.StatusUnauthorized)
- return
- }
- }
- var body struct {
- Path string `json:"path"`
- }
- if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
- http.Error(w, "bad json", http.StatusBadRequest)
- return
- }
- abs, err := safeArtifactPath(repoRoot, body.Path)
- if err != nil {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- script := filepath.Join(repoRoot, "scripts", "sign_windows.sh")
- cmd := exec.Command("bash", script, abs)
- cmd.Dir = repoRoot
- cmd.Env = signEnv(os.Environ())
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- if err := cmd.Run(); err != nil {
- log.Printf("sign failed: %v", err)
- http.Error(w, "sign failed", http.StatusInternalServerError)
- return
- }
- w.WriteHeader(http.StatusOK)
- })
- mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusOK)
- _, _ = w.Write([]byte("ok"))
- })
- log.Printf("sign_server listening on http://%s (repo %s)", addr, repoRoot)
- if err := http.ListenAndServe(addr, mux); err != nil {
- if strings.Contains(err.Error(), "address already in use") {
- log.Fatalf("%v: another sign_server may still be running; try `make sign-server-stop` or set SIGN_SERVER_ADDR to a free port", err)
- }
- log.Fatal(err)
- }
- }
- func signEnv(environ []string) []string {
- out := make([]string, 0, len(environ)+2)
- for _, e := range environ {
- if strings.HasPrefix(e, "SIGN_HTTP_URL=") {
- continue
- }
- if strings.HasPrefix(e, "SKIP_CODE_SIGN=") {
- continue
- }
- out = append(out, e)
- }
- out = append(out, "SKIP_CODE_SIGN=0", "SIGN_HTTP_URL=")
- return out
- }
- func safeArtifactPath(repoRoot, rel string) (string, error) {
- rel = filepath.Clean(filepath.FromSlash(rel))
- if rel == "." || strings.HasPrefix(rel, "..") {
- return "", errors.New("invalid path")
- }
- distPrefix := "dist" + string(os.PathSeparator)
- if !strings.HasPrefix(rel, distPrefix) {
- return "", errors.New("path must be under dist/")
- }
- if filepath.Ext(rel) != ".exe" {
- return "", errors.New("path must end with .exe")
- }
- rootClean := filepath.Clean(repoRoot)
- abs := filepath.Join(rootClean, rel)
- absClean := filepath.Clean(abs)
- relTo, err := filepath.Rel(rootClean, absClean)
- if err != nil || strings.HasPrefix(relTo, "..") {
- return "", errors.New("path escapes repo root")
- }
- st, err := os.Stat(absClean)
- if err != nil {
- return "", fmt.Errorf("stat: %w", err)
- }
- if st.IsDir() {
- return "", errors.New("not a file")
- }
- return absClean, nil
- }
|