parent
bcbff1c4ff
commit
a722307a4e
@ -0,0 +1,48 @@ |
|||||||
|
// package cache implements a really primitive cache that associates expiring
|
||||||
|
// values with string keys. This cache never clears itself out.
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"sync" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
// Cache is a cache for binary data
|
||||||
|
type Cache struct { |
||||||
|
entries map[string]*entry |
||||||
|
mutex sync.RWMutex |
||||||
|
} |
||||||
|
|
||||||
|
// entry is an entry in a Cache
|
||||||
|
type entry struct { |
||||||
|
data interface{} |
||||||
|
expiration time.Time |
||||||
|
} |
||||||
|
|
||||||
|
// NewCache creates a new Cache
|
||||||
|
func NewCache() *Cache { |
||||||
|
return &Cache{entries: make(map[string]*entry)} |
||||||
|
} |
||||||
|
|
||||||
|
// Get returns the currently cached value for the given key, as long as it
|
||||||
|
// hasn't expired. If the key was never set, or has expired, found will be
|
||||||
|
// false.
|
||||||
|
func (cache *Cache) Get(key string) (val interface{}, found bool) { |
||||||
|
cache.mutex.RLock() |
||||||
|
defer cache.mutex.RUnlock() |
||||||
|
entry := cache.entries[key] |
||||||
|
if entry == nil { |
||||||
|
return nil, false |
||||||
|
} else if entry.expiration.Before(time.Now()) { |
||||||
|
return nil, false |
||||||
|
} else { |
||||||
|
return entry.data, true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Set sets a value in the cache with an expiration of now + ttl.
|
||||||
|
func (cache *Cache) Set(key string, data interface{}, ttl time.Duration) { |
||||||
|
cache.mutex.Lock() |
||||||
|
defer cache.mutex.Unlock() |
||||||
|
cache.entries[key] = &entry{data, time.Now().Add(ttl)} |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"crypto/tls" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
ONE_DAY = 24 * time.Hour |
||||||
|
TWO_WEEKS = ONE_DAY * 14 |
||||||
|
ONE_MONTH = 1 |
||||||
|
ONE_YEAR = 1 |
||||||
|
) |
||||||
|
|
||||||
|
type Cfg struct { |
||||||
|
Port *string |
||||||
|
Raddr *string |
||||||
|
Log *string |
||||||
|
Monitor *bool |
||||||
|
Tls *bool |
||||||
|
} |
||||||
|
|
||||||
|
type TlsConfig struct { |
||||||
|
PrivateKeyFile string |
||||||
|
CertFile string |
||||||
|
Organization string |
||||||
|
CommonName string |
||||||
|
ServerTLSConfig *tls.Config |
||||||
|
} |
||||||
|
|
||||||
|
func NewTlsConfig(pk, cert, org, cn string) *TlsConfig { |
||||||
|
return &TlsConfig{ |
||||||
|
PrivateKeyFile: pk, |
||||||
|
CertFile: cert, |
||||||
|
Organization: org, |
||||||
|
CommonName: cn, |
||||||
|
ServerTLSConfig: &tls.Config{ |
||||||
|
CipherSuites: []uint16{ |
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, |
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, |
||||||
|
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, |
||||||
|
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, |
||||||
|
tls.TLS_RSA_WITH_RC4_128_SHA, |
||||||
|
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, |
||||||
|
tls.TLS_RSA_WITH_AES_128_CBC_SHA, |
||||||
|
tls.TLS_RSA_WITH_AES_256_CBC_SHA, |
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, |
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, |
||||||
|
}, |
||||||
|
PreferServerCipherSuites: true, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"bytes" |
||||||
|
"compress/flate" |
||||||
|
"compress/gzip" |
||||||
|
"fmt" |
||||||
|
"io/ioutil" |
||||||
|
"math" |
||||||
|
"net/http" |
||||||
|
"strconv" |
||||||
|
) |
||||||
|
|
||||||
|
func httpDump(req *http.Request, resp *http.Response) { |
||||||
|
defer resp.Body.Close() |
||||||
|
var respStatusStr string |
||||||
|
respStatus := resp.StatusCode |
||||||
|
respStatusHeader := int(math.Floor(float64(respStatus / 100))) |
||||||
|
switch respStatusHeader { |
||||||
|
case 2: |
||||||
|
respStatusStr = Green("<--" + strconv.Itoa(respStatus)) |
||||||
|
case 3: |
||||||
|
respStatusStr = Yellow("<--" + strconv.Itoa(respStatus)) |
||||||
|
case 4: |
||||||
|
respStatusStr = Magenta("<--" + strconv.Itoa(respStatus)) |
||||||
|
case 5: |
||||||
|
respStatusStr = Red("<--" + strconv.Itoa(respStatus)) |
||||||
|
} |
||||||
|
fmt.Println(Green("Request:")) |
||||||
|
fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) |
||||||
|
for headerName, headerContext := range req.Header { |
||||||
|
fmt.Printf("%s: %s\n", Blue(headerName), headerContext) |
||||||
|
} |
||||||
|
fmt.Println(Green("Response:")) |
||||||
|
for headerName, headerContext := range resp.Header { |
||||||
|
fmt.Printf("%s: %s\n", Blue(headerName), headerContext) |
||||||
|
} |
||||||
|
|
||||||
|
respBody, err := ioutil.ReadAll(resp.Body) |
||||||
|
if err != nil { |
||||||
|
logger.Println("func httpDump read resp body err:", err) |
||||||
|
} else { |
||||||
|
acceptEncode := resp.Header["Content-Encoding"] |
||||||
|
var respBodyBin bytes.Buffer |
||||||
|
w := bufio.NewWriter(&respBodyBin) |
||||||
|
w.Write(respBody) |
||||||
|
w.Flush() |
||||||
|
for _, compress := range acceptEncode { |
||||||
|
switch compress { |
||||||
|
case "gzip": |
||||||
|
r, err := gzip.NewReader(&respBodyBin) |
||||||
|
if err != nil { |
||||||
|
logger.Println("gzip reader err:", err) |
||||||
|
} else { |
||||||
|
defer r.Close() |
||||||
|
respBody, _ = ioutil.ReadAll(r) |
||||||
|
} |
||||||
|
break |
||||||
|
case "deflate": |
||||||
|
r := flate.NewReader(&respBodyBin) |
||||||
|
defer r.Close() |
||||||
|
respBody, _ = ioutil.ReadAll(r) |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
fmt.Printf("%s\n", string(respBody)) |
||||||
|
} |
||||||
|
|
||||||
|
fmt.Printf("%s%s%s\n", Black("####################"), Cyan("END"), Black("####################")) |
||||||
|
} |
@ -0,0 +1,311 @@ |
|||||||
|
// Package keyman provides convenience APIs around Go's built-in crypto APIs.
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"crypto/rand" |
||||||
|
"crypto/rsa" |
||||||
|
"crypto/x509" |
||||||
|
"crypto/x509/pkix" |
||||||
|
"encoding/pem" |
||||||
|
"fmt" |
||||||
|
"io/ioutil" |
||||||
|
"math/big" |
||||||
|
"net" |
||||||
|
"os" |
||||||
|
"time" |
||||||
|
|
||||||
|
"listome.com/log" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
PEM_HEADER_PRIVATE_KEY = "RSA PRIVATE KEY" |
||||||
|
PEM_HEADER_PUBLIC_KEY = "RSA PRIVATE KEY" |
||||||
|
PEM_HEADER_CERTIFICATE = "CERTIFICATE" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
tenYearsFromToday = time.Now().AddDate(10, 0, 0) |
||||||
|
) |
||||||
|
|
||||||
|
// PrivateKey is a convenience wrapper for rsa.PrivateKey
|
||||||
|
type PrivateKey struct { |
||||||
|
rsaKey *rsa.PrivateKey |
||||||
|
} |
||||||
|
|
||||||
|
// Certificate is a convenience wrapper for x509.Certificate
|
||||||
|
type Certificate struct { |
||||||
|
cert *x509.Certificate |
||||||
|
derBytes []byte |
||||||
|
} |
||||||
|
|
||||||
|
/******************************************************************************* |
||||||
|
* Private Key Functions |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
// GeneratePK generates a PrivateKey with a specified size in bits.
|
||||||
|
func GeneratePK(bits int) (key *PrivateKey, err error) { |
||||||
|
var rsaKey *rsa.PrivateKey |
||||||
|
rsaKey, err = rsa.GenerateKey(rand.Reader, bits) |
||||||
|
if err == nil { |
||||||
|
key = &PrivateKey{rsaKey: rsaKey} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// LoadPKFromFile loads a PEM-encoded PrivateKey from a file
|
||||||
|
func LoadPKFromFile(filename string) (key *PrivateKey, err error) { |
||||||
|
privateKeyData, err := ioutil.ReadFile(filename) |
||||||
|
if err != nil { |
||||||
|
if os.IsNotExist(err) { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return nil, fmt.Errorf("Unable to read private key file from file %s: %s", filename, err) |
||||||
|
} |
||||||
|
block, _ := pem.Decode(privateKeyData) |
||||||
|
if block == nil { |
||||||
|
return nil, fmt.Errorf("Unable to decode PEM encoded private key data: %s", err) |
||||||
|
} |
||||||
|
rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("Unable to decode X509 private key data: %s", err) |
||||||
|
} |
||||||
|
return &PrivateKey{rsaKey: rsaKey}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// PEMEncoded encodes the PrivateKey in PEM
|
||||||
|
func (key *PrivateKey) PEMEncoded() (pemBytes []byte) { |
||||||
|
return pem.EncodeToMemory(key.pemBlock()) |
||||||
|
} |
||||||
|
|
||||||
|
// WriteToFile writes the PEM-encoded PrivateKey to the given file
|
||||||
|
func (key *PrivateKey) WriteToFile(filename string) (err error) { |
||||||
|
keyOut, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("Failed to open %s for writing: %s", filename, err) |
||||||
|
} |
||||||
|
if err := pem.Encode(keyOut, key.pemBlock()); err != nil { |
||||||
|
return fmt.Errorf("Unable to PEM encode private key: %s", err) |
||||||
|
} |
||||||
|
if err := keyOut.Close(); err != nil { |
||||||
|
log.Debugf("Unable to close file: %v", err) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (key *PrivateKey) pemBlock() *pem.Block { |
||||||
|
return &pem.Block{Type: PEM_HEADER_PRIVATE_KEY, Bytes: x509.MarshalPKCS1PrivateKey(key.rsaKey)} |
||||||
|
} |
||||||
|
|
||||||
|
/******************************************************************************* |
||||||
|
* Certificate Functions |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
/* |
||||||
|
Certificate() generates a certificate for the Public Key of the given PrivateKey |
||||||
|
based on the given template and signed by the given issuer. If issuer is nil, |
||||||
|
the generated certificate is self-signed. |
||||||
|
*/ |
||||||
|
func (key *PrivateKey) Certificate(template *x509.Certificate, issuer *Certificate) (*Certificate, error) { |
||||||
|
return key.CertificateForKey(template, issuer, &key.rsaKey.PublicKey) |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
CertificateForKey() generates a certificate for the given Public Key based on |
||||||
|
the given template and signed by the given issuer. If issuer is nil, the |
||||||
|
generated certificate is self-signed. |
||||||
|
*/ |
||||||
|
func (key *PrivateKey) CertificateForKey(template *x509.Certificate, issuer *Certificate, publicKey interface{}) (*Certificate, error) { |
||||||
|
var issuerCert *x509.Certificate |
||||||
|
if issuer == nil { |
||||||
|
// Note - for self-signed certificates, we include the host's external IP address
|
||||||
|
issuerCert = template |
||||||
|
} else { |
||||||
|
issuerCert = issuer.cert |
||||||
|
} |
||||||
|
derBytes, err := x509.CreateCertificate( |
||||||
|
rand.Reader, // secure entropy
|
||||||
|
template, // the template for the new cert
|
||||||
|
issuerCert, // cert that's signing this cert
|
||||||
|
publicKey, // public key
|
||||||
|
key.rsaKey, // private key
|
||||||
|
) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return bytesToCert(derBytes) |
||||||
|
} |
||||||
|
|
||||||
|
// TLSCertificateFor generates a certificate useful for TLS use based on the
|
||||||
|
// given parameters. These certs are usable for key encipherment and digital
|
||||||
|
// signatures.
|
||||||
|
//
|
||||||
|
// organization: the org name for the cert.
|
||||||
|
// name: used as the common name for the cert. If name is an IP
|
||||||
|
// address, it is also added as an IP SAN.
|
||||||
|
// validUntil: time at which certificate expires
|
||||||
|
// isCA: whether or not this cert is a CA
|
||||||
|
// issuer: the certificate which is issuing the new cert. If nil, the
|
||||||
|
// new cert will be a self-signed CA certificate.
|
||||||
|
//
|
||||||
|
func (key *PrivateKey) TLSCertificateFor( |
||||||
|
organization string, |
||||||
|
name string, |
||||||
|
validUntil time.Time, |
||||||
|
isCA bool, |
||||||
|
issuer *Certificate) (cert *Certificate, err error) { |
||||||
|
|
||||||
|
template := &x509.Certificate{ |
||||||
|
SerialNumber: new(big.Int).SetInt64(int64(time.Now().UnixNano())), |
||||||
|
Subject: pkix.Name{ |
||||||
|
Organization: []string{organization}, |
||||||
|
CommonName: name, |
||||||
|
}, |
||||||
|
NotBefore: time.Now().AddDate(0, -1, 0), |
||||||
|
NotAfter: validUntil, |
||||||
|
|
||||||
|
BasicConstraintsValid: true, |
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, |
||||||
|
} |
||||||
|
|
||||||
|
// If name is an ip address, add it as an IP SAN
|
||||||
|
ip := net.ParseIP(name) |
||||||
|
if ip != nil { |
||||||
|
template.IPAddresses = []net.IP{ip} |
||||||
|
} |
||||||
|
|
||||||
|
isSelfSigned := issuer == nil |
||||||
|
if isSelfSigned { |
||||||
|
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} |
||||||
|
} |
||||||
|
|
||||||
|
// If it's a CA, add certificate signing
|
||||||
|
if isCA { |
||||||
|
template.KeyUsage = template.KeyUsage | x509.KeyUsageCertSign |
||||||
|
template.IsCA = true |
||||||
|
} |
||||||
|
|
||||||
|
cert, err = key.Certificate(template, issuer) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// LoadCertificateFromFile loads a Certificate from a PEM-encoded file
|
||||||
|
func LoadCertificateFromFile(filename string) (*Certificate, error) { |
||||||
|
certificateData, err := ioutil.ReadFile(filename) |
||||||
|
if err != nil { |
||||||
|
if os.IsNotExist(err) { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return nil, fmt.Errorf("Unable to read certificate file from disk: %s", err) |
||||||
|
} |
||||||
|
return LoadCertificateFromPEMBytes(certificateData) |
||||||
|
} |
||||||
|
|
||||||
|
// LoadCertificateFromPEMBytes loads a Certificate from a byte array in PEM
|
||||||
|
// format
|
||||||
|
func LoadCertificateFromPEMBytes(pemBytes []byte) (*Certificate, error) { |
||||||
|
block, _ := pem.Decode(pemBytes) |
||||||
|
if block == nil { |
||||||
|
return nil, fmt.Errorf("Unable to decode PEM encoded certificate") |
||||||
|
} |
||||||
|
return bytesToCert(block.Bytes) |
||||||
|
} |
||||||
|
|
||||||
|
// LoadCertificateFromX509 loads a Certificate from an x509.Certificate
|
||||||
|
func LoadCertificateFromX509(cert *x509.Certificate) (*Certificate, error) { |
||||||
|
pemBytes := pem.EncodeToMemory(&pem.Block{ |
||||||
|
Type: "CERTIFICATE", |
||||||
|
Headers: nil, |
||||||
|
Bytes: cert.Raw, |
||||||
|
}) |
||||||
|
return LoadCertificateFromPEMBytes(pemBytes) |
||||||
|
} |
||||||
|
|
||||||
|
// X509 returns the x509 certificate underlying this Certificate
|
||||||
|
func (cert *Certificate) X509() *x509.Certificate { |
||||||
|
return cert.cert |
||||||
|
} |
||||||
|
|
||||||
|
// PEMEncoded encodes the Certificate in PEM
|
||||||
|
func (cert *Certificate) PEMEncoded() (pemBytes []byte) { |
||||||
|
return pem.EncodeToMemory(cert.pemBlock()) |
||||||
|
} |
||||||
|
|
||||||
|
// WriteToFile writes the PEM-encoded Certificate to a file.
|
||||||
|
func (cert *Certificate) WriteToFile(filename string) (err error) { |
||||||
|
certOut, err := os.Create(filename) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("Failed to open %s for writing: %s", filename, err) |
||||||
|
} |
||||||
|
defer func() { |
||||||
|
if err := certOut.Close(); err != nil { |
||||||
|
log.Debugf("Unable to close file: %v", err) |
||||||
|
} |
||||||
|
}() |
||||||
|
return pem.Encode(certOut, cert.pemBlock()) |
||||||
|
} |
||||||
|
|
||||||
|
func (cert *Certificate) WriteToTempFile() (name string, err error) { |
||||||
|
// Create a temp file containing the certificate
|
||||||
|
tempFile, err := ioutil.TempFile("", "tempCert") |
||||||
|
if err != nil { |
||||||
|
return "", fmt.Errorf("Unable to create temp file: %s", err) |
||||||
|
} |
||||||
|
name = tempFile.Name() |
||||||
|
err = cert.WriteToFile(name) |
||||||
|
if err != nil { |
||||||
|
return "", fmt.Errorf("Unable to save certificate to temp file: %s", err) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// WriteToDERFile writes the DER-encoded Certificate to a file.
|
||||||
|
func (cert *Certificate) WriteToDERFile(filename string) (err error) { |
||||||
|
certOut, err := os.Create(filename) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("Failed to open %s for writing: %s", filename, err) |
||||||
|
} |
||||||
|
defer func() { |
||||||
|
if err := certOut.Close(); err != nil { |
||||||
|
log.Debugf("Unable to close file: %v", err) |
||||||
|
} |
||||||
|
}() |
||||||
|
_, err = certOut.Write(cert.derBytes) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// PoolContainingCert creates a pool containing this cert.
|
||||||
|
func (cert *Certificate) PoolContainingCert() *x509.CertPool { |
||||||
|
pool := x509.NewCertPool() |
||||||
|
pool.AddCert(cert.cert) |
||||||
|
return pool |
||||||
|
} |
||||||
|
|
||||||
|
// PoolContainingCerts constructs a CertPool containing all of the given certs
|
||||||
|
// (PEM encoded).
|
||||||
|
func PoolContainingCerts(certs ...string) (*x509.CertPool, error) { |
||||||
|
pool := x509.NewCertPool() |
||||||
|
for _, cert := range certs { |
||||||
|
c, err := LoadCertificateFromPEMBytes([]byte(cert)) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
pool.AddCert(c.cert) |
||||||
|
} |
||||||
|
return pool, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (cert *Certificate) ExpiresBefore(time time.Time) bool { |
||||||
|
return cert.cert.NotAfter.Before(time) |
||||||
|
} |
||||||
|
|
||||||
|
func bytesToCert(derBytes []byte) (*Certificate, error) { |
||||||
|
cert, err := x509.ParseCertificate(derBytes) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &Certificate{cert, derBytes}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (cert *Certificate) pemBlock() *pem.Block { |
||||||
|
return &pem.Block{Type: PEM_HEADER_CERTIFICATE, Bytes: cert.derBytes} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
-----BEGIN CERTIFICATE----- |
||||||
|
MIIDIjCCAgqgAwIBAgIIFFyG4pw+r+kwDQYJKoZIhvcNAQELBQAwLzEXMBUGA1UE |
||||||
|
ChMOZ29taXRtcHJveHkxLjAxFDASBgNVBAMTC2dvbWl0bXByb3h5MB4XDTE2MDUy |
||||||
|
OTEwMjQ0NloXDTE3MDYyOTEwMjQ0NlowLzEXMBUGA1UEChMOZ29taXRtcHJveHkx |
||||||
|
LjAxFDASBgNVBAMTC2dvbWl0bXByb3h5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A |
||||||
|
MIIBCgKCAQEA5i5e0Ioniij/zx4uP+pEwIfCPuqRHpmtxnylXtDJNS7Mibsl57cC |
||||||
|
pKDymhGtehXKzt/7lv7Zh4PQyHUAPJQqTp6Q/bQRcnCEKoY0jSCDy7ZYxz+6YMth |
||||||
|
JD9CG67jCio78hQDyYQc8RpUJ8FyH+HWE8xyPkixnGC9tZSG6bAtP2ORtn9AsKXX |
||||||
|
8Hj9zWKrQqWFMbQvOp8SGVkqNsO/ZvIO3Rf5pBPlPTUTsjXMFFN2PipoMgZnF43C |
||||||
|
eXt3de+PxFfs+nN2IIPbLTmRHW8qrworkJ0TBJWYMiT6mFOEiqlF92pzdLNDyR3e |
||||||
|
xednS3We07jZBAbvQGmy1eenZORXZsDuRQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC |
||||||
|
AqQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMB |
||||||
|
Af8wDQYJKoZIhvcNAQELBQADggEBAH1Gj0AtwzwbM0+NPrGsv2B0z/BLLDHu9MIN |
||||||
|
EhVIPgcGj9E9e3uP2uzxBBfFIeIk9w2GyuzqHqq/NTOJSf3CpL0RJYI9gS8qMg4x |
||||||
|
wn5wkSPVb8cp36jS3rNvpNx6nuz+P+H+t1x4+KWk12GM2gz7g/YSmkXOtntQzpOW |
||||||
|
Gl9ydNYWbd0ad5k+kQhBp2nsq1NByg3fwEd1jgvSnxKB+P76OK01pSxRczLpCp0F |
||||||
|
+1gpD6ukiPIC1RZCUiHk4UeY8jmpBNgNW4OJmxgA+LFclB3hytkxN2qbnk5xfA33 |
||||||
|
1JciXh8y0ZD1wXB7y14PAB1TB53Rgjvk38OrPpBf3nETxlqP5zQ= |
||||||
|
-----END CERTIFICATE----- |
@ -0,0 +1,27 @@ |
|||||||
|
-----BEGIN RSA PRIVATE KEY----- |
||||||
|
MIIEpAIBAAKCAQEA5i5e0Ioniij/zx4uP+pEwIfCPuqRHpmtxnylXtDJNS7Mibsl |
||||||
|
57cCpKDymhGtehXKzt/7lv7Zh4PQyHUAPJQqTp6Q/bQRcnCEKoY0jSCDy7ZYxz+6 |
||||||
|
YMthJD9CG67jCio78hQDyYQc8RpUJ8FyH+HWE8xyPkixnGC9tZSG6bAtP2ORtn9A |
||||||
|
sKXX8Hj9zWKrQqWFMbQvOp8SGVkqNsO/ZvIO3Rf5pBPlPTUTsjXMFFN2PipoMgZn |
||||||
|
F43CeXt3de+PxFfs+nN2IIPbLTmRHW8qrworkJ0TBJWYMiT6mFOEiqlF92pzdLND |
||||||
|
yR3exednS3We07jZBAbvQGmy1eenZORXZsDuRQIDAQABAoIBAQC1pxKccOsTVZfL |
||||||
|
1H2bYz4u0vSwwl0UUSOa6PN5CDxCBFTWvSME4qDrWzkZ7amCF4CeEpVybF0vXQ9/ |
||||||
|
oxY0uJlKwkiab2j1b2ZeSNtgJLDeK2GUnO5xRQ+8k0+QmUwD0aDNQo5akjBk9epE |
||||||
|
FZ4DGy7fIRFnpebFCnOswTJXafnLc0FeNpBgWaGgmc+PI2yZy6ed8nB8CVvhnEQu |
||||||
|
EbIuw9JEwAYbvqXJ9E0gkwUTkkaMPIM+lQ+uY94btWtTCMpl2HY3uufE1Zhfoivr |
||||||
|
hsATWsPGOSCaKEK82PlpXZb5dK0agRx688Z5xavXfjqUoqDvXsgHOCvIdwh22jgV |
||||||
|
H7tSsQUBAoGBAOyiYSQkyWdniHrIV0Q75npbJbJXQ9uMQGVN370QsBaWJN0XZrOa |
||||||
|
aDOmzpGfEIBpY6zaHwZl/IAc+ffC0s4DN9k7jmGOuOv0BmHHf88XyXzKKMpBrR1F |
||||||
|
ofPDN8k6D2VL9QG3/B25hLpzeLt3aqOIqF2Aykkb2b5MybJ7iojKHQ95AoGBAPkE |
||||||
|
yyk5F3hdPv5xA1h31h7yJWcoDKbBuLc8po1StCo1CkMZYjpD6d2Gl2QVFOXDBXUU |
||||||
|
3PjZ3EZc4mZGjeaMKzk+bxTO8d96KtoNp92ox8t6j35mcWKgB7AkJopMJecWstNx |
||||||
|
y4jUf2pEDEzHgRMKwkecGnYRYg6FssVHUUBxk2YtAoGBAMw8SgNcDMjGvzDaARfP |
||||||
|
a1lnmd2XG+Y94saMs/MGmDSmWiLIs8eIGS+bZOailyd9zp29lNF87LXWTJje+fr9 |
||||||
|
JBLGKFljSY+9ClUSTul66lheU15d2QBOvT0a3Oc8yVTwfgKBvYQaPsh+KVID+3Ix |
||||||
|
3L73DaCT/RcWR29Y2XS+nN5hAoGAUJ0cp9uznWOSYQx06M7iomIBecOdVN6hza/0 |
||||||
|
uwtrB77j85TkF7iknPJVUDEC8t6PhbebUXQ1uxGWuUVd6qS12XI8w5+67X2/IZgK |
||||||
|
H9fj1ht3GJRRYHwuAPezAlJkcssGWS0dls4z8VLWKITtZBy3iDcg2dgGxmzB0tuj |
||||||
|
khkyfoECgYALp8DFdyYhqKIRSOu+FhIvkYo+x2qjoetTBlArdHzfCzzLk1908rel |
||||||
|
XUQ2Sh5Y3DseJK0oXA5CQqu3qu2LCX3Djmj9w9oxvGqDBd0gTas088kTTOcsWurG |
||||||
|
CcTadlQWS/CeCDQx2E6sfvu920G6/BhBn0a6Jtzs+HGA13zBFuBcRA== |
||||||
|
-----END RSA PRIVATE KEY----- |
@ -0,0 +1,28 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"io" |
||||||
|
"net" |
||||||
|
) |
||||||
|
|
||||||
|
type mitmListener struct { |
||||||
|
conn net.Conn |
||||||
|
} |
||||||
|
|
||||||
|
func (listener *mitmListener) Accept() (net.Conn, error) { |
||||||
|
if listener.conn != nil { |
||||||
|
conn := listener.conn |
||||||
|
listener.conn = nil |
||||||
|
return conn, nil |
||||||
|
} else { |
||||||
|
return nil, io.EOF |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (listener *mitmListener) Close() error { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (listener *mitmListener) Addr() net.Addr { |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,245 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"crypto/tls" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"log" |
||||||
|
"net" |
||||||
|
"net/http" |
||||||
|
"net/http/httputil" |
||||||
|
"regexp" |
||||||
|
"strings" |
||||||
|
"sync" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
type HandlerWrapper struct { |
||||||
|
MyConfig *Cfg |
||||||
|
tlsConfig *TlsConfig |
||||||
|
wrapped http.Handler |
||||||
|
pk *PrivateKey |
||||||
|
pkPem []byte |
||||||
|
issuingCert *Certificate |
||||||
|
issuingCertPem []byte |
||||||
|
serverTLSConfig *tls.Config |
||||||
|
dynamicCerts *Cache |
||||||
|
certMutex sync.Mutex |
||||||
|
https bool |
||||||
|
} |
||||||
|
|
||||||
|
func (hw *HandlerWrapper) GenerateCertForClient() (err error) { |
||||||
|
if hw.tlsConfig.Organization == "" { |
||||||
|
hw.tlsConfig.Organization = "gomitmproxy" + Version |
||||||
|
} |
||||||
|
if hw.tlsConfig.CommonName == "" { |
||||||
|
hw.tlsConfig.CommonName = "gomitmproxy" |
||||||
|
} |
||||||
|
if hw.pk, err = LoadPKFromFile(hw.tlsConfig.PrivateKeyFile); err != nil { |
||||||
|
hw.pk, err = GeneratePK(2048) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("Unable to generate private key: %s", err) |
||||||
|
} |
||||||
|
hw.pk.WriteToFile(hw.tlsConfig.PrivateKeyFile) |
||||||
|
} |
||||||
|
hw.pkPem = hw.pk.PEMEncoded() |
||||||
|
hw.issuingCert, err = LoadCertificateFromFile(hw.tlsConfig.CertFile) |
||||||
|
if err != nil || hw.issuingCert.ExpiresBefore(time.Now().AddDate(0, ONE_MONTH, 0)) { |
||||||
|
hw.issuingCert, err = hw.pk.TLSCertificateFor( |
||||||
|
hw.tlsConfig.Organization, |
||||||
|
hw.tlsConfig.CommonName, |
||||||
|
time.Now().AddDate(ONE_YEAR, 0, 0), |
||||||
|
true, |
||||||
|
nil) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("Unable to generate self-signed issuing certificate: %s", err) |
||||||
|
} |
||||||
|
hw.issuingCert.WriteToFile(hw.tlsConfig.CertFile) |
||||||
|
} |
||||||
|
hw.issuingCertPem = hw.issuingCert.PEMEncoded() |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (hw *HandlerWrapper) FakeCertForName(name string) (cert *tls.Certificate, err error) { |
||||||
|
kpCandidateIf, found := hw.dynamicCerts.Get(name) |
||||||
|
if found { |
||||||
|
return kpCandidateIf.(*tls.Certificate), nil |
||||||
|
} |
||||||
|
|
||||||
|
hw.certMutex.Lock() |
||||||
|
defer hw.certMutex.Unlock() |
||||||
|
kpCandidateIf, found = hw.dynamicCerts.Get(name) |
||||||
|
if found { |
||||||
|
return kpCandidateIf.(*tls.Certificate), nil |
||||||
|
} |
||||||
|
|
||||||
|
//create certificate
|
||||||
|
certTTL := TWO_WEEKS |
||||||
|
generatedCert, err := hw.pk.TLSCertificateFor( |
||||||
|
hw.tlsConfig.Organization, |
||||||
|
name, |
||||||
|
time.Now().Add(certTTL), |
||||||
|
false, |
||||||
|
hw.issuingCert) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("Unable to issue certificate: %s", err) |
||||||
|
} |
||||||
|
keyPair, err := tls.X509KeyPair(generatedCert.PEMEncoded(), hw.pkPem) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("Unable to parse keypair for tls: %s", err) |
||||||
|
} |
||||||
|
|
||||||
|
cacheTTL := certTTL - ONE_DAY |
||||||
|
hw.dynamicCerts.Set(name, &keyPair, cacheTTL) |
||||||
|
return &keyPair, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.Request) { |
||||||
|
req.Header.Del("Proxy-Connection") |
||||||
|
req.Header.Set("Connection", "Keep-Alive") |
||||||
|
|
||||||
|
// handle connection
|
||||||
|
connIn, _, err := resp.(http.Hijacker).Hijack() |
||||||
|
if err != nil { |
||||||
|
logger.Println("hijack error:", err) |
||||||
|
} |
||||||
|
defer connIn.Close() |
||||||
|
|
||||||
|
var respOut *http.Response |
||||||
|
host := req.Host |
||||||
|
|
||||||
|
matched, _ := regexp.MatchString(":[0-9]+$", host) |
||||||
|
|
||||||
|
if !hw.https { |
||||||
|
if !matched { |
||||||
|
host += ":80" |
||||||
|
} |
||||||
|
|
||||||
|
connOut, err := net.DialTimeout("tcp", host, time.Second*30) |
||||||
|
if err != nil { |
||||||
|
logger.Println("dial to", host, "error:", err) |
||||||
|
} |
||||||
|
respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) |
||||||
|
if err != nil && err != io.EOF { |
||||||
|
logger.Println("read response error:", err) |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
if !matched { |
||||||
|
host += ":443" |
||||||
|
} |
||||||
|
|
||||||
|
connOut, err := tls.Dial("tcp", host, hw.tlsConfig.ServerTLSConfig) |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
logger.Panicln("tls dial to", host, "error:", err) |
||||||
|
} |
||||||
|
if err = req.Write(connOut); err != nil { |
||||||
|
logger.Println("send to server error", err) |
||||||
|
} |
||||||
|
|
||||||
|
respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) |
||||||
|
if err != nil && err != io.EOF { |
||||||
|
logger.Println("read response error:", err) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
if respOut == nil { |
||||||
|
log.Println("respOut is nil") |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
respDump, err := httputil.DumpResponse(respOut, true) |
||||||
|
if err != nil { |
||||||
|
logger.Println("respDump error:", err) |
||||||
|
} |
||||||
|
|
||||||
|
_, err = connIn.Write(respDump) |
||||||
|
if err != nil { |
||||||
|
logger.Println("connIn write error:", err) |
||||||
|
} |
||||||
|
|
||||||
|
if *hw.MyConfig.Monitor { |
||||||
|
go httpDump(req, respOut) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { |
||||||
|
if req.Method == "CONNECT" { |
||||||
|
hw.https = true |
||||||
|
hw.InterceptHTTPs(resp, req) |
||||||
|
} else { |
||||||
|
hw.DumpHTTPAndHTTPs(resp, req) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (hw *HandlerWrapper) InterceptHTTPs(resp http.ResponseWriter, req *http.Request) { |
||||||
|
addr := req.Host |
||||||
|
host := strings.Split(addr, ":")[0] |
||||||
|
|
||||||
|
cert, err := hw.FakeCertForName(host) |
||||||
|
if err != nil { |
||||||
|
msg := fmt.Sprintf("Could not get mitm cert for name: %s\nerror: %s", host, err) |
||||||
|
respBadGateway(resp, msg) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// handle connection
|
||||||
|
connIn, _, err := resp.(http.Hijacker).Hijack() |
||||||
|
if err != nil { |
||||||
|
msg := fmt.Sprintf("Unable to access underlying connection from client: %s", err) |
||||||
|
respBadGateway(resp, msg) |
||||||
|
return |
||||||
|
} |
||||||
|
tlsConfig := copyTlsConfig(hw.tlsConfig.ServerTLSConfig) |
||||||
|
tlsConfig.Certificates = []tls.Certificate{*cert} |
||||||
|
tlsConnIn := tls.Server(connIn, tlsConfig) |
||||||
|
|
||||||
|
listener := &mitmListener{tlsConnIn} |
||||||
|
|
||||||
|
handler := http.HandlerFunc(func(resp2 http.ResponseWriter, req2 *http.Request) { |
||||||
|
req2.URL.Scheme = "https" |
||||||
|
req2.URL.Host = req2.Host |
||||||
|
hw.DumpHTTPAndHTTPs(resp2, req2) |
||||||
|
|
||||||
|
}) |
||||||
|
|
||||||
|
go func() { |
||||||
|
err = http.Serve(listener, handler) |
||||||
|
if err != nil && err != io.EOF { |
||||||
|
log.Printf("Error serving mitm'ed connection: %s", err) |
||||||
|
} |
||||||
|
}() |
||||||
|
|
||||||
|
connIn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n")) |
||||||
|
} |
||||||
|
|
||||||
|
func Mitm(conf *Cfg, tlsConfig *TlsConfig) (*HandlerWrapper, error) { |
||||||
|
hw := &HandlerWrapper{ |
||||||
|
MyConfig: conf, |
||||||
|
tlsConfig: tlsConfig, |
||||||
|
dynamicCerts: NewCache(), |
||||||
|
} |
||||||
|
err := hw.GenerateCertForClient() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return hw, nil |
||||||
|
} |
||||||
|
|
||||||
|
func copyTlsConfig(template *tls.Config) *tls.Config { |
||||||
|
tlsConfig := &tls.Config{} |
||||||
|
if template != nil { |
||||||
|
*tlsConfig = *template |
||||||
|
} |
||||||
|
return tlsConfig |
||||||
|
} |
||||||
|
|
||||||
|
func respBadGateway(resp http.ResponseWriter, msg string) { |
||||||
|
log.Println(msg) |
||||||
|
resp.WriteHeader(502) |
||||||
|
resp.Write([]byte(msg)) |
||||||
|
} |
Loading…
Reference in new issue