Add certificate generation and -uninstall
This commit is contained in:
parent
c3ffec658a
commit
314622cbb3
110
main.go
110
main.go
@ -16,18 +16,27 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var installFlag = flag.Bool("install", false, "install the local root CA in the system trust store")
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
|
var installFlag = flag.Bool("install", false, "install the local root CA in the system trust store")
|
||||||
|
var uninstallFlag = flag.Bool("uninstall", false, "uninstall the local root CA from the system trust store")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
(&mkcert{}).Run()
|
if *installFlag && *uninstallFlag {
|
||||||
|
log.Fatalln("ERROR: you can't set -install and -uninstall at the same time")
|
||||||
|
}
|
||||||
|
(&mkcert{
|
||||||
|
installMode: *installFlag, uninstallMode: *uninstallFlag,
|
||||||
|
}).Run(flag.Args())
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootName = "rootCA.pem"
|
const rootName = "rootCA.pem"
|
||||||
@ -38,6 +47,8 @@ var rootSubject = pkix.Name{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mkcert struct {
|
type mkcert struct {
|
||||||
|
installMode, uninstallMode bool
|
||||||
|
|
||||||
CAROOT string
|
CAROOT string
|
||||||
caCert *x509.Certificate
|
caCert *x509.Certificate
|
||||||
caKey crypto.PrivateKey
|
caKey crypto.PrivateKey
|
||||||
@ -48,19 +59,99 @@ type mkcert struct {
|
|||||||
ignoreCheckFailure bool
|
ignoreCheckFailure bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mkcert) Run() {
|
func (m *mkcert) Run(args []string) {
|
||||||
m.CAROOT = getCAROOT()
|
m.CAROOT = getCAROOT()
|
||||||
if m.CAROOT == "" {
|
if m.CAROOT == "" {
|
||||||
log.Fatalln("ERROR: failed to find the default CA location, set one as the CAROOT env var")
|
log.Fatalln("ERROR: failed to find the default CA location, set one as the CAROOT env var")
|
||||||
}
|
}
|
||||||
fatalIfErr(os.MkdirAll(m.CAROOT, 0755), "failed to create the CAROOT")
|
fatalIfErr(os.MkdirAll(m.CAROOT, 0755), "failed to create the CAROOT")
|
||||||
m.loadCA()
|
m.loadCA()
|
||||||
if *installFlag {
|
|
||||||
|
if m.installMode {
|
||||||
m.install()
|
m.install()
|
||||||
|
if len(args) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if m.uninstallMode {
|
||||||
|
m.uninstall()
|
||||||
|
return
|
||||||
} else if !m.check() {
|
} else if !m.check() {
|
||||||
log.Println("Warning: the local CA is not installed in the system trust store! ⚠️")
|
log.Println("Warning: the local CA is not installed in the system trust store! ⚠️")
|
||||||
log.Println("Run \"mkcert -install\" to avoid verification errors ‼️")
|
log.Println("Run \"mkcert -install\" to avoid verification errors ‼️")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(args) == 0 {
|
||||||
|
log.Println("Usage: TODO")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
re := regexp.MustCompile(`^[0-9A-Za-z._-]+$`)
|
||||||
|
for _, name := range args {
|
||||||
|
if ip := net.ParseIP(name); ip != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if re.MatchString(name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Fatalf("ERROR: %q is not a valid hostname or IP", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.makeCert(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mkcert) makeCert(hosts []string) {
|
||||||
|
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
fatalIfErr(err, "failed to generate certificate key")
|
||||||
|
|
||||||
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||||
|
fatalIfErr(err, "failed to generate serial number")
|
||||||
|
|
||||||
|
tpl := &x509.Certificate{
|
||||||
|
SerialNumber: serialNumber,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
Organization: []string{"mkcert development certificate"},
|
||||||
|
},
|
||||||
|
|
||||||
|
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||||
|
NotBefore: time.Now().AddDate(0, 0, -1),
|
||||||
|
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
}
|
||||||
|
for _, h := range hosts {
|
||||||
|
if ip := net.ParseIP(h); ip != nil {
|
||||||
|
tpl.IPAddresses = append(tpl.IPAddresses, ip)
|
||||||
|
} else {
|
||||||
|
tpl.DNSNames = append(tpl.DNSNames, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub := priv.PublicKey
|
||||||
|
cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, &pub, m.caKey)
|
||||||
|
fatalIfErr(err, "failed to generate certificate")
|
||||||
|
|
||||||
|
filename := strings.Replace(hosts[0], ":", ".", -1)
|
||||||
|
if len(hosts) > 1 {
|
||||||
|
filename += "+" + strconv.Itoa(len(hosts)-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
privDER, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||||
|
fatalIfErr(err, "failed to encode certificate key")
|
||||||
|
err = ioutil.WriteFile(filename+"-key.pem", pem.EncodeToMemory(
|
||||||
|
&pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0644)
|
||||||
|
fatalIfErr(err, "failed to save certificate key")
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(filename+".pem", pem.EncodeToMemory(
|
||||||
|
&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0600)
|
||||||
|
fatalIfErr(err, "failed to save certificate key")
|
||||||
|
|
||||||
|
log.Printf("\nCreated a new certificate valid for the following names 📜")
|
||||||
|
for _, h := range hosts {
|
||||||
|
log.Printf(" - %q", h)
|
||||||
|
}
|
||||||
|
log.Printf("\nThe certificate is at \"./%s.pem\" and the key at \"./%s-key.pem\" ✅\n\n", filename, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadCA will load or create the CA at CAROOT.
|
// loadCA will load or create the CA at CAROOT.
|
||||||
@ -176,12 +267,17 @@ func (m *mkcert) install() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if m.check() { // useless, see comment on ignoreCheckFailure
|
if m.check() { // useless, see comment on ignoreCheckFailure
|
||||||
log.Println("The local CA is now installed in the system trust store! ⚡️")
|
log.Print("The local CA is now installed in the system trust store! ⚡️\n\n")
|
||||||
} else {
|
} else {
|
||||||
log.Fatalln("Installing failed. Please report the issue with details about your environment at https://github.com/FiloSottile/mkcert/issues/new 👎")
|
log.Fatal("Installing failed. Please report the issue with details about your environment at https://github.com/FiloSottile/mkcert/issues/new 👎\n\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mkcert) uninstall() {
|
||||||
|
m.uninstallPlatform()
|
||||||
|
log.Print("The local CA is now uninstalled from the system trust store! 👋\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
func (m *mkcert) check() bool {
|
func (m *mkcert) check() bool {
|
||||||
if m.ignoreCheckFailure {
|
if m.ignoreCheckFailure {
|
||||||
return true
|
return true
|
||||||
|
@ -95,6 +95,12 @@ func (m *mkcert) installPlatform() {
|
|||||||
fatalIfCmdErr(err, "security trust-settings-import", out)
|
fatalIfCmdErr(err, "security trust-settings-import", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mkcert) uninstallPlatform() {
|
||||||
|
cmd := exec.Command("sudo", "security", "remove-trusted-cert", "-d", filepath.Join(m.CAROOT, rootName))
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
fatalIfCmdErr(err, "security remove-trusted-cert", out)
|
||||||
|
}
|
||||||
|
|
||||||
func fatalIfCmdErr(err error, cmd string, out []byte) {
|
func fatalIfCmdErr(err error, cmd string, out []byte) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("ERROR: failed to execute \"%s\": %s\n\n%s\n", cmd, err, out)
|
log.Fatalf("ERROR: failed to execute \"%s\": %s\n\n%s\n", cmd, err, out)
|
||||||
|
Loading…
Reference in New Issue
Block a user