Skip to content

Commit

Permalink
Add parameter to the resolver's LookupIP method to exclude non authen…
Browse files Browse the repository at this point in the history
…ticated responses.

Switch to goproxy it's lightweight, appears to be more maintained, and doesn't break web sockets.
Avoid doing TLSA lookups if no authenticated IP addresses are available.
Send the authenticated response via proxy context to dialTLS in HTTP transport.
Move proxy details to proxy.go and use the cmd app only to configure and start godane.
Encrypt the private key
  • Loading branch information
buffrr committed Jul 22, 2020
1 parent 1c1d3e8 commit 50db885
Show file tree
Hide file tree
Showing 11 changed files with 698 additions and 359 deletions.
39 changes: 22 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
**Note: Go DANE is still under development, use at your own risk.**


Go DANE enables the use of DANE/TLSA in browsers using a simple proxy. It currently supports [DANE](https://tools.ietf.org/html/rfc6698) EE(3), and works with self-signed certificates.
Go DANE enables the use of [DANE (DNS Based Authentication of Named Entities)](https://tools.ietf.org/html/rfc6698) in browsers using a simple proxy. It currently supports DANE-EE, and works with self-signed certificates.



Expand All @@ -18,34 +18,36 @@ Go DANE enables the use of DANE/TLSA in browsers using a simple proxy. It curren

## How it works

Go DANE mitms HTTPS for sites that support DANE. It requires one root CA to be installed locally. Go DANE will use the root CA to create certificates on the fly if it was able to verify DANE/TLSA.
It won't mitm sites that don't use DANE/TLSA and will serve their original certificate instead.


Go DANE acts as a middleman between the browser and DANE enabled sites. It will check if a domain supports it, and generate a certificate on the fly if the authentication was successful. The connection will remain encrypted between you and the end server. If a website doesn't support DANE, its original certificate will be served instead.

For this to work, Go DANE generates a local certificate authority that must be installed in your browser's certificate store. This CA is used to issue certificates for successful DANE authentications.
## Usage

Download the latest pre-built binary for your OS from [releases](https://github.com/buffrr/godane/releases) and run
You can build it from source using `go build github.com/buffrr/godane/cmd/godane` or download a binary for your OS from [releases](https://github.com/buffrr/godane/releases)



./godane -dns tls://1.1.1.1

* Add Go DANE proxy to your web browser `127.0.0.1:8080` (see [Firefox instructions](doc/firefox.md)). Make sure to add it for HTTPS as well.

* Visit `http://godane.test` and click install certificate.
You will be prompted to enter a passphrase. This passphrase is used to encrypt the private key stored on disk.

* Add Go DANE proxy to your web browser `127.0.0.1:8080` for HTTP/HTTPS.

* Import the certificate file `cert.crt` stored at `~/.godane` into your browser.


Note: When you run Go DANE for the first time, it will generate a root CA and store it in `~/.godane`.
The easiest way to try it out is to use Firefox because it supports adding a proxy natively and has a built in CA store so that you don't have to add the root CA or proxy to your whole OS (it's still experimental).

#### Some sites that currently use DANE-EE(3):
Use `godane -help` to see command line options.

Some sites that currently use DANE-EE:
* FreeBSD: https://freebsd.org

* Tor Project: https://torproject.org

* Kumari https://www.kumari.net/

and more?

### Go DANE with handshake.org

Expand All @@ -60,20 +62,23 @@ You can also use [easyhandshake](https://easyhandshake.com) resolver.

Note: You can configure hsd to use a different port if 53 is in use.

Some handshake sites

* https://3b
* https://proofofconcept

## Use of resolvers

Go DANE doesn't perform DNSSEC verficiation by itself. The resolver you specify must be DNSSEC capable. If you have a local resolver that is DNSSEC capable, you can use udp or tcp. If not, please use a trusted resolver that supports DNSSEC and uses DOT or DOH.
Go DANE doesn't perform DNSSEC verification by itself. The resolver you specify must be DNSSEC capable. If you have a local validating resolver, you can use udp/tcp. If not, please use a trusted resolver that supports DNSSEC and communicates over a secure channel.

Note: Go DANE will ignore TLSA responses that don't come with the [Authenticaed Data (AD)](https://tools.ietf.org/html/rfc3655) flag.
Note: for Go DANE to know the dns response is validated, the resolver must set the Authenticated Data (AD) flag to true.

## Why?

I wanted to use DANE/TLSA, but no browser currently supports it. Also, it appears that it's [not possible](https://www.dnssec-validator.cz/) to support it via an extension. So this seemed like a fun idea to try, and with [martian](https://github.com/google/martian), it was pretty easy.
## Why?

I wanted to try DANE, but no browser currently supports it. It may still be a long way to go for browser support, but if you want to try it now you can!


## Contributing

Contributions are welcome!


Expand Down
1 change: 1 addition & 0 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

type entry struct {
msg []dns.RR
ad bool
ttl time.Time
}

Expand Down
224 changes: 224 additions & 0 deletions cert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
package godane

// adopted from github.com/google/martian/mitm
// modified to use with godane and to make it work with goproxy

import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"log"
"math/big"
"net"
"sync"
"time"
)

// maxSerialNumber is the upper boundary that is used to create unique serial
// numbers for the certificate. This can be any unsigned integer up to 20
// bytes (2^(8*20)-1).
var maxSerialNumber = big.NewInt(0).SetBytes(bytes.Repeat([]byte{255}, 20))

// mitmConfig is a set of configuration values that are used to build TLS configs
// capable of MITM.
type mitmConfig struct {
ca *x509.Certificate
capriv interface{}
priv *rsa.PrivateKey
keyID []byte
validity time.Duration
org string
getCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error)
roots *x509.CertPool

certmu sync.RWMutex
certs map[string]*tls.Certificate
}

// NewAuthority creates a new CA certificate and associated
// private key.
func NewAuthority(name, organization string, validity time.Duration) (*x509.Certificate, *rsa.PrivateKey, error) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
}
pub := priv.Public()

// Subject Key Identifier support for end entity certificate.
// https://www.ietf.org/rfc/rfc3280.txt (section 4.2.1.2)
pkixpub, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
return nil, nil, err
}
h := sha1.New()
h.Write(pkixpub)
keyID := h.Sum(nil)

// TODO: keep a map of used serial numbers to avoid potentially reusing a
// serial multiple times.
serial, err := rand.Int(rand.Reader, maxSerialNumber)
if err != nil {
return nil, nil, err
}

tmpl := &x509.Certificate{
SerialNumber: serial,
Subject: pkix.Name{
CommonName: name,
Organization: []string{organization},
},
SubjectKeyId: keyID,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
NotBefore: time.Now().Add(-validity),
NotAfter: time.Now().Add(validity),
DNSNames: []string{name},
IsCA: true,
}

raw, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, pub, priv)
if err != nil {
return nil, nil, err
}

// Parse certificate bytes so that we have a leaf certificate.
x509c, err := x509.ParseCertificate(raw)
if err != nil {
return nil, nil, err
}

return x509c, priv, nil
}

// newMITMConfig creates a MITM config using the CA certificate and
// private key to generate on-the-fly certificates.
func newMITMConfig(ca *x509.Certificate, privateKey interface{}, validity time.Duration, organization string) (*mitmConfig, error) {
roots := x509.NewCertPool()
roots.AddCert(ca)

priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
pub := priv.Public()

// Subject Key Identifier support for end entity certificate.
// https://www.ietf.org/rfc/rfc3280.txt (section 4.2.1.2)
pkixpub, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
return nil, err
}
h := sha1.New()
h.Write(pkixpub)
keyID := h.Sum(nil)

return &mitmConfig{
ca: ca,
capriv: privateKey,
priv: priv,
keyID: keyID,
validity: validity,
org: organization,
certs: make(map[string]*tls.Certificate),
roots: roots,
}, nil
}

// tlsForHost returns a *tls.mitmConfig that will generate certificates on-the-fly
// using the provided hostname
func (c *mitmConfig) tlsForHost(hostname string) *tls.Config {
return &tls.Config{
InsecureSkipVerify: false,
GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
if hostname != clientHello.ServerName {
return nil, fmt.Errorf("hostname %s does not match server name %s", hostname, clientHello.ServerName)
}

return c.cert(hostname)
},
NextProtos: []string{"http/1.1"},
}
}

func (c *mitmConfig) cert(hostname string) (*tls.Certificate, error) {
// Remove the port if it exists.
host, _, err := net.SplitHostPort(hostname)
if err == nil {
hostname = host
}

c.certmu.RLock()
tlsc, ok := c.certs[hostname]
c.certmu.RUnlock()

if ok {
log.Printf("cache hit for %s", hostname)

// Check validity of the certificate for hostname match, expiry, etc. In
// particular, if the cached certificate has expired, create a new one.
if _, err := tlsc.Leaf.Verify(x509.VerifyOptions{
DNSName: hostname,
Roots: c.roots,
}); err == nil {
return tlsc, nil
}

log.Printf("invalid certificate in cache for %s", hostname)
}

log.Printf("cache miss for %s", hostname)

serial, err := rand.Int(rand.Reader, maxSerialNumber)
if err != nil {
return nil, err
}

tmpl := &x509.Certificate{SerialNumber: serial,
Subject: pkix.Name{
CommonName: hostname,
Organization: []string{c.org},
},
SubjectKeyId: c.keyID,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
NotBefore: time.
Now().Add(-c.validity),
NotAfter: time.Now().Add(c.validity),
}

if ip := net.ParseIP(hostname); ip != nil {
tmpl.IPAddresses = []net.IP{ip}
} else {
tmpl.DNSNames = []string{hostname}
}

raw, err := x509.CreateCertificate(rand.Reader, tmpl, c.ca, c.priv.Public(), c.capriv)
if err != nil {
return nil, err
}

// Parse certificate bytes so that we have a leaf certificate.
x509c, err := x509.ParseCertificate(raw)
if err != nil {
return nil, err
}

tlsc = &tls.Certificate{
Certificate: [][]byte{raw, c.ca.Raw},
PrivateKey: c.priv,
Leaf: x509c,
}

c.certmu.Lock()
c.certs[hostname] = tlsc
c.certmu.Unlock()

return tlsc, nil
}
Loading

0 comments on commit 50db885

Please sign in to comment.