{"id":13481693,"url":"https://github.com/AdguardTeam/gomitmproxy","last_synced_at":"2025-03-27T12:31:19.282Z","repository":{"id":49960071,"uuid":"220100451","full_name":"AdguardTeam/gomitmproxy","owner":"AdguardTeam","description":"Simple golang mitm proxy implementation","archived":false,"fork":false,"pushed_at":"2024-03-15T12:19:26.000Z","size":79,"stargazers_count":277,"open_issues_count":11,"forks_count":55,"subscribers_count":17,"default_branch":"master","last_synced_at":"2024-08-04T09:06:35.303Z","etag":null,"topics":["golang","mitmproxy","open-source","proxy-server"],"latest_commit_sha":null,"homepage":"https://adguard.com/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AdguardTeam.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2019-11-06T22:13:55.000Z","updated_at":"2024-07-27T17:23:28.000Z","dependencies_parsed_at":"2024-01-07T22:50:24.049Z","dependency_job_id":"be3d8804-5427-42c9-9ec9-0464fb100286","html_url":"https://github.com/AdguardTeam/gomitmproxy","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdguardTeam%2Fgomitmproxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdguardTeam%2Fgomitmproxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdguardTeam%2Fgomitmproxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdguardTeam%2Fgomitmproxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AdguardTeam","download_url":"https://codeload.github.com/AdguardTeam/gomitmproxy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222251837,"owners_count":16955908,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","mitmproxy","open-source","proxy-server"],"created_at":"2024-07-31T17:00:54.351Z","updated_at":"2024-10-30T15:31:19.339Z","avatar_url":"https://github.com/AdguardTeam.png","language":"Go","readme":"[![Code Coverage](https://img.shields.io/codecov/c/github/AdguardTeam/gomitmproxy/master.svg)](https://codecov.io/github/AdguardTeam/gomitmproxy?branch=master)\n[![Go Report Card](https://goreportcard.com/badge/github.com/AdguardTeam/gomitmproxy)](https://goreportcard.com/report/AdguardTeam/gomitmproxy)\n[![GolangCI](https://golangci.com/badges/github.com/AdguardTeam/gomitmproxy.svg)](https://golangci.com/r/github.com/AdguardTeam/gomitmproxy)\n[![Go Doc](https://godoc.org/github.com/AdguardTeam/gomitmproxy?status.svg)](https://godoc.org/github.com/AdguardTeam/gomitmproxy)\n\n# gomitmproxy\n\nThis is a customizable HTTP proxy with TLS interception support.\nIt was created as a part of [AdGuard Home](https://github.com/AdguardTeam/AdGuardHome).\nHowever, it can be used for different purposes so we decided to make it a separate project.\n\n## Features\n \n* HTTP proxy\n* HTTP over TLS (HTTPS) proxy\n* Proxy authorization\n* TLS termination\n\n## How to use gomitmproxy\n\n### Simple HTTP proxy\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"github.com/AdguardTeam/gomitmproxy\"\n)\n\nfunc main() {\n\tproxy := gomitmproxy.NewProxy(gomitmproxy.Config{\n\t\tListenAddr: \u0026net.TCPAddr{\n\t\t\tIP:   net.IPv4(0, 0, 0, 0),\n\t\t\tPort: 8080,\n\t\t},\n\t})\n\terr := proxy.Start()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tsignalChannel := make(chan os.Signal, 1)\n\tsignal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM)\n\t\u003c-signalChannel\n\n\t// Clean up\n\tproxy.Close()\n}\n```\n\n### Modifying requests and responses\n\nYou can modify requests and responses using `OnRequest` and `OnResponse` handlers.\n\nThe example below will block requests to `example.net` and add a short comment to\nthe end of every HTML response.\n\n```go\nproxy := gomitmproxy.NewProxy(gomitmproxy.Config{\n    ListenAddr: \u0026net.TCPAddr{\n        IP:   net.IPv4(0, 0, 0, 0),\n        Port: 8080,\n    },\n    OnRequest: func(session *gomitmproxy.Session) (request *http.Request, response *http.Response) {\n        req := session.Request()\n\n        log.Printf(\"onRequest: %s %s\", req.Method, req.URL.String())\n\n        if req.URL.Host == \"example.net\" {\n            body := strings.NewReader(\"\u003chtml\u003e\u003cbody\u003e\u003ch1\u003eReplaced response\u003c/h1\u003e\u003c/body\u003e\u003c/html\u003e\")\n            res := proxyutil.NewResponse(http.StatusOK, body, req)\n            res.Header.Set(\"Content-Type\", \"text/html\")\n\n            // Use session props to pass the information about request being blocked\n            session.SetProp(\"blocked\", true)\n            return nil, res\n        }\n\n        return nil, nil\n    },\n    OnResponse: func(session *gomitmproxy.Session) *http.Response {\n        log.Printf(\"onResponse: %s\", session.Request().URL.String())\n\n        if _, ok := session.GetProp(\"blocked\"); ok {\n            log.Printf(\"onResponse: was blocked\")\n        }\n\n        res := session.Response()\n        req := session.Request()\n    \n        if strings.Index(res.Header.Get(\"Content-Type\"), \"text/html\") != 0 {\n            // Do nothing with non-HTML responses\n            return nil\n        }\n    \n        b, err := proxyutil.ReadDecompressedBody(res)\n        // Close the original body\n        _ = res.Body.Close()\n        if err != nil {\n            return proxyutil.NewErrorResponse(req, err)\n        }\n    \n        // Use latin1 before modifying the body\n        // Using this 1-byte encoding will let us preserve all original characters\n        // regardless of what exactly is the encoding\n        body, err := proxyutil.DecodeLatin1(bytes.NewReader(b))\n        if err != nil {\n            return proxyutil.NewErrorResponse(session.Request(), err)\n        }\n    \n        // Modifying the original body\n        modifiedBody, err := proxyutil.EncodeLatin1(body + \"\u003c!-- EDITED --\u003e\")\n        if err != nil {\n            return proxyutil.NewErrorResponse(session.Request(), err)\n        }\n    \n        res.Body = ioutil.NopCloser(bytes.NewReader(modifiedBody))\n        res.Header.Del(\"Content-Encoding\")\n        res.ContentLength = int64(len(modifiedBody))\n        return res\n    },\n})\n```\n\n### Proxy authorization\n\nIf you want to protect your proxy with Basic authentication, set `Username` and `Password`\nfields in the proxy configuration.\n\n```go\nproxy := gomitmproxy.NewProxy(gomitmproxy.Config{\n    ListenAddr: \u0026net.TCPAddr{\n        IP:   net.IPv4(0, 0, 0, 0),\n        Port: 8080,\n    },\n    Username: \"user\",\n    Password: \"pass\",\n})\n```\n\n### HTTP over TLS (HTTPS) proxy\n\nIf you want to protect yourself from eavesdropping on your traffic to proxy, you can configure\nit to work over a TLS tunnel. This is really simple to do, just set a `*tls.Config` instance\nin your proxy configuration.\n\n```go\ntlsConfig := \u0026tls.Config{\n    Certificates: []tls.Certificate{*proxyCert},\n}\nproxy := gomitmproxy.NewProxy(gomitmproxy.Config{\n    ListenAddr: addr,\n    TLSConfig:  tlsConfig,\n})\n```\n\n### TLS interception\n\nIf you want to do TLS termination, you first need to prepare a self-signed certificate\nthat will be used as a certificates authority. Use the following `openssl` commands to do this.\n\n```bash\nopenssl genrsa -out demo.key 2048\nopenssl req -new -x509 -key demo.key -out demo.crt -days 3650 -addext subjectAltName=DNS:\u003chostname\u003e,IP:\u003cip\u003e\n```\n\nNow you can use it to initialize `MITMConfig`:\n```go\ntlsCert, err := tls.LoadX509KeyPair(\"demo.crt\", \"demo.key\")\nif err != nil {\n    log.Fatal(err)\n}\nprivateKey := tlsCert.PrivateKey.(*rsa.PrivateKey)\n\nx509c, err := x509.ParseCertificate(tlsCert.Certificate[0])\nif err != nil {\n    log.Fatal(err)\n}\n\nmitmConfig, err := mitm.NewConfig(x509c, privateKey, nil)\nif err != nil {\n    log.Fatal(err)\n}\n\nmitmConfig.SetValidity(time.Hour * 24 * 7) // generate certs valid for 7 days\nmitmConfig.SetOrganization(\"gomitmproxy\")  // cert organization\n```\n\nPlease note that you can set `MITMExceptions` to a list of hostnames,\nwhich will be excluded from TLS interception.\n\n```go\nproxy := gomitmproxy.NewProxy(gomitmproxy.Config{\n    ListenAddr: \u0026net.TCPAddr{\n        IP:   net.IPv4(0, 0, 0, 0),\n        Port: 3333,\n    },\n    MITMConfig:     mitmConfig,\n    MITMExceptions: []string{\"example.com\"},\n})\n```\n\nIf you configure the `APIHost`, you'll be able to download the CA certificate\nfrom `http://[APIHost]/cert.crt` when the proxy is configured.\n\n```go\n// Navigate to http://gomitmproxy/cert.crt to download the CA certificate\nproxy.APIHost = \"gomitmproxy\"\n```\n\n### Custom certs storage\n\nBy default, `gomitmproxy` uses an in-memory map-based storage for the certificates,\ngenerated while doing TLS interception. It is often necessary to use a different kind\nof certificates storage. If this is your case, you can supply your own implementation\nof the `CertsStorage` interface.\n\n```go\n// CustomCertsStorage - an example of a custom cert storage\ntype CustomCertsStorage struct {\n\tcertsCache map[string]*tls.Certificate // cache with the generated certificates\n}\n\n// Get gets the certificate from the storage\nfunc (c *CustomCertsStorage) Get(key string) (*tls.Certificate, bool) {\n\tv, ok := c.certsCache[key]\n\treturn v, ok\n}\n\n// Set saves the certificate to the storage\nfunc (c *CustomCertsStorage) Set(key string, cert *tls.Certificate) {\n\tc.certsCache[key] = cert\n}\n```\n\nThen pass it to the `NewConfig` function.\n\n```go\nmitmConfig, err := mitm.NewConfig(x509c, privateKey, \u0026CustomCertsStorage{\n    certsCache: map[string]*tls.Certificate{}},\n)\n```\n\n## Notable alternatives\n\n* [martian](https://github.com/google/martian) - an awesome debugging proxy with TLS interception support.\n* [goproxy](https://github.com/elazarl/goproxy) - also supports TLS interception and requests. \n\n## TODO\n\n* [X] Basic HTTP proxy without MITM\n* [ ] Proxy\n    * [X] Expose APIs for the library users\n    * [X] How-to doc\n    * [X] Travis configuration\n    * [X] Proxy-Authorization\n    * [X] WebSockets support (see [this](https://github.com/google/martian/issues/31))\n    * [X] `certsCache` -- allow custom implementations\n    * [X] Support HTTP CONNECT over TLS\n    * [X] Test plain HTTP requests inside HTTP CONNECT\n    * [X] Test memory leaks\n    * [X] Editing response body in a callback\n    * [X] Handle unknown content-encoding values\n    * [X] Handle CONNECT to APIHost properly (without trying to actually connect anywhere)\n    * [X] Allow hijacking connections (!)\n    * [X] Multiple listeners\n    * [ ] Unit tests\n    * [ ] Check \u0026 fix TODOs\n    * [ ] Allow specifying net.Dialer\n    * [ ] Specify timeouts for http.Transport\n* [ ] MITM\n    * [X] Basic MITM\n    * [X] MITM exceptions\n    * [X] Handle invalid server certificates properly (not just reset connections)\n    * [X] Pass the most important tests on badssl.com/dashboard\n    * [X] Handle certificate authentication\n    * [ ] Allow configuring minimum supported TLS version\n    * [ ] OCSP check (see [example](https://stackoverflow.com/questions/46626963/golang-sending-ocsp-request-returns))\n    * [ ] (?) HPKP (see [example](https://github.com/tam7t/hpkp))\n    * [ ] (?) CT logs (see [example](https://github.com/google/certificate-transparency-go))\n    * [ ] (?) CRLSets (see [example](https://github.com/agl/crlset-tools))\n","funding_links":[],"categories":["Go"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAdguardTeam%2Fgomitmproxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAdguardTeam%2Fgomitmproxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAdguardTeam%2Fgomitmproxy/lists"}