Last active
November 26, 2019 21:37
-
-
Save korc/032812cb6500de2aba27622cf320675a to your computer and use it in GitHub Desktop.
Encrypt HTTP (ex PUT/POST) payload
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"flag" | |
"io" | |
"log" | |
"net/http" | |
"net/http/httputil" | |
"net/url" | |
"os" | |
"strings" | |
"golang.org/x/crypto/openpgp" | |
"golang.org/x/crypto/openpgp/packet" | |
) | |
type EncryptedBody struct { | |
OrigBody io.ReadCloser | |
CryptedBody *bytes.Buffer | |
pWr io.WriteCloser | |
bodyClosed bool | |
} | |
func NewEncryptedBody(body io.ReadCloser, to []*openpgp.Entity, signer *openpgp.Entity) (*EncryptedBody, error) { | |
eb := &EncryptedBody{ | |
OrigBody: body, | |
CryptedBody: bytes.NewBuffer([]byte{}), | |
} | |
var err error | |
eb.pWr, err = openpgp.Encrypt(eb.CryptedBody, to, signer, &openpgp.FileHints{IsBinary: true}, nil) | |
if err != nil { | |
return nil, err | |
} | |
return eb, err | |
} | |
func (eb *EncryptedBody) Close() error { | |
eb.bodyClosed = true | |
eb.pWr.Close() | |
return eb.OrigBody.Close() | |
} | |
func (eb *EncryptedBody) Read(p []byte) (n int, err error) { | |
if !eb.bodyClosed { | |
bodybuf := make([]byte, len(p)) | |
n, err := eb.OrigBody.Read(bodybuf) | |
log.Printf("Read %d (out of %d) bytes from original body, err = %v", n, len(p), err) | |
if n > 0 { | |
if nWrote, err := eb.pWr.Write(bodybuf[:n]); err != nil { | |
log.Printf("Could not write (%d) to encryptor: %s", nWrote, err) | |
} | |
} | |
if err == io.EOF { | |
eb.bodyClosed = true | |
eb.pWr.Close() | |
} | |
} | |
return eb.CryptedBody.Read(p) | |
} | |
type arrayFlag []string | |
func (f *arrayFlag) String() string { | |
return strings.Join(*f, ", ") | |
} | |
func (f *arrayFlag) Set(value string) error { | |
*f = append(*f, value) | |
return nil | |
} | |
func main() { | |
var rcptFlag arrayFlag | |
listenAddrFlag := flag.String("listen", ":8080", "Listen address") | |
remoteAddr := flag.String("remote", "", "Remote address") | |
signFlag := flag.String("sign", "", "Sign with entity") | |
addExtFlag := flag.String("ext", "", "Add extension (ex: .pgp) to URL path") | |
methodFlag := flag.String("methods", "PUT,POST", "methods to encrypt") | |
flag.Var(&rcptFlag, "to", "Public key file of recipient to encrypt to(multiple args ok)") | |
flag.Parse() | |
if *remoteAddr == "" || len(rcptFlag) == 0 { | |
log.Fatal("Need -remote and -to flags") | |
} | |
var signer *openpgp.Entity | |
if *signFlag != "" { | |
if f, err := os.Open(*signFlag); err != nil { | |
log.Fatalf("Cannot open signing file %#v: %s", *signFlag, err) | |
} else { | |
var err error | |
if signer, err = openpgp.ReadEntity(packet.NewReader(f)); err != nil { | |
log.Fatalf("Could not read signing entity from %#v: %s", *signFlag, err) | |
} | |
if signer.PrivateKey == nil { | |
log.Fatalf("File %#v does not contain private key", *signFlag) | |
} | |
if signer.PrivateKey.Encrypted { | |
log.Fatalf("Cannot use encrypted private key in %#v", *signFlag) | |
} | |
} | |
} | |
rcpts := make([]*openpgp.Entity, 0) | |
for _, pubkeyFile := range rcptFlag { | |
if f, err := os.Open(pubkeyFile); err != nil { | |
log.Fatalf("Cannot open public key %#v: %s", pubkeyFile, err) | |
} else { | |
if rcpt, err := openpgp.ReadEntity(packet.NewReader(f)); err != nil { | |
log.Fatal("Cannot read entity: ", err) | |
} else { | |
rcpts = append(rcpts, rcpt) | |
} | |
} | |
} | |
remoteUrl, err := url.Parse(*remoteAddr) | |
if err != nil { | |
log.Fatalf("Cannot create URL from %#v: %s", *remoteAddr, err) | |
} | |
var methods map[string]bool | |
if *methodFlag != "" { | |
methods = make(map[string]bool) | |
for _, m := range strings.Split(*methodFlag, ",") { | |
methods[m] = true | |
} | |
} | |
log.Print("Listening and serving on: ", *listenAddrFlag) | |
if err := http.ListenAndServe(*listenAddrFlag, &httputil.ReverseProxy{ | |
Director: func(request *http.Request) { | |
request.URL.Host = remoteUrl.Host | |
request.URL.Scheme = remoteUrl.Scheme | |
request.URL.Path = remoteUrl.Path + request.URL.Path | |
log.Printf("%s %s (%d bytes)", request.Method, request.URL, request.ContentLength) | |
if methods == nil || methods[request.Method] { | |
if *addExtFlag != "" { | |
request.URL.Path = request.URL.Path + *addExtFlag | |
} | |
if request.Body != nil { | |
request.Header.Del("Content-Length") | |
request.ContentLength = -1 | |
request.Body, err = NewEncryptedBody(request.Body, rcpts, signer) | |
if err != nil { | |
log.Fatal("Cannot create encryptor: ", err) | |
} | |
} else { | |
log.Printf("No request body to encrypt") | |
} | |
} | |
}, | |
}); err != nil { | |
log.Fatal("Could not listen: ", err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment