Add Firefox support

Fixes #6
This commit is contained in:
Filippo Valsorda 2018-06-28 01:29:20 -04:00
parent 7544098b30
commit e4c5c312a7
6 changed files with 159 additions and 23 deletions

@ -33,16 +33,17 @@ On macOS, use Homebrew.
```
brew install --HEAD https://github.com/FiloSottile/mkcert/raw/master/HomebrewFormula/mkcert.rb
brew install nss # if you use Firefox
```
On Linux (install support coming soon!), use [the pre-built binaries](https://github.com/FiloSottile/mkcert/releases), or build from source.
On Linux (`-install` support coming soon!), use [the pre-built binaries (again, coming soon)](https://github.com/FiloSottile/mkcert/releases), or build from source.
```
$ git clone https://github.com/FiloSottile/mkcert
$ cd mkcert && make
```
Windows will be supported soon.
Windows will be supported next.
## Advanced topics

@ -150,3 +150,7 @@ func (m *mkcert) newCA() {
log.Printf("Created a new local CA at \"%s\" 💥\n", m.CAROOT)
}
func (m *mkcert) caUniqueName() string {
return "mkcert development CA " + m.caCert.SerialNumber.String()
}

65
main.go

@ -62,9 +62,19 @@ func (m *mkcert) Run(args []string) {
} else if m.uninstallMode {
m.uninstall()
return
} else if !m.check() {
log.Println("Warning: the local CA is not installed in the system trust store! ⚠️")
log.Println("Run \"mkcert -install\" to avoid verification errors ‼️")
} else {
var warning bool
if !m.checkPlatform() {
warning = true
log.Println("Warning: the local CA is not installed in the system trust store! ⚠️")
}
if hasFirefox && !m.checkFirefox() {
warning = true
log.Println("Warning: the local CA is not installed in the Firefox trust store! ⚠️")
}
if warning {
log.Println("Run \"mkcert -install\" to avoid verification errors ‼️")
}
}
if len(args) == 0 {
@ -134,26 +144,45 @@ func getCAROOT() string {
}
func (m *mkcert) install() {
if m.check() {
return
var printed bool
if !m.checkPlatform() {
m.installPlatform()
m.ignoreCheckFailure = true // TODO: replace with a check for a successful install
log.Print("The local CA is now installed in the system trust store! ⚡️")
printed = true
}
m.installPlatform()
m.ignoreCheckFailure = true
if m.check() { // useless, see comment on ignoreCheckFailure
log.Print("The local CA is now installed in the system trust store! ⚡️\n\n")
} else {
log.Fatal("Installing failed. Please report the issue with details about your environment at https://github.com/FiloSottile/mkcert/issues/new 👎\n\n")
if hasFirefox && !m.checkFirefox() {
if hasCertutil {
m.installFirefox()
log.Print("The local CA is now installed in the Firefox trust store (requires restart)! 🦊")
} else {
log.Println(`Warning: "certutil" is not available, so the CA can't be automatically installed in Firefox! ⚠️`)
log.Printf(`Install "certutil" with "%s" and re-run "mkcert -install" 👈`, CertutilInstallHelp)
}
printed = true
}
if printed {
log.Print("")
}
}
func (m *mkcert) uninstall() {
m.uninstallPlatform()
log.Print("The local CA is now uninstalled from the system trust store! 👋\n\n")
if hasFirefox {
if hasCertutil {
m.uninstallFirefox()
} else {
log.Print("")
log.Println(`Warning: "certutil" is not available, so the CA can't be automatically uninstalled from Firefox (if it was ever installed)! ⚠️`)
log.Printf(`You can install "certutil" with "%s" and re-run "mkcert -uninstall" 👈`, CertutilInstallHelp)
log.Print("")
}
}
log.Print("The local CA is now uninstalled from the system trust store(s)! 👋")
log.Print("")
}
func (m *mkcert) check() bool {
func (m *mkcert) checkPlatform() bool {
if m.ignoreCheckFailure {
return true
}
@ -167,3 +196,9 @@ func fatalIfErr(err error, msg string) {
log.Fatalf("ERROR: %s: %s", msg, err)
}
}
func fatalIfCmdErr(err error, cmd string, out []byte) {
if err != nil {
log.Fatalf("ERROR: failed to execute \"%s\": %s\n\n%s\n", cmd, err, out)
}
}

@ -16,6 +16,12 @@ import (
"github.com/DHowett/go-plist"
)
var (
FirefoxPath = "/Applications/Firefox.app"
FirefoxProfile = os.Getenv("HOME") + "/Library/Application Support/Firefox/Profiles/*"
CertutilInstallHelp = "brew install nss"
)
// https://github.com/golang/go/issues/24652#issuecomment-399826583
var trustSettings []interface{}
var _, _ = plist.Unmarshal(trustSettingsData, &trustSettings)
@ -100,9 +106,3 @@ func (m *mkcert) uninstallPlatform() {
out, err := cmd.CombinedOutput()
fatalIfCmdErr(err, "security remove-trusted-cert", out)
}
func fatalIfCmdErr(err error, cmd string, out []byte) {
if err != nil {
log.Fatalf("ERROR: failed to execute \"%s\": %s\n\n%s\n", cmd, err, out)
}
}

89
truststore_firefox.go Normal file

@ -0,0 +1,89 @@
package main
import (
"log"
"os"
"os/exec"
"path/filepath"
"strings"
)
var (
hasFirefox bool
hasCertutil bool
certutilPath string
)
func init() {
_, err := os.Stat(FirefoxPath)
hasFirefox = !os.IsNotExist(err)
out, err := exec.Command("brew", "--prefix", "nss").Output()
if err != nil {
return
}
certutilPath = filepath.Join(strings.TrimSpace(string(out)), "bin", "certutil")
_, err = os.Stat(certutilPath)
hasCertutil = !os.IsNotExist(err)
}
func (m *mkcert) checkFirefox() bool {
if !hasCertutil {
return false
}
success := true
if m.forEachFirefoxProfile(func(profile string) {
err := exec.Command(certutilPath, "-V", "-d", profile, "-u", "L", "-n", m.caUniqueName()).Run()
if err != nil {
success = false
}
}) == 0 {
success = false
}
return success
}
func (m *mkcert) installFirefox() {
if m.forEachFirefoxProfile(func(profile string) {
cmd := exec.Command(certutilPath, "-A", "-d", profile, "-t", "C,,", "-n", m.caUniqueName(), "-i", filepath.Join(m.CAROOT, rootName))
out, err := cmd.CombinedOutput()
fatalIfCmdErr(err, "certutil -A", out)
}) == 0 {
log.Println("ERROR: no Firefox security databases found")
}
if !m.checkFirefox() {
log.Println("Installing in Firefox failed. Please report the issue with details about your environment at https://github.com/FiloSottile/mkcert/issues/new 👎")
log.Println("Note that if you never started Firefox, you need to do that at least once.")
}
}
func (m *mkcert) uninstallFirefox() {
m.forEachFirefoxProfile(func(profile string) {
err := exec.Command(certutilPath, "-V", "-d", profile, "-u", "L", "-n", m.caUniqueName()).Run()
if err != nil {
return
}
cmd := exec.Command(certutilPath, "-D", "-d", profile, "-n", m.caUniqueName())
out, err := cmd.CombinedOutput()
fatalIfCmdErr(err, "certutil -D", out)
})
}
func (m *mkcert) forEachFirefoxProfile(f func(profile string)) (found int) {
profiles, _ := filepath.Glob(FirefoxProfile)
if len(profiles) == 0 {
return
}
for _, profile := range profiles {
if _, err := os.Stat(filepath.Join(profile, "cert8.db")); !os.IsNotExist(err) {
f(profile)
found++
}
if _, err := os.Stat(filepath.Join(profile, "cert9.db")); !os.IsNotExist(err) {
f("sql:" + profile)
found++
}
}
return
}

@ -6,9 +6,16 @@ package main
import (
"log"
"os"
"path/filepath"
)
var (
FirefoxPath = "/usr/bin/firefox"
FirefoxProfile = os.Getenv("HOME") + "/.mozilla/firefox/*"
CertutilInstallHelp = "apt install libnss3-tools"
)
func (m *mkcert) installPlatform() {
log.Fatalf("-install is not yet supported on Linux 😣\nYou can manually install the root certificate at %q in the meantime.", filepath.Join(m.CAROOT, rootName))
}