87 lines
2.3 KiB
Go
87 lines
2.3 KiB
Go
package plugin
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/pem"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/yetone/magpie/internal/catalog"
|
|
)
|
|
|
|
// caEnv has Bun trust what this machine trusts. Bun checks TLS against
|
|
// the roots it ships, not the system's, so behind a box that re-signs
|
|
// HTTPS (a company proxy, a VPN, Surge's or Proxyman's MITM) whose root
|
|
// the user installed, magpie gets through and Bun doesn't: `bun add`
|
|
// fails with "UNABLE_TO_VERIFY_LEAF_SIGNATURE downloading package
|
|
// manifest", and a plugin's requests fail the same way. NODE_EXTRA_CA_CERTS
|
|
// adds the system's roots to Bun's, for installs and for the host alike.
|
|
// One the environment sets already is the user's and is left as it is.
|
|
func caEnv(env []string) []string {
|
|
for _, kv := range env {
|
|
k, v, _ := strings.Cut(kv, "=")
|
|
if strings.ToUpper(k) == "NODE_EXTRA_CA_CERTS" && strings.TrimSpace(v) != "" {
|
|
return nil
|
|
}
|
|
}
|
|
if p := systemCAFile(); p != "" {
|
|
return []string{"NODE_EXTRA_CA_CERTS=" + p}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
systemCAOnce sync.Once
|
|
systemCAPath string
|
|
)
|
|
|
|
// systemCAFile is a PEM file of the roots the system trusts beyond Bun's
|
|
// own, "" when there are none to add; it is made once a run.
|
|
var systemCAFile = func() string {
|
|
systemCAOnce.Do(func() {
|
|
if p := systemCABundle(); p != "" {
|
|
systemCAPath = p
|
|
return
|
|
}
|
|
certs := systemRoots()
|
|
if len(certs) == 0 {
|
|
return
|
|
}
|
|
var b bytes.Buffer
|
|
for _, der := range certs {
|
|
_ = pem.Encode(&b, &pem.Block{Type: "CERTIFICATE", Bytes: der})
|
|
}
|
|
systemCAPath = writeCAFile(b.Bytes())
|
|
})
|
|
return systemCAPath
|
|
}
|
|
|
|
// writeCAFile keeps pem beside Bun's install cache, named by its content so
|
|
// a Bun that is running keeps reading the file it was given.
|
|
func writeCAFile(b []byte) string {
|
|
sum := sha256.Sum256(b)
|
|
dir := filepath.Join(filepath.Dir(catalog.CachePath()), "bun")
|
|
p := filepath.Join(dir, "system-ca-"+hex.EncodeToString(sum[:6])+".pem")
|
|
if old, err := os.ReadFile(p); err == nil && bytes.Equal(old, b) {
|
|
return p
|
|
}
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return ""
|
|
}
|
|
tmp, err := os.CreateTemp(dir, ".system-ca-*")
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
_, werr := tmp.Write(b)
|
|
cerr := tmp.Close()
|
|
if werr != nil || cerr != nil || os.Rename(tmp.Name(), p) != nil {
|
|
os.Remove(tmp.Name())
|
|
return ""
|
|
}
|
|
return p
|
|
}
|