From b8e4e915bf37c132f8467697dc01137964fc4f46 Mon Sep 17 00:00:00 2001 From: idk Date: Tue, 12 Mar 2019 16:36:29 -0400 Subject: [PATCH] move to make a page --- .gitignore | 2 +- docs/README.md | 269 +++++++++++++++++++++++++++++++++++++++++++++ docs/README.pdf | Bin 0 -> 54209 bytes docs/httptunnel.go | 142 ++++++++++++++++++++++++ docs/index.html | 175 +++++++++++++++++++++++++++++ 5 files changed, 587 insertions(+), 1 deletion(-) create mode 100644 docs/README.md create mode 100644 docs/README.pdf create mode 100644 docs/httptunnel.go create mode 100644 docs/index.html diff --git a/.gitignore b/.gitignore index a8257a0..4dc6426 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ httpproxy-* *.out README.md.asc -doc/doc +docs/doc diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..e393cd5 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,269 @@ +How to Write an HTTP Proxy for I2P in Go +======================================== + +In this short guide, I will show you how to create a standalone HTTP proxy which +will only ever make connections over the I2P network as an introduction to I2P +application development in Go. Although most of the readers will have an +understanding of HTTP proxies already, and there is already an HTTP proxy to I2P +in the core I2P software, there is a method to this madness: + +Why another Go HTTP Proxy Tutorial? +----------------------------------- + +There are tons of tutorials out there to write HTTP proxies in Go(1). There is a +ton of example code out there you can just copy-and-paste, modify to suit your +needs, and use. So why another one now? + +Well one reason there are so many is that Go makes it very easy to write a +reasonably reliable HTTP proxy. It's a simple project that shows why Go can be +good at helping you do the things you need, if what you need is an HTTP client +or server. Likewise, the HTTP proxy makes a good introduction to why I2P can be +good at helping you do the things you need, if what you need to do is make HTTP +requests in privacy. So it exists to bridge the mental gap between the Go +application and I2P client in a way which is accessible to people who learned Go +from the internet like me. + +*warning:* Be careful and do not use this example code unmodified in your +real application. It deliberately references an earlier state of the application +in the root of this repository. This is a teaching tool and not production code. + +What is an HTTP Proxy? +---------------------- + +At it's simplest it's two things, it's an HTTP server, and an HTTP Client. When +the HTTP server recieves a request, it handles it by forwarding it to the HTTP +client, which then retrieves and returns it to the server, which forwards it +back to the requestor. In-between recieving the request and forwarding it to +the client, the server can make changes to how the requests are handled. So in +our example, we'll be leaving the example HTTP server in place, but modifying +the HTTP client to handle requests by routing them to I2P. + +Step One: Setting up the HTTP Server Structure +---------------------------------------------- + +Creating a custom HTTP server in Go is easy. We just need to create a type +called Proxy, which implements the http.Server that forwards you to the client. +First, import the goSam library. + + import ( + "github.com/eyedeekay/goSam" + ) + +goSam implements a transport which is compatible with Go's http.Client. The +proxy will then need to contain a goSam.Client and an http.Client. + + type Proxy struct { + Sam *goSam.Client + Client *http.Client + } + +In order to implement the http Server interface, we need the ServeHTTP function +within the Proxy structure, which has the following signature: + + func (p *Proxy) ServeHTTP(wr http.ResponseWriter, req *http.Request) + +Where req is the request made by the user, and wr is the the stream where the +response will be written and returned to the user. + +When you create an instance of this struct, you should make sure to first set +up an instance of goSam.Client and pass it to the struct, like this: + + sam, err := goSam.NewClientFromOptions( + goSam.SetUnpublished(true), + ) + if err != nil { + log.Fatal(err) + } + handler := &i2phttpproxy.Proxy{ + Sam: sam, + } + +Note that goSam has been set up to use an Unpublished leaseset in this case, +because it will be used as a client and not a service and doesn't need to be +discovered until it starts communicating with a service. + +Step Two: Anonymize the application +----------------------------------- + +If you're developing a privacy-aware application, it's important to anonymize +more than just the connection, but also the application as well. In the case of +an HTTP proxy, you should probably at least scrub some headers out. To do this, +make a slice with the headers you want to delete at the first hop, and a +function which deletes them from the underlying structure. + + var hopHeaders = []string{ + "Accept-Language", + "Connection", + "Keep-Alive", + "Proxy-Authenticate", + "Proxy-Authorization", + "Proxy-Connection", + "Trailers", + "Upgrade", + "X-Forwarded-For", + "X-Real-IP", + } + + func delHopHeaders(header http.Header) { + for _, h := range hopHeaders { + header.Del(h) + } + .... + +If you want, you can also re-write the user agent string in this function as +well. + + .... + if header.Get("User-Agent") != "MYOB/6.66 (AN/ON)" { + header.Set("User-Agent", "MYOB/6.66 (AN/ON)") + } + } + +Because our proxy will only be used for I2P addresses, we'll want ServeHTTP to +ignore non-I2P addresses. We can do this by examining and dropping the request +when the server recieves it, like this: + + func (p *Proxy) ServeHTTP(wr http.ResponseWriter, req *http.Request) { + if req.URL.Scheme != "http" && !strings.HasSuffix(req.URL.Host, ".i2p") { + msg := "unsupported protocal scheme " + req.URL.Scheme + http.Error(wr, msg, http.StatusBadRequest) + log.Println(msg) + return + } + ... + +Once you're done, add the header-scrubbing function to the ServeHTTP function. + + ... + delHopHeaders(req.Header) + ... + +Step Three: Actually handle the request +--------------------------------------- + +Now that we've got the application anonymized, it's time to handle the request. + + + ... + p.get(wr, req) + } + +To keep the steps of the procedure cleanly separated, I chose to contain this +in it's own function. It just carries the same signature of the ServeHTTP +function, which + + func (p *Proxy) get(wr http.ResponseWriter, req *http.Request) { + req.RequestURI = "" + resp, err := p.Client.Do(req) + if err != nil { + log.Println("ServeHTTP:", err) + return + } + defer resp.Body.Close() + + wr.WriteHeader(resp.StatusCode) + io.Copy(wr, resp.Body) + } + +But wait! Where's all the I2P stuff? What will this do if it's not connected to +I2P? It will just be an HTTP Proxy that doesn't work, because it's already +blacklisting non-I2P addresses. + +Step Four: Set up the I2P Client +-------------------------------- + +Setting up the I2P Client is really easy, but a good example would be a little +long so I decided to extract it out to it's own function and walk through the +steps. In the end, we need to come up with a new http.Client which uses i2p as a +Transport. In order to ensure that it uses the same settings as the goSam +client, the function is part of the Proxy struct. + + func (p *Proxy) NewClient() *http.Client { + return &http.Client{ + +In order to use the resulting http.Client with i2p, you'll need to alter it's +DialContext function to use the one from goSam instead. + + Transport: &http.Transport{ + DialContext: p.Sam.DialContext, + .... + +It may also be helpful to limit connections, set some timeouts(Go by default +expects you to set timeouts). Some example settings you might change would be: + + .... + MaxConnsPerHost: 1, + MaxIdleConns: 0, + MaxIdleConnsPerHost: 1, + DisableKeepAlives: false, + ResponseHeaderTimeout: time.Second * 600, + IdleConnTimeout: time.Second * 300, + }, + CheckRedirect: nil, + Timeout: time.Second * 600, + } + } + +Step Five: Create the main() function +------------------------------------- + +Now it's finally time to put it all together and make our code runnable. First, +lets make it accept a flag which allows the user to determine which port it +runs on. I don't know what ports other people use: + + func main() { + var addr = flag.String("addr", "127.0.0.1:7950", "The addr of the application.") + flag.Parse() + ... + +Next, set up the goSam client and hand it off to a new instance of *Proxy: + + ... + sam, err := goSam.NewClientFromOptions( + goSam.SetHost("127.0.0.1"), + goSam.SetPort("7656"), + goSam.SetUnpublished(true), + goSam.SetInLength(uint(2)), + goSam.SetOutLength(uint(2)), + goSam.SetInQuantity(uint(1)), + goSam.SetOutQuantity(uint(1)), + goSam.SetInBackups(uint(1)), + goSam.SetOutBackups(uint(1)), + goSam.SetReduceIdle(true), + goSam.SetReduceIdleTime(uint(2000000)), + ) + if err != nil { + log.Fatal(err) + } + handler := &Proxy{ + Sam: sam, + } + ... + +Use that SAM connection to set up an HTTP client: + + ... + handler.Client = handler.NewClient() + ... + +And serve it up. It's just that easy. + + ... + log.Println("Starting proxy server on", *addr) + if err := http.ListenAndServe(*addr, handler); err != nil { + log.Fatal("ListenAndServe:", err) + } + } + +:) + +Credits/Notes: +============== + + 1. [This guide is based on a guide for the clearnet](https://medium.com/@mlowicki/http-s-proxy-in-golang-in-less-than-100-lines-of-code-6a51c2f2c38c) +and that is only 10% because it was easier for me. 90% of the reason is to +illustrate that *using I2P in your application is, in all likelihood, not that* +*different than what you're already doing.*\* In all, this example is only 111 +lines of code long minus comments. + * if the rest of your application only sends and recieves that which is +non-linkable and absolutely necessary to its operation. diff --git a/docs/README.pdf b/docs/README.pdf new file mode 100644 index 0000000000000000000000000000000000000000..49878605c4ba4b7fa9dbd7c8a9c40e92887e0e88 GIT binary patch literal 54209 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 to Write an HTTP Proxy for I2P in Go +

In this short guide, I will show you how to create a standalone HTTP proxy which will only ever make connections over the I2P network as an introduction to I2P application development in Go. Although most of the readers will have an understanding of HTTP proxies already, and there is already an HTTP proxy to I2P in the core I2P software, there is a method to this madness:

+

Why another Go HTTP Proxy Tutorial?

+

There are tons of tutorials out there to write HTTP proxies in Go(1). There is a ton of example code out there you can just copy-and-paste, modify to suit your needs, and use. So why another one now?

+

Well one reason there are so many is that Go makes it very easy to write a reasonably reliable HTTP proxy. It's a simple project that shows why Go can be good at helping you do the things you need, if what you need is an HTTP client or server. Likewise, the HTTP proxy makes a good introduction to why I2P can be good at helping you do the things you need, if what you need to do is make HTTP requests in privacy. So it exists to bridge the mental gap between the Go application and I2P client in a way which is accessible to people who learned Go from the internet like me.

+

warning: Be careful and do not use this example code unmodified in your real application. It deliberately references an earlier state of the application in the root of this repository. This is a teaching tool and not production code.

+

What is an HTTP Proxy?

+

At it's simplest it's two things, it's an HTTP server, and an HTTP Client. When the HTTP server recieves a request, it handles it by forwarding it to the HTTP client, which then retrieves and returns it to the server, which forwards it back to the requestor. In-between recieving the request and forwarding it to the client, the server can make changes to how the requests are handled. So in our example, we'll be leaving the example HTTP server in place, but modifying the HTTP client to handle requests by routing them to I2P.

+

Step One: Setting up the HTTP Server Structure

+

Creating a custom HTTP server in Go is easy. We just need to create a type called Proxy, which implements the http.Server that forwards you to the client. First, import the goSam library.

+
    import (
+        "github.com/eyedeekay/goSam"
+    )
+
+

goSam implements a transport which is compatible with Go's http.Client. The proxy will then need to contain a goSam.Client and an http.Client.

+
    type Proxy struct {
+        Sam    *goSam.Client
+        Client *http.Client
+    }
+
+

In order to implement the http Server interface, we need the ServeHTTP function within the Proxy structure, which has the following signature:

+
    func (p *Proxy) ServeHTTP(wr http.ResponseWriter, req *http.Request)
+
+

Where req is the request made by the user, and wr is the the stream where the response will be written and returned to the user.

+

When you create an instance of this struct, you should make sure to first set up an instance of goSam.Client and pass it to the struct, like this:

+
    sam, err := goSam.NewClientFromOptions(
+        goSam.SetUnpublished(true),
+    )
+    if err != nil {
+        log.Fatal(err)
+    }
+    handler := &i2phttpproxy.Proxy{
+        Sam: sam,
+    }
+
+

Note that goSam has been set up to use an Unpublished leaseset in this case, because it will be used as a client and not a service and doesn't need to be discovered until it starts communicating with a service.

+

Step Two: Anonymize the application

+

If you're developing a privacy-aware application, it's important to anonymize more than just the connection, but also the application as well. In the case of an HTTP proxy, you should probably at least scrub some headers out. To do this, make a slice with the headers you want to delete at the first hop, and a function which deletes them from the underlying structure.

+
    var hopHeaders = []string{
+        "Accept-Language",
+        "Connection",
+        "Keep-Alive",
+        "Proxy-Authenticate",
+        "Proxy-Authorization",
+        "Proxy-Connection",
+        "Trailers",
+        "Upgrade",
+        "X-Forwarded-For",
+        "X-Real-IP",
+    }
+
+    func delHopHeaders(header http.Header) {
+        for _, h := range hopHeaders {
+            header.Del(h)
+        }
+        ....
+
+

If you want, you can also re-write the user agent string in this function as well.

+
        ....
+        if header.Get("User-Agent") != "MYOB/6.66 (AN/ON)" {
+            header.Set("User-Agent", "MYOB/6.66 (AN/ON)")
+        }
+    }
+
+

Because our proxy will only be used for I2P addresses, we'll want ServeHTTP to ignore non-I2P addresses. We can do this by examining and dropping the request when the server recieves it, like this:

+
    func (p *Proxy) ServeHTTP(wr http.ResponseWriter, req *http.Request) {
+        if req.URL.Scheme != "http" && !strings.HasSuffix(req.URL.Host, ".i2p") {
+            msg := "unsupported protocal scheme " + req.URL.Scheme
+            http.Error(wr, msg, http.StatusBadRequest)
+            log.Println(msg)
+            return
+        }
+    ...
+
+

Once you're done, add the header-scrubbing function to the ServeHTTP function.

+
    ...
+        delHopHeaders(req.Header)
+    ...
+
+

Step Three: Actually handle the request

+

Now that we've got the application anonymized, it's time to handle the request.

+
    ...
+        p.get(wr, req)
+    }
+
+

To keep the steps of the procedure cleanly separated, I chose to contain this in it's own function. It just carries the same signature of the ServeHTTP function, which

+
    func (p *Proxy) get(wr http.ResponseWriter, req *http.Request) {
+        req.RequestURI = ""
+        resp, err := p.Client.Do(req)
+        if err != nil {
+            log.Println("ServeHTTP:", err)
+            return
+        }
+        defer resp.Body.Close()
+
+        wr.WriteHeader(resp.StatusCode)
+        io.Copy(wr, resp.Body)
+    }
+
+

But wait! Where's all the I2P stuff? What will this do if it's not connected to I2P? It will just be an HTTP Proxy that doesn't work, because it's already blacklisting non-I2P addresses.

+

Step Four: Set up the I2P Client

+

Setting up the I2P Client is really easy, but a good example would be a little long so I decided to extract it out to it's own function and walk through the steps. In the end, we need to come up with a new http.Client which uses i2p as a Transport. In order to ensure that it uses the same settings as the goSam client, the function is part of the Proxy struct.

+
    func (p *Proxy) NewClient() *http.Client {
+        return &http.Client{
+
+

In order to use the resulting http.Client with i2p, you'll need to alter it's DialContext function to use the one from goSam instead.

+
            Transport: &http.Transport{
+                DialContext:           p.Sam.DialContext,
+            ....
+
+

It may also be helpful to limit connections, set some timeouts(Go by default expects you to set timeouts). Some example settings you might change would be:

+
            ....
+                MaxConnsPerHost:       1,
+                MaxIdleConns:          0,
+                MaxIdleConnsPerHost:   1,
+                DisableKeepAlives:     false,
+                ResponseHeaderTimeout: time.Second * 600,
+                IdleConnTimeout:       time.Second * 300,
+            },
+            CheckRedirect: nil,
+            Timeout:       time.Second * 600,
+        }
+    }
+
+

Step Five: Create the main() function

+

Now it's finally time to put it all together and make our code runnable. First, lets make it accept a flag which allows the user to determine which port it runs on. I don't know what ports other people use:

+
    func main() {
+        var addr = flag.String("addr", "127.0.0.1:7950", "The addr of the application.")
+        flag.Parse()
+    ...
+
+

Next, set up the goSam client and hand it off to a new instance of *Proxy:

+
    ...
+        sam, err := goSam.NewClientFromOptions(
+            goSam.SetHost("127.0.0.1"),
+            goSam.SetPort("7656"),
+            goSam.SetUnpublished(true),
+            goSam.SetInLength(uint(2)),
+            goSam.SetOutLength(uint(2)),
+            goSam.SetInQuantity(uint(1)),
+            goSam.SetOutQuantity(uint(1)),
+            goSam.SetInBackups(uint(1)),
+            goSam.SetOutBackups(uint(1)),
+            goSam.SetReduceIdle(true),
+            goSam.SetReduceIdleTime(uint(2000000)),
+        )
+        if err != nil {
+            log.Fatal(err)
+        }
+        handler := &Proxy{
+            Sam: sam,
+        }
+    ...
+
+

Use that SAM connection to set up an HTTP client:

+
    ...
+        handler.Client = handler.NewClient()
+    ...
+
+

And serve it up. It's just that easy.

+
    ...
+        log.Println("Starting proxy server on", *addr)
+        if err := http.ListenAndServe(*addr, handler); err != nil {
+            log.Fatal("ListenAndServe:", err)
+        }
+    }
+
+

:)

+

Credits/Notes:

+
    +
  1. This guide is based on a guide for the clearnet and that is only 10% because it was easier for me. 90% of the reason is to illustrate that using I2P in your application is, in all likelihood, not that different than what you're already doing.* In all, this example is only 111 lines of code long minus comments.
  2. +
+
    +
  • if the rest of your application only sends and recieves that which is non-linkable and absolutely necessary to its operation.
  • +