├── .gitignore ├── LICENSE ├── cmd └── tlsproxy │ └── main.go ├── go.mod ├── go.sum ├── tlsproxy.go └── tlsproxy_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.pem 2 | vendor 3 | tlsproxy 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2016 Brave New Software Project, Inc. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /cmd/tlsproxy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "flag" 6 | "net" 7 | "net/http" 8 | _ "net/http/pprof" 9 | "os" 10 | "time" 11 | 12 | "github.com/getlantern/golog" 13 | "github.com/getlantern/keyman" 14 | "github.com/oxtoacart/bpool" 15 | 16 | "github.com/getlantern/tlsproxy" 17 | ) 18 | 19 | var ( 20 | log = golog.LoggerFor("tlsproxy") 21 | 22 | mode = flag.String("mode", "server", "Mode. server = listen for TLS connections, client = listen for plain text connections") 23 | hostname = flag.String("hostname", "", "Hostname to use for TLS. If not supplied, will auto-detect hostname") 24 | listenAddr = flag.String("listen-addr", ":6380", "Address at which to listen for incoming connections") 25 | forwardAddr = flag.String("forward-addr", "localhost:6379", "Address to which to forward connections") 26 | keepAlivePeriod = flag.Duration("keepaliveperiod", 2*time.Hour, "Period for sending tcp keepalives") 27 | pkfile = flag.String("pkfile", "pk.pem", "File containing private key for this proxy") 28 | certfile = flag.String("certfile", "cert.pem", "File containing the certificate for this proxy") 29 | cafile = flag.String("cafile", "cert.pem", "File containing the certificate authority (or just certificate) with which to verify the remote end's identity") 30 | pprofAddr = flag.String("pprofaddr", "localhost:4000", "pprof address to listen on, not activate pprof if empty") 31 | help = flag.Bool("help", false, "Get usage help") 32 | 33 | buffers = bpool.NewBytePool(25000, 32768) 34 | ) 35 | 36 | func main() { 37 | flag.Parse() 38 | if *help { 39 | flag.Usage() 40 | os.Exit(0) 41 | } 42 | 43 | if *pprofAddr != "" { 44 | go func() { 45 | log.Debugf("Starting pprof page at http://%s/debug/pprof", *pprofAddr) 46 | if err := http.ListenAndServe(*pprofAddr, nil); err != nil { 47 | log.Error(err) 48 | } 49 | }() 50 | } 51 | 52 | hostname := *hostname 53 | if hostname == "" { 54 | _hostname, err := os.Hostname() 55 | if err == nil { 56 | hostname = _hostname 57 | } 58 | } 59 | if hostname == "" { 60 | hostname = "localhost" 61 | } 62 | 63 | log.Debugf("Mode: %v", *mode) 64 | log.Debugf("Hostname: %v", hostname) 65 | log.Debugf("Forwarding to: %v", *forwardAddr) 66 | log.Debugf("TCP KeepAlive Period: %v", *keepAlivePeriod) 67 | 68 | cert, err := keyman.KeyPairFor(hostname, "getlantern.org", *pkfile, *certfile) 69 | if err != nil { 70 | log.Fatalf("Unable to load keypair: %v", err) 71 | } 72 | ca, err := keyman.LoadCertificateFromFile(*cafile) 73 | if err != nil { 74 | log.Fatalf("Unable to load ca certificate: %v", err) 75 | } 76 | pool := ca.PoolContainingCert() 77 | 78 | tlsConfig := &tls.Config{ 79 | Certificates: []tls.Certificate{cert}, 80 | ClientAuth: tls.RequireAndVerifyClientCert, 81 | RootCAs: pool, 82 | ClientCAs: pool, 83 | } 84 | 85 | l, err := net.Listen("tcp", *listenAddr) 86 | if err != nil { 87 | log.Fatalf("Unable to listen at %v: %v", *listenAddr, err) 88 | } 89 | 90 | switch *mode { 91 | case "server": 92 | tlsproxy.RunServer(l, *forwardAddr, *keepAlivePeriod, tlsConfig) 93 | case "client": 94 | tlsproxy.RunClient(l, *forwardAddr, *keepAlivePeriod, tlsConfig) 95 | default: 96 | log.Fatalf("Unknown mode: %v", *mode) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/getlantern/tlsproxy 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/getlantern/byteexec v0.0.0-20170405023437-4cfb26ec74f4 // indirect 7 | github.com/getlantern/elevate v0.0.0-20180207094634-c2e2e4901072 // indirect 8 | github.com/getlantern/fdcount v0.0.0-20190912142506-f89afd7367c4 // indirect 9 | github.com/getlantern/filepersist v0.0.0-20160317154340-c5f0cd24e799 // indirect 10 | github.com/getlantern/golog v0.0.0-20200929154820-62107891371a 11 | github.com/getlantern/keyman v0.0.0-20180207174507-f55e7280e93a 12 | github.com/getlantern/mockconn v0.0.0-20191023022503-481dbcceeb58 // indirect 13 | github.com/getlantern/mtime v0.0.0-20200228202836-084e1d8282b0 // indirect 14 | github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd 15 | github.com/libp2p/go-buffer-pool v0.0.2 16 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c 17 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 18 | github.com/stretchr/testify v1.5.1 19 | ) 20 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/getlantern/byteexec v0.0.0-20170405023437-4cfb26ec74f4 h1:Nqmy8i81dzokjNHpyOg24gnQBeGRF7D51m8HmBRNn0Y= 6 | github.com/getlantern/byteexec v0.0.0-20170405023437-4cfb26ec74f4/go.mod h1:4WCQkaCIwta0KlF9bQZA1jYqp8bzIS2PeCqjnef8nZ8= 7 | github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4= 8 | github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= 9 | github.com/getlantern/elevate v0.0.0-20180207094634-c2e2e4901072 h1:Sxd/u3rnHYAqXzpRXjOA7DPyRKYzcivMRBKtOhsRwW8= 10 | github.com/getlantern/elevate v0.0.0-20180207094634-c2e2e4901072/go.mod h1:T4VB2POK13lsPLFV98WJQrL7gAXYD9TyJxBU2P8c8p4= 11 | github.com/getlantern/errors v1.0.1 h1:XukU2whlh7OdpxnkXhNH9VTLVz0EVPGKDV5K0oWhvzw= 12 | github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A= 13 | github.com/getlantern/fdcount v0.0.0-20190912142506-f89afd7367c4 h1:JdD4XSaT6/j6InM7MT1E4WRvzR8gurxfq53A3ML3B/Q= 14 | github.com/getlantern/fdcount v0.0.0-20190912142506-f89afd7367c4/go.mod h1:XZwE+iIlAgr64OFbXKFNCllBwV4wEipPx8Hlo2gZdbM= 15 | github.com/getlantern/filepersist v0.0.0-20160317154340-c5f0cd24e799 h1:FhkPUYCQYmoxS02r2GRrIV7dahUIncRl36xzs3/mnjA= 16 | github.com/getlantern/filepersist v0.0.0-20160317154340-c5f0cd24e799/go.mod h1:8DGAx0LNUfXNnEH+fXI0s3OCBA/351kZCiz/8YSK3i8= 17 | github.com/getlantern/golog v0.0.0-20200929154820-62107891371a h1:97NO5ovLBt5jj7TUzfPSwNDL6gyYhXEbaFhgzLB6h1o= 18 | github.com/getlantern/golog v0.0.0-20200929154820-62107891371a/go.mod h1:ZyIjgH/1wTCl+B+7yH1DqrWp6MPJqESmwmEQ89ZfhvA= 19 | github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0= 20 | github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o= 21 | github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc= 22 | github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA= 23 | github.com/getlantern/keyman v0.0.0-20180207174507-f55e7280e93a h1:SBw2c296eaLrrnydtLhcjgvNB1mDaXF1SSfW4DGt0kk= 24 | github.com/getlantern/keyman v0.0.0-20180207174507-f55e7280e93a/go.mod h1:FMf0g72BHs14jVcD8i8ubEk4sMB6JdidBn67d44i3ws= 25 | github.com/getlantern/mockconn v0.0.0-20191023022503-481dbcceeb58 h1:95ogd2dX0JeYDUI4Ssl9ARhVAh+kvOTpN6TOD/tX0Vs= 26 | github.com/getlantern/mockconn v0.0.0-20191023022503-481dbcceeb58/go.mod h1:+F5GJ7qGpQ03DBtcOEyQpM30ix4BLswdaojecFtsdy8= 27 | github.com/getlantern/mtime v0.0.0-20200228202836-084e1d8282b0 h1:90WndSSKhubMxjsGVQThsxOQGcgccpybVcipB+KVkZY= 28 | github.com/getlantern/mtime v0.0.0-20200228202836-084e1d8282b0/go.mod h1:GfzwugvtH7YcmNIrHHizeyImsgEdyL88YkdnK28B14c= 29 | github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd h1:mn98vs69Kqw56iKhR82mjk16Q1q5aDFFW0E89/QbXkQ= 30 | github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd/go.mod h1:wKdY0ikOgzrWSeB9UyBVKPRhjXQ+vTb+BPeJuypUuNE= 31 | github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA= 32 | github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= 33 | github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= 34 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 35 | github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= 36 | github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= 37 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= 38 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= 39 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 40 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 41 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM= 42 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= 43 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 44 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 45 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 46 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 47 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 48 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 49 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 50 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 51 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 52 | -------------------------------------------------------------------------------- /tlsproxy.go: -------------------------------------------------------------------------------- 1 | // tlsproxy provides a TLS proxy kind of like stunnel 2 | package tlsproxy 3 | 4 | import ( 5 | "crypto/tls" 6 | "net" 7 | _ "net/http/pprof" 8 | "time" 9 | 10 | "github.com/getlantern/netx" 11 | pool "github.com/libp2p/go-buffer-pool" 12 | "github.com/siddontang/go/log" 13 | ) 14 | 15 | // The practical TCP MSS for anything traversing Ethernet and using TCP timestamps 16 | const maxFrameSize = 1448 17 | 18 | func RunServer(l net.Listener, forwardAddr string, keepAlivePeriod time.Duration, tlsConfig *tls.Config) { 19 | doRun(tls.NewListener(wrapKeepAliveListener(keepAlivePeriod, l), tlsConfig), func() (net.Conn, error) { 20 | return dial(keepAlivePeriod, forwardAddr) 21 | }) 22 | } 23 | 24 | func RunClient(l net.Listener, forwardAddr string, keepAlivePeriod time.Duration, tlsConfig *tls.Config) { 25 | host, _, err := net.SplitHostPort(forwardAddr) 26 | if err != nil { 27 | log.Fatalf("Unable to determine hostname for server: %v", err) 28 | } 29 | tlsConfig.ServerName = host 30 | tlsConfig.ClientSessionCache = tls.NewLRUClientSessionCache(5000) 31 | 32 | doRun(wrapKeepAliveListener(keepAlivePeriod, l), func() (net.Conn, error) { 33 | _conn, err := dial(keepAlivePeriod, forwardAddr) 34 | if err != nil { 35 | return _conn, err 36 | } 37 | conn := tls.Client(_conn, tlsConfig) 38 | err = conn.Handshake() 39 | if err != nil { 40 | _conn.Close() 41 | return nil, err 42 | } 43 | if !conn.ConnectionState().DidResume { 44 | log.Debug("Connection did not resume") 45 | } 46 | return conn, nil 47 | }) 48 | } 49 | 50 | func doRun(l net.Listener, dial func() (net.Conn, error)) { 51 | defer l.Close() 52 | log.Debugf("Listening for incoming connections at: %v", l.Addr()) 53 | 54 | for { 55 | in, err := l.Accept() 56 | if err != nil { 57 | log.Errorf("Unable to accept: %v", err) 58 | return 59 | } 60 | 61 | go func() { 62 | defer in.Close() 63 | out, err := dial() 64 | if err != nil { 65 | log.Debugf("Unable to dial forwarding address: %v", err) 66 | return 67 | } 68 | defer out.Close() 69 | 70 | log.Debugf("Copying from %v to %v", in.RemoteAddr(), out.RemoteAddr()) 71 | bufOut := pool.Get(maxFrameSize) 72 | bufIn := pool.Get(maxFrameSize) 73 | defer pool.Put(bufOut) 74 | defer pool.Put(bufIn) 75 | netx.BidiCopy(out, in, bufOut, bufIn) 76 | }() 77 | } 78 | } 79 | 80 | func dial(keepAlivePeriod time.Duration, forwardAddr string) (net.Conn, error) { 81 | conn, err := net.DialTimeout("tcp", forwardAddr, 30*time.Second) 82 | if err == nil && keepAlivePeriod > 0 { 83 | addKeepalive(keepAlivePeriod, conn) 84 | } 85 | return conn, err 86 | } 87 | 88 | func addKeepalive(keepAlivePeriod time.Duration, conn net.Conn) { 89 | c, ok := conn.(*net.TCPConn) 90 | if !ok { 91 | log.Error("Conn was not a TCPConn, can't set KeepAlivePeriod!") 92 | return 93 | } 94 | err := c.SetKeepAlive(true) 95 | if err != nil { 96 | log.Errorf("Unable to turn on TCP keep alives: %v", err) 97 | return 98 | } 99 | err = c.SetKeepAlivePeriod(keepAlivePeriod) 100 | if err != nil { 101 | log.Errorf("Unable to set KeepAlivePeriod: %v", err) 102 | } 103 | } 104 | 105 | func wrapKeepAliveListener(keepAlivePeriod time.Duration, l net.Listener) net.Listener { 106 | if keepAlivePeriod <= 0 { 107 | return l 108 | } 109 | 110 | return &keepAliveListener{l: l, keepAlivePeriod: keepAlivePeriod} 111 | } 112 | 113 | type keepAliveListener struct { 114 | l net.Listener 115 | keepAlivePeriod time.Duration 116 | } 117 | 118 | func (l *keepAliveListener) Accept() (net.Conn, error) { 119 | conn, err := l.l.Accept() 120 | if err == nil { 121 | addKeepalive(l.keepAlivePeriod, conn) 122 | } 123 | return conn, err 124 | } 125 | 126 | func (l *keepAliveListener) Close() error { 127 | return l.l.Close() 128 | } 129 | 130 | func (l *keepAliveListener) Addr() net.Addr { 131 | return l.l.Addr() 132 | } 133 | -------------------------------------------------------------------------------- /tlsproxy_test.go: -------------------------------------------------------------------------------- 1 | package tlsproxy 2 | 3 | import ( 4 | "crypto/tls" 5 | "io" 6 | "net" 7 | "testing" 8 | "time" 9 | 10 | "github.com/getlantern/keyman" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | const ( 15 | iters = 100 16 | ) 17 | 18 | var ( 19 | data = []byte("Hello there strange and wonderful benchmarking world!") 20 | ) 21 | 22 | func TestProxy(t *testing.T) { 23 | pk, err := keyman.GeneratePK(2048) 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | cert, err := pk.TLSCertificateFor(time.Now().Add(24*time.Hour), false, nil, "lantern", "lantern") 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | keyPair, err := tls.X509KeyPair(cert.PEMEncoded(), pk.PEMEncoded()) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | serverConfig := &tls.Config{ 36 | Certificates: []tls.Certificate{keyPair}, 37 | } 38 | clientConfig := &tls.Config{ 39 | InsecureSkipVerify: true, 40 | } 41 | 42 | l, err := net.Listen("tcp", "localhost:0") 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | defer l.Close() 47 | go func() { 48 | for { 49 | conn, listenErr := l.Accept() 50 | if listenErr != nil { 51 | return 52 | } 53 | // echo 54 | go func() { 55 | defer conn.Close() 56 | io.Copy(conn, conn) 57 | }() 58 | } 59 | }() 60 | 61 | sl, err := net.Listen("tcp", "localhost:0") 62 | if err != nil { 63 | t.Fatal(err) 64 | } 65 | defer sl.Close() 66 | 67 | cl, err := net.Listen("tcp", "localhost:0") 68 | if err != nil { 69 | t.Fatal(err) 70 | } 71 | defer cl.Close() 72 | 73 | go RunServer(sl, l.Addr().String(), 150*time.Millisecond, serverConfig) 74 | go RunClient(cl, sl.Addr().String(), 150*time.Millisecond, clientConfig) 75 | 76 | clientAddr := cl.Addr().String() 77 | 78 | conn, err := net.Dial("tcp", clientAddr) 79 | if err != nil { 80 | t.Fatalf("Unable to dial client proxy: %v", err) 81 | } 82 | defer conn.Close() 83 | 84 | // Write 85 | go func() { 86 | for j := 0; j < iters; j++ { 87 | _, err := conn.Write(data) 88 | if err != nil { 89 | t.Fatalf("%d Unable to write: %v", j, err) 90 | } 91 | } 92 | }() 93 | 94 | // Read (should stop automatically due to TCP keepalive) 95 | buf := make([]byte, len(data)) 96 | for i := 0; i < iters; i++ { 97 | _, err := io.ReadFull(conn, buf) 98 | if !assert.NoError(t, err) { 99 | return 100 | } 101 | } 102 | } 103 | --------------------------------------------------------------------------------