mirror of https://github.com/namecoin/ncdns
Allow ncdns to automatically sync TLSA records with Firefox cert_override.txt.
parent
21c3c3d041
commit
fade5f412a
@ -0,0 +1,50 @@
|
||||
package tlsoverridefirefox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FilterOverrides accepts as input the contents of a Firefox cert_override.txt
|
||||
// and a host suffix to blacklist (usually "bit"). It returns the contents of
|
||||
// a new Firefox cert_override.txt that contains all overrides as the input
|
||||
// file except for those that match the host suffix.
|
||||
func FilterOverrides(overrides, blacklistedHostSuffix string) (string, error) {
|
||||
result := ""
|
||||
|
||||
overridesSlice := strings.Split(overrides, "\n")
|
||||
|
||||
for _, override := range overridesSlice {
|
||||
trimmed := strings.TrimSpace(override)
|
||||
if trimmed == "" {
|
||||
// This is a blank line; don't try to parse it or
|
||||
// include it in output.
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(trimmed, "#") {
|
||||
// This is a comment; pass it through verbatim.
|
||||
result = result + override + "\n"
|
||||
continue
|
||||
}
|
||||
|
||||
tabSplit := strings.Split(override, "\t")
|
||||
hostAndPort := tabSplit[0]
|
||||
|
||||
host, _, err := net.SplitHostPort(hostAndPort)
|
||||
if err != nil {
|
||||
// Don't log err since it may contain private data.
|
||||
return "", fmt.Errorf("Error parsing hostport")
|
||||
}
|
||||
|
||||
if host == blacklistedHostSuffix ||
|
||||
strings.HasSuffix(host, "."+blacklistedHostSuffix) {
|
||||
// Host is blacklisted; don't include it in output
|
||||
continue
|
||||
}
|
||||
|
||||
result = result + override + "\n"
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
package tlsoverridefirefoxsync
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hlandau/xlog"
|
||||
"gopkg.in/hlandau/easyconfig.v1/cflag"
|
||||
|
||||
"github.com/namecoin/ncdns/namecoin"
|
||||
"github.com/namecoin/ncdns/ncdumpzone"
|
||||
"github.com/namecoin/ncdns/tlsoverridefirefox"
|
||||
)
|
||||
|
||||
var (
|
||||
flagGroup = cflag.NewGroup(nil, "tlsoverridefirefox")
|
||||
syncEnableFlag = cflag.Bool(flagGroup, "sync", false,
|
||||
"Synchronize TLSA records from the Namecoin zone to Firefox's "+
|
||||
"cert_override.txt")
|
||||
firefoxProfileDirFlag = cflag.String(flagGroup, "profiledir", "",
|
||||
"Firefox profile directory")
|
||||
)
|
||||
|
||||
var log, Log = xlog.New("ncdns.tlsoverridefirefoxsync")
|
||||
|
||||
var zoneData string
|
||||
var zoneDataReady = false
|
||||
var zoneDataMux sync.Mutex
|
||||
|
||||
// Note: the reason for the Fatal reaction to errors is that, if we stop
|
||||
// syncing the override list, Firefox will continue trusting .bit certs that
|
||||
// might be revoked in Namecoin. Therefore, it is important that, in such a
|
||||
// situation, .bit domains must stop resolving until the issue is corrected.
|
||||
// Forcing ncdns to exit is the least complex way to achieve this.
|
||||
|
||||
func watchZone(conn namecoin.Conn) {
|
||||
for {
|
||||
var result bytes.Buffer
|
||||
|
||||
err := ncdumpzone.Dump(conn, &result, "firefox-override")
|
||||
log.Fatale(err, "Couldn't dump zone for Firefox override sync")
|
||||
|
||||
zoneDataMux.Lock()
|
||||
zoneData = result.String()
|
||||
zoneDataReady = true
|
||||
zoneDataMux.Unlock()
|
||||
|
||||
time.Sleep(10 * time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
func watchProfile(suffix string) {
|
||||
if firefoxProfileDirFlag.Value() == "" {
|
||||
log.Fatal("Missing required config option tlsoverridefirefox.profiledir")
|
||||
}
|
||||
|
||||
for {
|
||||
if profileInUse() {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// At this point we know that Firefox is not running.
|
||||
|
||||
zoneDataMux.Lock()
|
||||
zoneDataReadyLocal := zoneDataReady
|
||||
zoneDataLocal := zoneData
|
||||
zoneDataMux.Unlock()
|
||||
|
||||
if !zoneDataReadyLocal {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug("Syncing zone to cert_override.txt...")
|
||||
|
||||
prevOverrides, err := ioutil.ReadFile(
|
||||
firefoxProfileDirFlag.Value() + "/cert_override.txt")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// cert_override.txt doesn't exist in a default
|
||||
// Firefox install; it's only created once the
|
||||
// first override is configured in the Firefox
|
||||
// GUI. If it's not there, we can pretend we
|
||||
// read an empty file.
|
||||
prevOverrides = []byte(``)
|
||||
} else {
|
||||
log.Fatale(err,
|
||||
"Couldn't read Firefox "+
|
||||
"cert_override.txt")
|
||||
}
|
||||
}
|
||||
|
||||
filteredPrevOverrides, err := tlsoverridefirefox.
|
||||
FilterOverrides(string(prevOverrides), suffix)
|
||||
log.Fatale(err, "Couldn't filter Firefox overrides")
|
||||
|
||||
newOverrides := filteredPrevOverrides + zoneDataLocal + "\n"
|
||||
|
||||
// TODO: Does 0600 match the default behavior of Firefox?
|
||||
// TODO: maybe instead write to a temp file and then move the file into place?
|
||||
err = ioutil.WriteFile(firefoxProfileDirFlag.Value()+
|
||||
"/cert_override.txt", []byte(newOverrides), 0600)
|
||||
log.Fatale(err, "Couldn't write Firefox cert_override.txt")
|
||||
|
||||
log.Debug("Finished syncing zone to cert_override.txt")
|
||||
|
||||
time.Sleep(10 * time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
func profileInUse() bool {
|
||||
// This glob pattern matches the ".sqlite-wal" and ".sqlite-shm" files
|
||||
// that are only present when Firefox's databases are open.
|
||||
matches, err := filepath.Glob(firefoxProfileDirFlag.Value() + "/*.sqlite-*")
|
||||
log.Fatale(err, "Couldn't check if Firefox is running for override sync")
|
||||
|
||||
return matches != nil
|
||||
}
|
||||
|
||||
// Start starts 2 background threads that synchronize the blockchain's TLSA
|
||||
// records to a Firefox profile's cert_override.txt. It accepts a connection
|
||||
// to access Namecoin Core, as well as a host suffix (usually "bit").
|
||||
func Start(conn namecoin.Conn, suffix string) error {
|
||||
if syncEnableFlag.Value() {
|
||||
go watchZone(conn)
|
||||
go watchProfile(suffix)
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue