srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/internal/ca/install.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/ca/install.go')
-rw-r--r--internal/ca/install.go112
1 files changed, 112 insertions, 0 deletions
diff --git a/internal/ca/install.go b/internal/ca/install.go
new file mode 100644
index 0000000..bafcea4
--- /dev/null
+++ b/internal/ca/install.go
@@ -0,0 +1,112 @@
+package ca
+
+import (
+ "fmt"
+ "os/exec"
+ "path/filepath"
+ "strings"
+)
+
+// commandExists reports whether name is on PATH. Var, not a plain func
+// call, so tests can substitute a fake lookup without touching the real
+// PATH - trust-store tooling varies enough across distros that testing
+// the actual detection logic (which command wins, in what order) matters
+// more than testing against whatever happens to be installed on the
+// machine running `go test`.
+var commandExists = func(name string) bool {
+ _, err := exec.LookPath(name)
+ return err == nil
+}
+
+// InstallInstructions returns copy-pasteable, OS-specific steps for
+// trusting caPath as a root CA. It only ever prints commands - it never
+// runs anything itself. Installing a root CA is a genuinely sensitive,
+// system-wide trust change (and system trust-store tooling varies enough
+// across distros that guessing wrong and auto-running the wrong command
+// is worse than asking); the user running the printed command themselves
+// keeps them in control of a change that affects every TLS connection on
+// the machine, not just mitmux's own traffic.
+func InstallInstructions(goos, caPath string) string {
+ switch goos {
+ case "linux":
+ return linuxInstructions(caPath)
+ case "darwin":
+ return darwinInstructions(caPath)
+ case "windows":
+ return windowsInstructions(caPath)
+ default:
+ return genericInstructions(caPath)
+ }
+}
+
+func linuxInstructions(caPath string) string {
+ var b strings.Builder
+ fmt.Fprintf(&b, "System trust store (curl, most CLI tools, Chrome/Chromium):\n")
+
+ switch {
+ case commandExists("trust"):
+ fmt.Fprintf(&b, " sudo trust anchor --store %s\n", caPath)
+ case commandExists("update-ca-trust"):
+ fmt.Fprintf(&b, " sudo cp %s /etc/pki/ca-trust/source/anchors/mitmux-ca.pem\n", caPath)
+ fmt.Fprintf(&b, " sudo update-ca-trust\n")
+ case commandExists("update-ca-certificates"):
+ fmt.Fprintf(&b, " sudo cp %s /usr/local/share/ca-certificates/mitmux-ca.crt\n", caPath)
+ fmt.Fprintf(&b, " sudo update-ca-certificates\n")
+ default:
+ fmt.Fprintf(&b, " No known trust-store tool (trust / update-ca-trust /\n")
+ fmt.Fprintf(&b, " update-ca-certificates) found on PATH. Check your distro's\n")
+ fmt.Fprintf(&b, " docs for how it manages /etc/ssl/certs.\n")
+ }
+
+ fmt.Fprintf(&b, "\nFirefox (and Chrome/Chromium's own NSS store, which doesn't\n")
+ fmt.Fprintf(&b, "always follow the system trust store on Linux):\n")
+ if commandExists("certutil") {
+ fmt.Fprintf(&b, " certutil -d sql:$HOME/.mozilla/firefox/<your-profile> -A -n mitmux -t \"C,,\" -i %s\n", caPath)
+ fmt.Fprintf(&b, " (find <your-profile> with: ls ~/.mozilla/firefox | grep default)\n")
+ } else {
+ fmt.Fprintf(&b, " Import manually: Settings -> Privacy & Security -> Certificates\n")
+ fmt.Fprintf(&b, " -> View Certificates -> Authorities -> Import, select %s\n", caPath)
+ fmt.Fprintf(&b, " (or install nss-tools/libnss3-tools for certutil, which can\n")
+ fmt.Fprintf(&b, " script this instead)\n")
+ }
+ return b.String()
+}
+
+func darwinInstructions(caPath string) string {
+ var b strings.Builder
+ fmt.Fprintf(&b, "System-wide (Keychain Access -> System, or via Terminal):\n")
+ fmt.Fprintf(&b, " sudo security add-trusted-cert -d -r trustRoot \\\n")
+ fmt.Fprintf(&b, " -k /Library/Keychains/System.keychain %s\n", caPath)
+ fmt.Fprintf(&b, "\nCurrent user only (no sudo, login keychain):\n")
+ fmt.Fprintf(&b, " security add-trusted-cert -d -r trustRoot \\\n")
+ fmt.Fprintf(&b, " -k ~/Library/Keychains/login.keychain-db %s\n", caPath)
+ fmt.Fprintf(&b, "\nFirefox uses its own certificate store, not the macOS Keychain -\n")
+ fmt.Fprintf(&b, "import %s manually via Settings -> Privacy & Security ->\n", caPath)
+ fmt.Fprintf(&b, "Certificates -> View Certificates -> Authorities -> Import.\n")
+ return b.String()
+}
+
+func windowsInstructions(caPath string) string {
+ var b strings.Builder
+ fmt.Fprintf(&b, "From an elevated (Administrator) command prompt:\n")
+ fmt.Fprintf(&b, " certutil -addstore -f \"ROOT\" %s\n", caPath)
+ fmt.Fprintf(&b, "\nOr from an elevated PowerShell:\n")
+ fmt.Fprintf(&b, " Import-Certificate -FilePath %s -CertStoreLocation Cert:\\LocalMachine\\Root\n", caPath)
+ fmt.Fprintf(&b, "\nFirefox uses its own certificate store, not the Windows store -\n")
+ fmt.Fprintf(&b, "import %s manually via Settings -> Privacy & Security ->\n", caPath)
+ fmt.Fprintf(&b, "Certificates -> View Certificates -> Authorities -> Import.\n")
+ return b.String()
+}
+
+func genericInstructions(caPath string) string {
+ return fmt.Sprintf("No install steps known for this OS - import %s into your\n"+
+ "client's trust store manually (browser certificate settings, or\n"+
+ "whatever --cacert / equivalent flag your TLS client offers).\n", caPath)
+}
+
+// CertPath returns the path to the CA certificate PEM file inside dir
+// (see EnsureCA), for callers that just need to point a user or a tool
+// at it.
+func CertPath(dir string) string {
+ return filepath.Join(dir, certFileName)
+}